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
@@ -115,10 +115,10 @@ class WrGauge {
115
115
  anchor: el,
116
116
  };
117
117
  });
118
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrGauge, deps: [], target: i0.ɵɵFactoryTarget.Component });
119
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: WrGauge, isStandalone: true, selector: "wr-gauge", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, strokeWidth: { classPropertyName: "strokeWidth", publicName: "strokeWidth", isSignal: true, isRequired: false, transformFunction: null }, trackColor: { classPropertyName: "trackColor", publicName: "trackColor", isSignal: true, isRequired: false, transformFunction: null }, valueColor: { classPropertyName: "valueColor", publicName: "valueColor", isSignal: true, isRequired: false, transformFunction: null }, showValue: { classPropertyName: "showValue", publicName: "showValue", isSignal: true, isRequired: false, transformFunction: null }, suffix: { classPropertyName: "suffix", publicName: "suffix", isSignal: true, isRequired: false, transformFunction: null }, tooltip: { classPropertyName: "tooltip", publicName: "tooltip", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "wr-gauge" }, viewQueries: [{ propertyName: "surface", first: true, predicate: ["surface"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n The dial is drawn in an aria-hidden <svg> and the printed value is optional, so\n with `showValue` off the component had no accessible representation at all.\n `role=\"meter\"` plus the value attributes make it readable either way.\n-->\n<div\n #surface\n class=\"wr-gauge__surface\"\n [style.width.px]=\"size()\"\n role=\"meter\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-valuenow]=\"displayValue()\"\n [attr.aria-valuetext]=\"valueText()\"\n (mouseenter)=\"tip.enter(0)\"\n (mouseleave)=\"tip.leave($event)\"\n>\n <svg [attr.viewBox]=\"viewBox\" aria-hidden=\"true\">\n <path\n class=\"wr-gauge__track\"\n [attr.d]=\"trackPath()\"\n fill=\"none\"\n [attr.stroke]=\"trackColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n stroke-linecap=\"round\"\n />\n <path\n class=\"wr-gauge__value\"\n [attr.d]=\"valuePath()\"\n fill=\"none\"\n [attr.stroke]=\"valueColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n stroke-linecap=\"round\"\n />\n </svg>\n @if (showValue()) {\n <div class=\"wr-gauge__text\">\n {{ displayValue() }}<span class=\"wr-gauge__suffix\">{{ suffix() }}</span>\n </div>\n }\n</div>\n", encapsulation: i0.ViewEncapsulation.None });
118
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrGauge, deps: [], target: i0.ɵɵFactoryTarget.Component });
119
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: WrGauge, isStandalone: true, selector: "wr-gauge", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, strokeWidth: { classPropertyName: "strokeWidth", publicName: "strokeWidth", isSignal: true, isRequired: false, transformFunction: null }, trackColor: { classPropertyName: "trackColor", publicName: "trackColor", isSignal: true, isRequired: false, transformFunction: null }, valueColor: { classPropertyName: "valueColor", publicName: "valueColor", isSignal: true, isRequired: false, transformFunction: null }, showValue: { classPropertyName: "showValue", publicName: "showValue", isSignal: true, isRequired: false, transformFunction: null }, suffix: { classPropertyName: "suffix", publicName: "suffix", isSignal: true, isRequired: false, transformFunction: null }, tooltip: { classPropertyName: "tooltip", publicName: "tooltip", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "wr-gauge" }, viewQueries: [{ propertyName: "surface", first: true, predicate: ["surface"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n The dial is drawn in an aria-hidden <svg> and the printed value is optional, so\n with `showValue` off the component had no accessible representation at all.\n `role=\"meter\"` plus the value attributes make it readable either way.\n-->\n<div\n #surface\n class=\"wr-gauge__surface\"\n [style.width.px]=\"size()\"\n role=\"meter\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-valuenow]=\"displayValue()\"\n [attr.aria-valuetext]=\"valueText()\"\n (mouseenter)=\"tip.enter(0)\"\n (mouseleave)=\"tip.leave($event)\"\n>\n <svg [attr.viewBox]=\"viewBox\" aria-hidden=\"true\">\n <path\n class=\"wr-gauge__track\"\n [attr.d]=\"trackPath()\"\n fill=\"none\"\n [attr.stroke]=\"trackColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n stroke-linecap=\"round\"\n />\n <path\n class=\"wr-gauge__value\"\n [attr.d]=\"valuePath()\"\n fill=\"none\"\n [attr.stroke]=\"valueColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n stroke-linecap=\"round\"\n />\n </svg>\n @if (showValue()) {\n <div class=\"wr-gauge__text\">\n {{ displayValue() }}<span class=\"wr-gauge__suffix\">{{ suffix() }}</span>\n </div>\n }\n</div>\n", encapsulation: i0.ViewEncapsulation.None });
120
120
  }
121
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrGauge, decorators: [{
121
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrGauge, decorators: [{
122
122
  type: Component,
123
123
  args: [{ selector: 'wr-gauge', encapsulation: ViewEncapsulation.None, host: { class: 'wr-gauge' }, template: "<!--\n The dial is drawn in an aria-hidden <svg> and the printed value is optional, so\n with `showValue` off the component had no accessible representation at all.\n `role=\"meter\"` plus the value attributes make it readable either way.\n-->\n<div\n #surface\n class=\"wr-gauge__surface\"\n [style.width.px]=\"size()\"\n role=\"meter\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-valuenow]=\"displayValue()\"\n [attr.aria-valuetext]=\"valueText()\"\n (mouseenter)=\"tip.enter(0)\"\n (mouseleave)=\"tip.leave($event)\"\n>\n <svg [attr.viewBox]=\"viewBox\" aria-hidden=\"true\">\n <path\n class=\"wr-gauge__track\"\n [attr.d]=\"trackPath()\"\n fill=\"none\"\n [attr.stroke]=\"trackColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n stroke-linecap=\"round\"\n />\n <path\n class=\"wr-gauge__value\"\n [attr.d]=\"valuePath()\"\n fill=\"none\"\n [attr.stroke]=\"valueColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n stroke-linecap=\"round\"\n />\n </svg>\n @if (showValue()) {\n <div class=\"wr-gauge__text\">\n {{ displayValue() }}<span class=\"wr-gauge__suffix\">{{ suffix() }}</span>\n </div>\n }\n</div>\n" }]
124
124
  }], propDecorators: { ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], strokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "strokeWidth", required: false }] }], trackColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackColor", required: false }] }], valueColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueColor", required: false }] }], showValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValue", required: false }] }], suffix: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffix", required: false }] }], tooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltip", required: false }] }], surface: [{ type: i0.ViewChild, args: ['surface', { isSignal: true }] }] } });
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-gauge.mjs","sources":["../../../projects/lib/gauge/gauge.ts","../../../projects/lib/gauge/gauge.html","../../../projects/lib/gauge/ngwr-gauge.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, viewChild } from '@angular/core';\n\nimport { useI18nText } from 'ngwr/i18n';\nimport { useChartTooltip } from 'ngwr/popover';\nimport { clamp } from 'ngwr/utils';\n\n/**\n * Semicircle progress gauge. Display-only — for interactive use see the\n * {@link WrKnob} from `ngwr/knob`.\n *\n * @example\n * ```html\n * <wr-gauge [value]=\"72\" />\n * <wr-gauge [value]=\"9.5\" [min]=\"0\" [max]=\"10\" suffix=\"/10\" valueColor=\"var(--wr-color-warning)\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/gauge\n */\n@Component({\n selector: 'wr-gauge',\n templateUrl: './gauge.html',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-gauge' },\n})\nexport class WrGauge {\n /** Accessible name — the role needs one. Falls back to `gauge.label`. */\n readonly ariaLabel = input<string | null>(null);\n\n protected readonly resolvedAriaLabel = useI18nText(this.ariaLabel, 'gauge.label', 'Gauge');\n\n /**\n * The reading. Coerced like every other numeric input here — it was the only one\n * without it, so a NaN reached the arc's `d` as the literal text `NaN`, which is\n * invalid path geometry and drops the arc.\n */\n readonly value = input.required<number, unknown>({\n transform: (v: unknown): number => coerceNumberProperty(v, 0),\n });\n readonly min = input(0, { transform: (v: unknown): number => coerceNumberProperty(v, 0) });\n readonly max = input(100, { transform: (v: unknown): number => coerceNumberProperty(v, 100) });\n\n /** Diameter in CSS pixels. @default 160 */\n readonly size = input(160, { transform: (v: unknown): number => Math.max(48, coerceNumberProperty(v, 160)) });\n\n /** Arc stroke thickness in viewBox units (out of 100). @default 10 */\n readonly strokeWidth = input(10, {\n transform: (v: unknown): number => Math.max(1, coerceNumberProperty(v, 10)),\n });\n\n readonly trackColor = input<string>('rgba(var(--wr-color-light-rgb), 0.6)');\n readonly valueColor = input<string>('var(--wr-color-primary)');\n\n /** Show the value text in the center. @default true */\n readonly showValue = input(true, { transform: coerceBooleanProperty });\n\n readonly suffix = input<string>('');\n\n /**\n * Show a tooltip with the reading on hover, named by `ariaLabel` when one is\n * given. Pointer only — the meter announces the same reading. @default true\n */\n readonly tooltip = input(true, { transform: coerceBooleanProperty });\n\n private readonly surface = viewChild<ElementRef<HTMLElement>>('surface');\n\n // Geometry — semicircle in a 100×56 viewBox (centre at 50, 50).\n protected readonly cx = 50;\n protected readonly cy = 50;\n // A negative radius is invalid in the SVG path grammar, so the browser drops the arc\n // and the dial vanishes — which a fat stroke reaches at 99. `wr-knob` floors it too.\n protected readonly radius = computed(() => Math.max(1, 50 - this.strokeWidth() / 2 - 0.5));\n\n /**\n * The reading as it can actually be shown. Only `ratio` used to be clamped, so an\n * over-range value drew a full bar while `aria-valuenow` announced the raw number\n * against `aria-valuemax` — an invalid state for `role=\"meter\"` — and the printed\n * text disagreed with the bar as well.\n */\n protected readonly displayValue = computed(() => clamp(this.value(), this.min(), this.max()));\n\n protected readonly ratio = computed(() => {\n const range = this.max() - this.min();\n if (range <= 0) return 0;\n return clamp((this.value() - this.min()) / range, 0, 1);\n });\n\n protected readonly trackPath = computed(() => {\n const r = this.radius();\n return `M ${this.cx - r} ${this.cy} A ${r} ${r} 0 0 1 ${this.cx + r} ${this.cy}`;\n });\n\n protected readonly valuePath = computed(() => {\n const r = this.radius();\n const angle = Math.PI * (1 - this.ratio());\n const ex = this.cx + r * Math.cos(angle);\n const ey = this.cy - r * Math.sin(angle);\n // The arc always spans `π × ratio` radians (≤ 180°), so largeArc stays 0.\n // Previously this was set to 1 when ratio > 0.5, which made the swept arc\n // take the long way round and visually flipped the bar.\n return `M ${this.cx - r} ${this.cy} A ${r} ${r} 0 0 1 ${ex.toFixed(2)} ${ey.toFixed(2)}`;\n });\n\n protected readonly viewBox = '0 0 100 56';\n\n /**\n * The reading as the meter announces it — `aria-valuetext`, the number plus the\n * suffix — so the tooltip, the printed value and a screen reader all say one\n * thing.\n */\n protected readonly valueText = computed(() => `${this.displayValue()}${this.suffix()}`);\n\n /**\n * One tooltip, pointing at the top of the dial. The whole gauge is one section.\n * The label is the `ariaLabel` input and not the catalog fallback, which names\n * the kind of widget rather than the reading.\n */\n protected readonly tip = useChartTooltip(this.tooltip, () => {\n const el = this.surface()?.nativeElement;\n if (!el) return null;\n return {\n datum: { label: this.ariaLabel() ?? undefined, value: this.valueText(), color: this.valueColor() },\n anchor: el,\n };\n });\n}\n","<!--\n The dial is drawn in an aria-hidden <svg> and the printed value is optional, so\n with `showValue` off the component had no accessible representation at all.\n `role=\"meter\"` plus the value attributes make it readable either way.\n-->\n<div\n #surface\n class=\"wr-gauge__surface\"\n [style.width.px]=\"size()\"\n role=\"meter\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-valuenow]=\"displayValue()\"\n [attr.aria-valuetext]=\"valueText()\"\n (mouseenter)=\"tip.enter(0)\"\n (mouseleave)=\"tip.leave($event)\"\n>\n <svg [attr.viewBox]=\"viewBox\" aria-hidden=\"true\">\n <path\n class=\"wr-gauge__track\"\n [attr.d]=\"trackPath()\"\n fill=\"none\"\n [attr.stroke]=\"trackColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n stroke-linecap=\"round\"\n />\n <path\n class=\"wr-gauge__value\"\n [attr.d]=\"valuePath()\"\n fill=\"none\"\n [attr.stroke]=\"valueColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n stroke-linecap=\"round\"\n />\n </svg>\n @if (showValue()) {\n <div class=\"wr-gauge__text\">\n {{ displayValue() }}<span class=\"wr-gauge__suffix\">{{ suffix() }}</span>\n </div>\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
+ {"version":3,"file":"ngwr-gauge.mjs","sources":["../../../projects/lib/gauge/gauge.ts","../../../projects/lib/gauge/gauge.html","../../../projects/lib/gauge/ngwr-gauge.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, viewChild } from '@angular/core';\n\nimport { useI18nText } from 'ngwr/i18n';\nimport { useChartTooltip } from 'ngwr/popover';\nimport { clamp } from 'ngwr/utils';\n\n/**\n * Semicircle progress gauge. Display-only — for interactive use see the\n * {@link WrKnob} from `ngwr/knob`.\n *\n * @example\n * ```html\n * <wr-gauge [value]=\"72\" />\n * <wr-gauge [value]=\"9.5\" [min]=\"0\" [max]=\"10\" suffix=\"/10\" valueColor=\"var(--wr-color-warning)\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/gauge\n */\n@Component({\n selector: 'wr-gauge',\n templateUrl: './gauge.html',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-gauge' },\n})\nexport class WrGauge {\n /** Accessible name — the role needs one. Falls back to `gauge.label`. */\n readonly ariaLabel = input<string | null>(null);\n\n protected readonly resolvedAriaLabel = useI18nText(this.ariaLabel, 'gauge.label', 'Gauge');\n\n /**\n * The reading. Coerced like every other numeric input here — it was the only one\n * without it, so a NaN reached the arc's `d` as the literal text `NaN`, which is\n * invalid path geometry and drops the arc.\n */\n readonly value = input.required<number, unknown>({\n transform: (v: unknown): number => coerceNumberProperty(v, 0),\n });\n readonly min = input(0, { transform: (v: unknown): number => coerceNumberProperty(v, 0) });\n readonly max = input(100, { transform: (v: unknown): number => coerceNumberProperty(v, 100) });\n\n /** Diameter in CSS pixels. @default 160 */\n readonly size = input(160, { transform: (v: unknown): number => Math.max(48, coerceNumberProperty(v, 160)) });\n\n /** Arc stroke thickness in viewBox units (out of 100). @default 10 */\n readonly strokeWidth = input(10, {\n transform: (v: unknown): number => Math.max(1, coerceNumberProperty(v, 10)),\n });\n\n readonly trackColor = input<string>('rgba(var(--wr-color-light-rgb), 0.6)');\n readonly valueColor = input<string>('var(--wr-color-primary)');\n\n /** Show the value text in the center. @default true */\n readonly showValue = input(true, { transform: coerceBooleanProperty });\n\n readonly suffix = input<string>('');\n\n /**\n * Show a tooltip with the reading on hover, named by `ariaLabel` when one is\n * given. Pointer only — the meter announces the same reading. @default true\n */\n readonly tooltip = input(true, { transform: coerceBooleanProperty });\n\n private readonly surface = viewChild<ElementRef<HTMLElement>>('surface');\n\n // Geometry — semicircle in a 100×56 viewBox (centre at 50, 50).\n protected readonly cx = 50;\n protected readonly cy = 50;\n // A negative radius is invalid in the SVG path grammar, so the browser drops the arc\n // and the dial vanishes — which a fat stroke reaches at 99. `wr-knob` floors it too.\n protected readonly radius = computed(() => Math.max(1, 50 - this.strokeWidth() / 2 - 0.5));\n\n /**\n * The reading as it can actually be shown. Only `ratio` used to be clamped, so an\n * over-range value drew a full bar while `aria-valuenow` announced the raw number\n * against `aria-valuemax` — an invalid state for `role=\"meter\"` — and the printed\n * text disagreed with the bar as well.\n */\n protected readonly displayValue = computed(() => clamp(this.value(), this.min(), this.max()));\n\n protected readonly ratio = computed(() => {\n const range = this.max() - this.min();\n if (range <= 0) return 0;\n return clamp((this.value() - this.min()) / range, 0, 1);\n });\n\n protected readonly trackPath = computed(() => {\n const r = this.radius();\n return `M ${this.cx - r} ${this.cy} A ${r} ${r} 0 0 1 ${this.cx + r} ${this.cy}`;\n });\n\n protected readonly valuePath = computed(() => {\n const r = this.radius();\n const angle = Math.PI * (1 - this.ratio());\n const ex = this.cx + r * Math.cos(angle);\n const ey = this.cy - r * Math.sin(angle);\n // The arc always spans `π × ratio` radians (≤ 180°), so largeArc stays 0.\n // Previously this was set to 1 when ratio > 0.5, which made the swept arc\n // take the long way round and visually flipped the bar.\n return `M ${this.cx - r} ${this.cy} A ${r} ${r} 0 0 1 ${ex.toFixed(2)} ${ey.toFixed(2)}`;\n });\n\n protected readonly viewBox = '0 0 100 56';\n\n /**\n * The reading as the meter announces it — `aria-valuetext`, the number plus the\n * suffix — so the tooltip, the printed value and a screen reader all say one\n * thing.\n */\n protected readonly valueText = computed(() => `${this.displayValue()}${this.suffix()}`);\n\n /**\n * One tooltip, pointing at the top of the dial. The whole gauge is one section.\n * The label is the `ariaLabel` input and not the catalog fallback, which names\n * the kind of widget rather than the reading.\n */\n protected readonly tip = useChartTooltip(this.tooltip, () => {\n const el = this.surface()?.nativeElement;\n if (!el) return null;\n return {\n datum: { label: this.ariaLabel() ?? undefined, value: this.valueText(), color: this.valueColor() },\n anchor: el,\n };\n });\n}\n","<!--\n The dial is drawn in an aria-hidden <svg> and the printed value is optional, so\n with `showValue` off the component had no accessible representation at all.\n `role=\"meter\"` plus the value attributes make it readable either way.\n-->\n<div\n #surface\n class=\"wr-gauge__surface\"\n [style.width.px]=\"size()\"\n role=\"meter\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-valuenow]=\"displayValue()\"\n [attr.aria-valuetext]=\"valueText()\"\n (mouseenter)=\"tip.enter(0)\"\n (mouseleave)=\"tip.leave($event)\"\n>\n <svg [attr.viewBox]=\"viewBox\" aria-hidden=\"true\">\n <path\n class=\"wr-gauge__track\"\n [attr.d]=\"trackPath()\"\n fill=\"none\"\n [attr.stroke]=\"trackColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n stroke-linecap=\"round\"\n />\n <path\n class=\"wr-gauge__value\"\n [attr.d]=\"valuePath()\"\n fill=\"none\"\n [attr.stroke]=\"valueColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n stroke-linecap=\"round\"\n />\n </svg>\n @if (showValue()) {\n <div class=\"wr-gauge__text\">\n {{ displayValue() }}<span class=\"wr-gauge__suffix\">{{ suffix() }}</span>\n </div>\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-glitch-text-testing.mjs","sources":["../../../projects/lib/glitch-text/testing/wr-glitch-text-harness.ts","../../../projects/lib/glitch-text/testing/ngwr-glitch-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 } from '@angular/cdk/testing';\n\nimport type { WrGlitchTextColourSplit, WrGlitchTextDurations, WrGlitchTextHarnessFilters } from './interfaces';\n\n/**\n * Test harness for `<wr-glitch-text>`.\n *\n * **The one thing worth testing here is the pairing.** The two torn clones are\n * `::before` / `::after` pseudo-elements whose glyphs come from `content:\n * attr(data-text)`, so the string has to be in `__label`'s text node AND in the\n * `data-text` attribute on `__clones`. Drop either binding and the visible text stays\n * flawless while the effect disappears — no error, no missing element, nothing else in\n * the DOM changes. {@link isCloneTextInSync} is that assertion in one call.\n *\n * **Why they are two elements**, which is also why `data-text` is not on the host:\n * generated content is exposed to the accessibility tree, so pseudos on the host made\n * the string announce three times. `__clones` carries the `aria-hidden`;\n * {@link isCloneLayerHidden} is the assertion that keeps it there.\n *\n * **Everything else the harness reads is an inline custom property**, because that is\n * everything the component writes: the two clone durations, the two colour-split\n * shadows and the optional slice background. They are worth pinning for the unit\n * transforms — a duration that loses its `s`, or two clones that end up on the same\n * multiple and tear in lockstep, is a regression that changes no class, no attribute\n * and no text.\n *\n * **What the harness deliberately does not offer**, and why. There is no\n * `isGlitching()`: the tear is a `@keyframes` animation on a pseudo-element, applied by\n * a stylesheet a unit test never loads, so the only available answer is a class name\n * repackaged as a state. There is no `hover()` / `isGlitchingOnHover()`: the gate is a\n * `:hover` SELECTOR, and the DOM after a dispatched mouse event is byte-identical to\n * the DOM before it — a spec built on that would pass against a component whose hover\n * rules had been deleted. There is no `getCloneOffset()` / `getClipPath()`: the ±10px\n * offsets and the tear bands live in the stylesheet and are pure layout besides. There\n * is no `respectsReducedMotion()`: the handling is a `@media` block with no TypeScript\n * counterpart, so the DOM is identical in both states and there is nothing to read.\n * There is no resolver for the shadow colours — they hold `var(--wr-color-info)` /\n * `var(--wr-color-danger)`, and resolving those needs the theme stylesheet. And there\n * is no `getSpeed()`: the input is never written to the DOM, only its two products are,\n * and dividing one back would invent a precision the component never published (at\n * `[speed]=\"0.3\"` the after-duration is literally `0.8999999999999999s`).\n *\n * @example\n * ```ts\n * const glitch = await loader.getHarness(WrGlitchTextHarness);\n *\n * expect(await glitch.isCloneTextInSync()).toBe(true);\n * expect((await glitch.getDurations()).after).toBeCloseTo(3);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrGlitchTextHarness extends ComponentHarness {\n static hostSelector = 'wr-glitch-text';\n\n /** The readable copy of the string — the only part of the component in the a11y tree. */\n private readonly label = this.locatorFor('.wr-glitch-text__label');\n\n /** The `aria-hidden` layer the two pseudo-clones hang off. */\n private readonly clones = this.locatorFor('.wr-glitch-text__clones');\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrGlitchTextHarnessFilters = {}): HarnessPredicate<WrGlitchTextHarness> {\n return new HarnessPredicate(WrGlitchTextHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getText(), text))\n .addOption(\n 'hoverOnly',\n options.hoverOnly,\n async (harness, hoverOnly) => (await harness.isHoverOnly()) === hoverOnly\n );\n }\n\n /**\n * The rendered text, trimmed.\n *\n * The half a sighted user reads, and the half every other check would notice\n * breaking. Read off `__label`, which is the whole of the component's accessible\n * content — the clone layer beside it is `aria-hidden` and empty in the DOM.\n */\n async getText(): Promise<string> {\n return (await this.label()).text();\n }\n\n /**\n * The string the two clone layers draw from — `data-text` on `__clones`.\n *\n * The half nothing else notices. `content: attr(data-text)` is how both pseudo-clones\n * get their glyphs, so this attribute IS the effect. `null` means the mirror is gone\n * and the component is rendering plain text with extra classes.\n */\n async getCloneText(): Promise<string | null> {\n return (await this.clones()).getAttribute('data-text');\n }\n\n /**\n * Whether the clone attribute still mirrors the rendered text.\n *\n * The component's defining silent failure, as a single assertion — two separate reads\n * are two things a spec can forget to write together. Compared raw on both sides,\n * with no trimming: `__label` holds the bare interpolation, so its text node and the\n * attribute are byte-identical or the mirror is broken. An empty `text` is legal and\n * counts as in sync — `data-text=\"\"` is a mirror of nothing, not a missing binding,\n * which is why {@link getCloneText} reports the absent case as `null` instead.\n */\n async isCloneTextInSync(): Promise<boolean> {\n const label = await this.label();\n return (await label.getProperty<string>('textContent')) === (await (await this.clones()).getAttribute('data-text'));\n }\n\n /**\n * Whether the clone layer is still hidden from assistive technology.\n *\n * The reason the component is two elements rather than one. `content: attr(data-text)`\n * is exposed to the accessibility tree, so with the pseudos on the host the string was\n * announced three times and a wrapping heading or link computed its name as\n * \"404 404 404\". Nothing visual changes if the `aria-hidden` is dropped, and neither\n * a11y gate in this repo can see it: `check:a11y` reads prerendered HTML with no\n * stylesheets, and axe has no rule for duplicated generated content.\n */\n async isCloneLayerHidden(): Promise<boolean> {\n return (await (await this.clones()).getAttribute('aria-hidden')) === 'true';\n }\n\n /**\n * Whether the effect idles until the pointer arrives (`enableOnHover`).\n *\n * The `wr-glitch-text--hover-only` class is the only DOM trace of that input, the gate\n * itself being a `:hover` rule. It defaults to `true`, so this is also how a spec\n * catches the day someone \"fixes\" the default to match the component's own JSDoc,\n * which describes the opposite.\n */\n async isHoverOnly(): Promise<boolean> {\n return (await this.host()).hasClass('wr-glitch-text--hover-only');\n }\n\n /**\n * How long each clone takes to complete a cycle, in seconds.\n *\n * The two must NOT be equal: they run at 2× and 3× the `speed` multiplier so the tears\n * fall out of phase, and clones tearing in lockstep read as one shifted copy rather\n * than as a glitch. That regression changes no class, no attribute and no text, and no\n * visual gate in this repo can see it either.\n *\n * These are raw JavaScript floats — `[speed]=\"0.3\"` writes `0.8999999999999999s` —\n * so compare with `toBeCloseTo`, never against a formatted string.\n */\n async getDurations(): Promise<WrGlitchTextDurations> {\n return {\n before: await this.seconds('--wr-glitch-text-before-duration'),\n after: await this.seconds('--wr-glitch-text-after-duration'),\n };\n }\n\n /**\n * Whether the red / cyan colour split is switched on (`enableShadows`).\n *\n * The component writes the literal `none` rather than removing the properties when the\n * split is off, so \"disabled\" and \"never set\" stay distinguishable — an absent property\n * would mean a broken binding, and this reports that as no split either.\n */\n async hasColourSplit(): Promise<boolean> {\n const { before, after } = await this.getColourSplit();\n return before !== 'none' && after !== 'none' && before !== '' && after !== '';\n }\n\n /**\n * The two clone `text-shadow`s, exactly as written — `var()` tokens and all.\n *\n * Returned unresolved on purpose: resolving `var(--wr-color-info)` needs the theme\n * stylesheet, and a unit test that answered with a colour would be inventing one.\n * What the raw pair does prove is the thing a copy-paste breaks — the offsets must\n * have OPPOSITE signs and the two tints must be DIFFERENT intents. Give both clones\n * the same colour, or the same sign, and the chromatic aberration is gone while every\n * other reading stays green.\n */\n async getColourSplit(): Promise<WrGlitchTextColourSplit> {\n return {\n before: await this.inlineVar('--wr-glitch-text-before-shadow'),\n after: await this.inlineVar('--wr-glitch-text-after-shadow'),\n };\n }\n\n /**\n * The masking colour behind the clone slices, or `null` when none was given.\n *\n * The binding emits `null` for an empty input, so the property is ABSENT rather than\n * set to `transparent`, and the stylesheet's own `var(--wr-glitch-text-bg, transparent)`\n * fallback does the work. A component that started writing `transparent` inline would\n * look identical and break nothing until a consumer relied on that fallback — which is\n * the whole reason this reports the absent case as `null` instead of as a colour.\n */\n async getSliceBackground(): Promise<string | null> {\n const raw = await this.inlineVar('--wr-glitch-text-bg');\n return raw === '' ? null : raw;\n }\n\n /**\n * One custom property off the host's `style` ATTRIBUTE.\n *\n * Not `getCssValue()`, which is `getComputedStyle`: with the entry point's own\n * stylesheet loaded that resolves the sheet's declaration, so the method\n * answers plausibly at exactly the moment the host binding — the thing under\n * test — is what broke. The attribute holds only what the component wrote.\n */\n private async inlineVar(property: string): Promise<string> {\n const attr = (await (await this.host()).getAttribute('style')) ?? '';\n const found = new RegExp(`(?:^|;)\\\\s*${property}\\\\s*:\\\\s*([^;]+)`).exec(attr);\n return (found?.[1] ?? '').trim();\n }\n\n /** One of the host's inline duration properties, as a number of seconds. */\n private async seconds(property: string): Promise<number> {\n const raw = await this.inlineVar(property);\n const value = Number.parseFloat(raw);\n\n if (Number.isNaN(value)) {\n throw new Error(\n `WrGlitchTextHarness.getDurations(): \\`${property}\\` computed to \"${raw}\", which is not a number of ` +\n 'seconds. The component writes it inline on the host as a multiple of `[speed]`; an empty value means ' +\n 'that binding is gone, and there is no honest duration to report in its place.'\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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"}
1
+ {"version":3,"file":"ngwr-glitch-text-testing.mjs","sources":["../../../projects/lib/glitch-text/testing/wr-glitch-text-harness.ts","../../../projects/lib/glitch-text/testing/ngwr-glitch-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 } from '@angular/cdk/testing';\n\nimport type { WrGlitchTextColourSplit, WrGlitchTextDurations, WrGlitchTextHarnessFilters } from './interfaces';\n\n/**\n * Test harness for `<wr-glitch-text>`.\n *\n * **The one thing worth testing here is the pairing.** The two torn clones are\n * `::before` / `::after` pseudo-elements whose glyphs come from `content:\n * attr(data-text)`, so the string has to be in `__label`'s text node AND in the\n * `data-text` attribute on `__clones`. Drop either binding and the visible text stays\n * flawless while the effect disappears — no error, no missing element, nothing else in\n * the DOM changes. {@link isCloneTextInSync} is that assertion in one call.\n *\n * **Why they are two elements**, which is also why `data-text` is not on the host:\n * generated content is exposed to the accessibility tree, so pseudos on the host made\n * the string announce three times. `__clones` carries the `aria-hidden`;\n * {@link isCloneLayerHidden} is the assertion that keeps it there.\n *\n * **Everything else the harness reads is an inline custom property**, because that is\n * everything the component writes: the two clone durations, the two colour-split\n * shadows and the optional slice background. They are worth pinning for the unit\n * transforms — a duration that loses its `s`, or two clones that end up on the same\n * multiple and tear in lockstep, is a regression that changes no class, no attribute\n * and no text.\n *\n * **What the harness deliberately does not offer**, and why. There is no\n * `isGlitching()`: the tear is a `@keyframes` animation on a pseudo-element, applied by\n * a stylesheet a unit test never loads, so the only available answer is a class name\n * repackaged as a state. There is no `hover()` / `isGlitchingOnHover()`: the gate is a\n * `:hover` SELECTOR, and the DOM after a dispatched mouse event is byte-identical to\n * the DOM before it — a spec built on that would pass against a component whose hover\n * rules had been deleted. There is no `getCloneOffset()` / `getClipPath()`: the ±10px\n * offsets and the tear bands live in the stylesheet and are pure layout besides. There\n * is no `respectsReducedMotion()`: the handling is a `@media` block with no TypeScript\n * counterpart, so the DOM is identical in both states and there is nothing to read.\n * There is no resolver for the shadow colours — they hold `var(--wr-color-info)` /\n * `var(--wr-color-danger)`, and resolving those needs the theme stylesheet. And there\n * is no `getSpeed()`: the input is never written to the DOM, only its two products are,\n * and dividing one back would invent a precision the component never published (at\n * `[speed]=\"0.3\"` the after-duration is literally `0.8999999999999999s`).\n *\n * @example\n * ```ts\n * const glitch = await loader.getHarness(WrGlitchTextHarness);\n *\n * expect(await glitch.isCloneTextInSync()).toBe(true);\n * expect((await glitch.getDurations()).after).toBeCloseTo(3);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrGlitchTextHarness extends ComponentHarness {\n static hostSelector = 'wr-glitch-text';\n\n /** The readable copy of the string — the only part of the component in the a11y tree. */\n private readonly label = this.locatorFor('.wr-glitch-text__label');\n\n /** The `aria-hidden` layer the two pseudo-clones hang off. */\n private readonly clones = this.locatorFor('.wr-glitch-text__clones');\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrGlitchTextHarnessFilters = {}): HarnessPredicate<WrGlitchTextHarness> {\n return new HarnessPredicate(WrGlitchTextHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getText(), text))\n .addOption(\n 'hoverOnly',\n options.hoverOnly,\n async (harness, hoverOnly) => (await harness.isHoverOnly()) === hoverOnly\n );\n }\n\n /**\n * The rendered text, trimmed.\n *\n * The half a sighted user reads, and the half every other check would notice\n * breaking. Read off `__label`, which is the whole of the component's accessible\n * content — the clone layer beside it is `aria-hidden` and empty in the DOM.\n */\n async getText(): Promise<string> {\n return (await this.label()).text();\n }\n\n /**\n * The string the two clone layers draw from — `data-text` on `__clones`.\n *\n * The half nothing else notices. `content: attr(data-text)` is how both pseudo-clones\n * get their glyphs, so this attribute IS the effect. `null` means the mirror is gone\n * and the component is rendering plain text with extra classes.\n */\n async getCloneText(): Promise<string | null> {\n return (await this.clones()).getAttribute('data-text');\n }\n\n /**\n * Whether the clone attribute still mirrors the rendered text.\n *\n * The component's defining silent failure, as a single assertion — two separate reads\n * are two things a spec can forget to write together. Compared raw on both sides,\n * with no trimming: `__label` holds the bare interpolation, so its text node and the\n * attribute are byte-identical or the mirror is broken. An empty `text` is legal and\n * counts as in sync — `data-text=\"\"` is a mirror of nothing, not a missing binding,\n * which is why {@link getCloneText} reports the absent case as `null` instead.\n */\n async isCloneTextInSync(): Promise<boolean> {\n const label = await this.label();\n return (await label.getProperty<string>('textContent')) === (await (await this.clones()).getAttribute('data-text'));\n }\n\n /**\n * Whether the clone layer is still hidden from assistive technology.\n *\n * The reason the component is two elements rather than one. `content: attr(data-text)`\n * is exposed to the accessibility tree, so with the pseudos on the host the string was\n * announced three times and a wrapping heading or link computed its name as\n * \"404 404 404\". Nothing visual changes if the `aria-hidden` is dropped, and neither\n * a11y gate in this repo can see it: `check:a11y` reads prerendered HTML with no\n * stylesheets, and axe has no rule for duplicated generated content.\n */\n async isCloneLayerHidden(): Promise<boolean> {\n return (await (await this.clones()).getAttribute('aria-hidden')) === 'true';\n }\n\n /**\n * Whether the effect idles until the pointer arrives (`enableOnHover`).\n *\n * The `wr-glitch-text--hover-only` class is the only DOM trace of that input, the gate\n * itself being a `:hover` rule. It defaults to `true`, so this is also how a spec\n * catches the day someone \"fixes\" the default to match the component's own JSDoc,\n * which describes the opposite.\n */\n async isHoverOnly(): Promise<boolean> {\n return (await this.host()).hasClass('wr-glitch-text--hover-only');\n }\n\n /**\n * How long each clone takes to complete a cycle, in seconds.\n *\n * The two must NOT be equal: they run at 2× and 3× the `speed` multiplier so the tears\n * fall out of phase, and clones tearing in lockstep read as one shifted copy rather\n * than as a glitch. That regression changes no class, no attribute and no text, and no\n * visual gate in this repo can see it either.\n *\n * These are raw JavaScript floats — `[speed]=\"0.3\"` writes `0.8999999999999999s` —\n * so compare with `toBeCloseTo`, never against a formatted string.\n */\n async getDurations(): Promise<WrGlitchTextDurations> {\n return {\n before: await this.seconds('--wr-glitch-text-before-duration'),\n after: await this.seconds('--wr-glitch-text-after-duration'),\n };\n }\n\n /**\n * Whether the red / cyan colour split is switched on (`enableShadows`).\n *\n * The component writes the literal `none` rather than removing the properties when the\n * split is off, so \"disabled\" and \"never set\" stay distinguishable — an absent property\n * would mean a broken binding, and this reports that as no split either.\n */\n async hasColourSplit(): Promise<boolean> {\n const { before, after } = await this.getColourSplit();\n return before !== 'none' && after !== 'none' && before !== '' && after !== '';\n }\n\n /**\n * The two clone `text-shadow`s, exactly as written — `var()` tokens and all.\n *\n * Returned unresolved on purpose: resolving `var(--wr-color-info)` needs the theme\n * stylesheet, and a unit test that answered with a colour would be inventing one.\n * What the raw pair does prove is the thing a copy-paste breaks — the offsets must\n * have OPPOSITE signs and the two tints must be DIFFERENT intents. Give both clones\n * the same colour, or the same sign, and the chromatic aberration is gone while every\n * other reading stays green.\n */\n async getColourSplit(): Promise<WrGlitchTextColourSplit> {\n return {\n before: await this.inlineVar('--wr-glitch-text-before-shadow'),\n after: await this.inlineVar('--wr-glitch-text-after-shadow'),\n };\n }\n\n /**\n * The masking colour behind the clone slices, or `null` when none was given.\n *\n * The binding emits `null` for an empty input, so the property is ABSENT rather than\n * set to `transparent`, and the stylesheet's own `var(--wr-glitch-text-bg, transparent)`\n * fallback does the work. A component that started writing `transparent` inline would\n * look identical and break nothing until a consumer relied on that fallback — which is\n * the whole reason this reports the absent case as `null` instead of as a colour.\n */\n async getSliceBackground(): Promise<string | null> {\n const raw = await this.inlineVar('--wr-glitch-text-bg');\n return raw === '' ? null : raw;\n }\n\n /**\n * One custom property off the host's `style` ATTRIBUTE.\n *\n * Not `getCssValue()`, which is `getComputedStyle`: with the entry point's own\n * stylesheet loaded that resolves the sheet's declaration, so the method\n * answers plausibly at exactly the moment the host binding — the thing under\n * test — is what broke. The attribute holds only what the component wrote.\n */\n private async inlineVar(property: string): Promise<string> {\n const attr = (await (await this.host()).getAttribute('style')) ?? '';\n const found = new RegExp(`(?:^|;)\\\\s*${property}\\\\s*:\\\\s*([^;]+)`).exec(attr);\n return (found?.[1] ?? '').trim();\n }\n\n /** One of the host's inline duration properties, as a number of seconds. */\n private async seconds(property: string): Promise<number> {\n const raw = await this.inlineVar(property);\n const value = Number.parseFloat(raw);\n\n if (Number.isNaN(value)) {\n throw new Error(\n `WrGlitchTextHarness.getDurations(): \\`${property}\\` computed to \"${raw}\", which is not a number of ` +\n 'seconds. The component writes it inline on the host as a multiple of `[speed]`; an empty value means ' +\n 'that binding is gone, and there is no honest duration to report in its place.'\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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"}
@@ -51,10 +51,10 @@ class WrGlitchText {
51
51
  ...(ngDevMode ? [{ debugName: "beforeShadow" }] : /* istanbul ignore next */ []));
52
52
  afterShadow = computed(() => (this.enableShadows() ? '-5px 0 var(--wr-color-danger)' : 'none'), /* @ts-ignore */
53
53
  ...(ngDevMode ? [{ debugName: "afterShadow" }] : /* istanbul ignore next */ []));
54
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrGlitchText, deps: [], target: i0.ɵɵFactoryTarget.Component });
55
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: WrGlitchText, isStandalone: true, selector: "wr-glitch-text", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: true, transformFunction: null }, speed: { classPropertyName: "speed", publicName: "speed", isSignal: true, isRequired: false, transformFunction: null }, enableShadows: { classPropertyName: "enableShadows", publicName: "enableShadows", isSignal: true, isRequired: false, transformFunction: null }, enableOnHover: { classPropertyName: "enableOnHover", publicName: "enableOnHover", isSignal: true, isRequired: false, transformFunction: null }, background: { classPropertyName: "background", publicName: "background", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.wr-glitch-text--hover-only": "enableOnHover()", "style.--wr-glitch-text-before-duration": "(speed() * 2) + 's'", "style.--wr-glitch-text-after-duration": "(speed() * 3) + 's'", "style.--wr-glitch-text-before-shadow": "beforeShadow()", "style.--wr-glitch-text-after-shadow": "afterShadow()", "style.--wr-glitch-text-bg": "background() || null" }, classAttribute: "wr-glitch-text" }, ngImport: i0, template: "<!--\n Two elements on purpose. The torn clones are `::before` / `::after` whose glyphs\n come from `content: attr(data-text)`, and CSS generated content IS exposed to the\n accessibility tree \u2014 so with the pseudos on the host the string was announced\n three times, and a heading or link wrapping this computed its name as\n \"404 404 404\". The clones now hang off their own `aria-hidden` layer instead:\n the same split `wr-blur-text` and `wr-circular-text` use \u2014 one readable copy,\n one decorative layer taken out of the tree with a single `aria-hidden`.\n\n `__clones` comes second and is absolutely positioned, so it contributes nothing\n to layout and still paints over the label, exactly as the host's own pseudos did.\n-->\n<span class=\"wr-glitch-text__label\">{{ text() }}</span>\n<span class=\"wr-glitch-text__clones\" aria-hidden=\"true\" [attr.data-text]=\"text()\"></span>\n", styles: [".wr-glitch-text{--wr-glitch-text-before-duration: 2s;--wr-glitch-text-after-duration: 3s;--wr-glitch-text-before-shadow: 5px 0 var(--wr-color-info);--wr-glitch-text-after-shadow: -5px 0 var(--wr-color-danger);position:relative;display:inline-block;white-space:nowrap;-webkit-user-select:none;user-select:none;font-weight:900;color:var(--wr-color-on-surface, #0f172a)}.wr-glitch-text__clones{position:absolute;inset:0}.wr-glitch-text__clones:before,.wr-glitch-text__clones:after{content:attr(data-text);position:absolute;top:0;color:inherit;background-color:var(--wr-glitch-text-bg, transparent);overflow:hidden;clip-path:inset(0 0 0 0)}.wr-glitch-text:not(.wr-glitch-text--hover-only) .wr-glitch-text__clones:before{left:-10px;text-shadow:var(--wr-glitch-text-before-shadow);animation:wr-glitch-text-tear var(--wr-glitch-text-before-duration) infinite linear alternate-reverse}.wr-glitch-text:not(.wr-glitch-text--hover-only) .wr-glitch-text__clones:after{left:10px;text-shadow:var(--wr-glitch-text-after-shadow);animation:wr-glitch-text-tear var(--wr-glitch-text-after-duration) infinite linear alternate-reverse}.wr-glitch-text--hover-only .wr-glitch-text__clones:before,.wr-glitch-text--hover-only .wr-glitch-text__clones:after{content:\"\";opacity:0;animation:none}.wr-glitch-text--hover-only:hover .wr-glitch-text__clones:before{content:attr(data-text);opacity:1;left:-10px;text-shadow:var(--wr-glitch-text-before-shadow);animation:wr-glitch-text-tear var(--wr-glitch-text-before-duration) infinite linear alternate-reverse}.wr-glitch-text--hover-only:hover .wr-glitch-text__clones:after{content:attr(data-text);opacity:1;left:10px;text-shadow:var(--wr-glitch-text-after-shadow);animation:wr-glitch-text-tear var(--wr-glitch-text-after-duration) infinite linear alternate-reverse}@media(prefers-reduced-motion:reduce){.wr-glitch-text__clones:before,.wr-glitch-text__clones:after{content:\"\"!important;opacity:0!important;animation:none!important}}@keyframes wr-glitch-text-tear{0%{clip-path:inset(20% 0 50% 0)}5%{clip-path:inset(10% 0 60% 0)}10%{clip-path:inset(15% 0 55% 0)}15%{clip-path:inset(25% 0 35% 0)}20%{clip-path:inset(30% 0 40% 0)}25%{clip-path:inset(40% 0 20% 0)}30%{clip-path:inset(10% 0 60% 0)}35%{clip-path:inset(15% 0 55% 0)}40%{clip-path:inset(25% 0 35% 0)}45%{clip-path:inset(30% 0 40% 0)}50%{clip-path:inset(20% 0 50% 0)}55%{clip-path:inset(10% 0 60% 0)}60%{clip-path:inset(15% 0 55% 0)}65%{clip-path:inset(25% 0 35% 0)}70%{clip-path:inset(30% 0 40% 0)}75%{clip-path:inset(40% 0 20% 0)}80%{clip-path:inset(20% 0 50% 0)}85%{clip-path:inset(10% 0 60% 0)}90%{clip-path:inset(15% 0 55% 0)}95%{clip-path:inset(25% 0 35% 0)}to{clip-path:inset(30% 0 40% 0)}}\n"], encapsulation: i0.ViewEncapsulation.None });
54
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrGlitchText, deps: [], target: i0.ɵɵFactoryTarget.Component });
55
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.7", type: WrGlitchText, isStandalone: true, selector: "wr-glitch-text", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: true, transformFunction: null }, speed: { classPropertyName: "speed", publicName: "speed", isSignal: true, isRequired: false, transformFunction: null }, enableShadows: { classPropertyName: "enableShadows", publicName: "enableShadows", isSignal: true, isRequired: false, transformFunction: null }, enableOnHover: { classPropertyName: "enableOnHover", publicName: "enableOnHover", isSignal: true, isRequired: false, transformFunction: null }, background: { classPropertyName: "background", publicName: "background", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.wr-glitch-text--hover-only": "enableOnHover()", "style.--wr-glitch-text-before-duration": "(speed() * 2) + 's'", "style.--wr-glitch-text-after-duration": "(speed() * 3) + 's'", "style.--wr-glitch-text-before-shadow": "beforeShadow()", "style.--wr-glitch-text-after-shadow": "afterShadow()", "style.--wr-glitch-text-bg": "background() || null" }, classAttribute: "wr-glitch-text" }, ngImport: i0, template: "<!--\n Two elements on purpose. The torn clones are `::before` / `::after` whose glyphs\n come from `content: attr(data-text)`, and CSS generated content IS exposed to the\n accessibility tree \u2014 so with the pseudos on the host the string was announced\n three times, and a heading or link wrapping this computed its name as\n \"404 404 404\". The clones now hang off their own `aria-hidden` layer instead:\n the same split `wr-blur-text` and `wr-circular-text` use \u2014 one readable copy,\n one decorative layer taken out of the tree with a single `aria-hidden`.\n\n `__clones` comes second and is absolutely positioned, so it contributes nothing\n to layout and still paints over the label, exactly as the host's own pseudos did.\n-->\n<span class=\"wr-glitch-text__label\">{{ text() }}</span>\n<span class=\"wr-glitch-text__clones\" aria-hidden=\"true\" [attr.data-text]=\"text()\"></span>\n", styles: [".wr-glitch-text{--wr-glitch-text-before-duration: 2s;--wr-glitch-text-after-duration: 3s;--wr-glitch-text-before-shadow: 5px 0 var(--wr-color-info);--wr-glitch-text-after-shadow: -5px 0 var(--wr-color-danger);position:relative;display:inline-block;white-space:nowrap;-webkit-user-select:none;user-select:none;font-weight:900;color:var(--wr-color-on-surface, #0f172a)}.wr-glitch-text__clones{position:absolute;inset:0}.wr-glitch-text__clones:before,.wr-glitch-text__clones:after{content:attr(data-text);position:absolute;top:0;color:inherit;background-color:var(--wr-glitch-text-bg, transparent);overflow:hidden;clip-path:inset(0 0 0 0)}.wr-glitch-text:not(.wr-glitch-text--hover-only) .wr-glitch-text__clones:before{left:-10px;text-shadow:var(--wr-glitch-text-before-shadow);animation:wr-glitch-text-tear var(--wr-glitch-text-before-duration) infinite linear alternate-reverse}.wr-glitch-text:not(.wr-glitch-text--hover-only) .wr-glitch-text__clones:after{left:10px;text-shadow:var(--wr-glitch-text-after-shadow);animation:wr-glitch-text-tear var(--wr-glitch-text-after-duration) infinite linear alternate-reverse}.wr-glitch-text--hover-only .wr-glitch-text__clones:before,.wr-glitch-text--hover-only .wr-glitch-text__clones:after{content:\"\";opacity:0;animation:none}.wr-glitch-text--hover-only:hover .wr-glitch-text__clones:before{content:attr(data-text);opacity:1;left:-10px;text-shadow:var(--wr-glitch-text-before-shadow);animation:wr-glitch-text-tear var(--wr-glitch-text-before-duration) infinite linear alternate-reverse}.wr-glitch-text--hover-only:hover .wr-glitch-text__clones:after{content:attr(data-text);opacity:1;left:10px;text-shadow:var(--wr-glitch-text-after-shadow);animation:wr-glitch-text-tear var(--wr-glitch-text-after-duration) infinite linear alternate-reverse}@media(prefers-reduced-motion:reduce){.wr-glitch-text__clones:before,.wr-glitch-text__clones:after{content:\"\"!important;opacity:0!important;animation:none!important}}@keyframes wr-glitch-text-tear{0%{clip-path:inset(20% 0 50% 0)}5%{clip-path:inset(10% 0 60% 0)}10%{clip-path:inset(15% 0 55% 0)}15%{clip-path:inset(25% 0 35% 0)}20%{clip-path:inset(30% 0 40% 0)}25%{clip-path:inset(40% 0 20% 0)}30%{clip-path:inset(10% 0 60% 0)}35%{clip-path:inset(15% 0 55% 0)}40%{clip-path:inset(25% 0 35% 0)}45%{clip-path:inset(30% 0 40% 0)}50%{clip-path:inset(20% 0 50% 0)}55%{clip-path:inset(10% 0 60% 0)}60%{clip-path:inset(15% 0 55% 0)}65%{clip-path:inset(25% 0 35% 0)}70%{clip-path:inset(30% 0 40% 0)}75%{clip-path:inset(40% 0 20% 0)}80%{clip-path:inset(20% 0 50% 0)}85%{clip-path:inset(10% 0 60% 0)}90%{clip-path:inset(15% 0 55% 0)}95%{clip-path:inset(25% 0 35% 0)}to{clip-path:inset(30% 0 40% 0)}}\n"], encapsulation: i0.ViewEncapsulation.None });
56
56
  }
57
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrGlitchText, decorators: [{
57
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrGlitchText, decorators: [{
58
58
  type: Component,
59
59
  args: [{ selector: 'wr-glitch-text', encapsulation: ViewEncapsulation.None, host: {
60
60
  class: 'wr-glitch-text',
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-glitch-text.mjs","sources":["../../../projects/lib/glitch-text/glitch-text.ts","../../../projects/lib/glitch-text/glitch-text.html","../../../projects/lib/glitch-text/ngwr-glitch-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 GlitchText effect by David Haz / reactbits.dev.\n * Original: https://www.reactbits.dev/text-animations/glitch-text\n *\n * Pure CSS — two `::before` / `::after` clones offset by ±10px with\n * coloured `text-shadow`s, both running a clip-path keyframe to slice\n * random horizontal bands. The clones hang off an `aria-hidden` layer\n * rather than the host, because generated content is announced.\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\nimport { numAttr } from 'ngwr/utils';\n\n/**\n * Glitchy text effect — colour-split horizontal-tear glitch on the\n * provided text. Defaults to playing constantly; toggle `[enableOnHover]`\n * to make it idle until the user hovers.\n *\n * @example\n * ```html\n * <wr-glitch-text text=\"404\" />\n * <wr-glitch-text text=\"ERROR\" [speed]=\"0.5\" [enableOnHover]=\"true\" />\n * ```\n *\n * @see https://www.reactbits.dev/text-animations/glitch-text\n */\n@Component({\n selector: 'wr-glitch-text',\n templateUrl: './glitch-text.html',\n styleUrl: './glitch-text.scss',\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-glitch-text',\n '[class.wr-glitch-text--hover-only]': 'enableOnHover()',\n '[style.--wr-glitch-text-before-duration]': \"(speed() * 2) + 's'\",\n '[style.--wr-glitch-text-after-duration]': \"(speed() * 3) + 's'\",\n '[style.--wr-glitch-text-before-shadow]': 'beforeShadow()',\n '[style.--wr-glitch-text-after-shadow]': 'afterShadow()',\n '[style.--wr-glitch-text-bg]': 'background() || null',\n },\n})\nexport class WrGlitchText {\n /** Text to glitch. Required — the value populates `data-text` for the pseudo clones. */\n readonly text = input.required<string>();\n\n /** Time multiplier — higher = slower glitching. @default 1 */\n readonly speed = input(1, { transform: numAttr(1) });\n\n /** Show the red / cyan colour-split shadows. @default true */\n readonly enableShadows = input(true, { transform: coerceBooleanProperty });\n\n /** Only glitch on hover (idle until then). @default true */\n readonly enableOnHover = input(true, { transform: coerceBooleanProperty });\n\n /**\n * Surface colour for the clone slices. Pass the colour of the surface the\n * component sits on so the slicing illusion masks cleanly; leave unset\n * (transparent) on the page canvas. @default '' (transparent)\n */\n readonly background = input('');\n\n protected readonly beforeShadow = computed(() => (this.enableShadows() ? '5px 0 var(--wr-color-info)' : 'none'));\n protected readonly afterShadow = computed(() => (this.enableShadows() ? '-5px 0 var(--wr-color-danger)' : 'none'));\n}\n","<!--\n Two elements on purpose. The torn clones are `::before` / `::after` whose glyphs\n come from `content: attr(data-text)`, and CSS generated content IS exposed to the\n accessibility tree — so with the pseudos on the host the string was announced\n three times, and a heading or link wrapping this computed its name as\n \"404 404 404\". The clones now hang off their own `aria-hidden` layer instead:\n the same split `wr-blur-text` and `wr-circular-text` use — one readable copy,\n one decorative layer taken out of the tree with a single `aria-hidden`.\n\n `__clones` comes second and is absolutely positioned, so it contributes nothing\n to layout and still paints over the label, exactly as the host's own pseudos did.\n-->\n<span class=\"wr-glitch-text__label\">{{ text() }}</span>\n<span class=\"wr-glitch-text__clones\" aria-hidden=\"true\" [attr.data-text]=\"text()\"></span>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;AAAA;;;;;;;;;;;;;AAaG;AAOH;;;;;;;;;;;;AAYG;MAgBU,YAAY,CAAA;;IAEd,IAAI,GAAG,KAAK,CAAC,QAAQ;6EAAU;;AAG/B,IAAA,KAAK,GAAG,KAAK,CAAC,CAAC,EAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,OAAA,EAAA,8BAAA,EAAA,CAAA,EAAI,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,EAAA,CAAG;;IAG3C,aAAa,GAAG,KAAK,CAAC,IAAI,qFAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;;IAGjE,aAAa,GAAG,KAAK,CAAC,IAAI,qFAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;AAE1E;;;;AAIG;IACM,UAAU,GAAG,KAAK,CAAC,EAAE;mFAAC;AAEZ,IAAA,YAAY,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,aAAa,EAAE,GAAG,4BAA4B,GAAG,MAAM,CAAC;qFAAC;AAC7F,IAAA,WAAW,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,aAAa,EAAE,GAAG,+BAA+B,GAAG,MAAM,CAAC;oFAAC;uGArBvG,YAAY,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAZ,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAY,ooCChDzB,+4BAcA,EAAA,MAAA,EAAA,CAAA,unFAAA,CAAA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FDkCa,YAAY,EAAA,UAAA,EAAA,CAAA;kBAfxB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,gBAAgB,EAAA,aAAA,EAGX,iBAAiB,CAAC,IAAI,EAAA,IAAA,EAC/B;AACJ,wBAAA,KAAK,EAAE,gBAAgB;AACvB,wBAAA,oCAAoC,EAAE,iBAAiB;AACvD,wBAAA,0CAA0C,EAAE,qBAAqB;AACjE,wBAAA,yCAAyC,EAAE,qBAAqB;AAChE,wBAAA,wCAAwC,EAAE,gBAAgB;AAC1D,wBAAA,uCAAuC,EAAE,eAAe;AACxD,wBAAA,6BAA6B,EAAE,sBAAsB;AACtD,qBAAA,EAAA,QAAA,EAAA,+4BAAA,EAAA,MAAA,EAAA,CAAA,unFAAA,CAAA,EAAA;;;AE9CH;;AAEG;;;;"}
1
+ {"version":3,"file":"ngwr-glitch-text.mjs","sources":["../../../projects/lib/glitch-text/glitch-text.ts","../../../projects/lib/glitch-text/glitch-text.html","../../../projects/lib/glitch-text/ngwr-glitch-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 GlitchText effect by David Haz / reactbits.dev.\n * Original: https://www.reactbits.dev/text-animations/glitch-text\n *\n * Pure CSS — two `::before` / `::after` clones offset by ±10px with\n * coloured `text-shadow`s, both running a clip-path keyframe to slice\n * random horizontal bands. The clones hang off an `aria-hidden` layer\n * rather than the host, because generated content is announced.\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\nimport { numAttr } from 'ngwr/utils';\n\n/**\n * Glitchy text effect — colour-split horizontal-tear glitch on the\n * provided text. Defaults to playing constantly; toggle `[enableOnHover]`\n * to make it idle until the user hovers.\n *\n * @example\n * ```html\n * <wr-glitch-text text=\"404\" />\n * <wr-glitch-text text=\"ERROR\" [speed]=\"0.5\" [enableOnHover]=\"true\" />\n * ```\n *\n * @see https://www.reactbits.dev/text-animations/glitch-text\n */\n@Component({\n selector: 'wr-glitch-text',\n templateUrl: './glitch-text.html',\n styleUrl: './glitch-text.scss',\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-glitch-text',\n '[class.wr-glitch-text--hover-only]': 'enableOnHover()',\n '[style.--wr-glitch-text-before-duration]': \"(speed() * 2) + 's'\",\n '[style.--wr-glitch-text-after-duration]': \"(speed() * 3) + 's'\",\n '[style.--wr-glitch-text-before-shadow]': 'beforeShadow()',\n '[style.--wr-glitch-text-after-shadow]': 'afterShadow()',\n '[style.--wr-glitch-text-bg]': 'background() || null',\n },\n})\nexport class WrGlitchText {\n /** Text to glitch. Required — the value populates `data-text` for the pseudo clones. */\n readonly text = input.required<string>();\n\n /** Time multiplier — higher = slower glitching. @default 1 */\n readonly speed = input(1, { transform: numAttr(1) });\n\n /** Show the red / cyan colour-split shadows. @default true */\n readonly enableShadows = input(true, { transform: coerceBooleanProperty });\n\n /** Only glitch on hover (idle until then). @default true */\n readonly enableOnHover = input(true, { transform: coerceBooleanProperty });\n\n /**\n * Surface colour for the clone slices. Pass the colour of the surface the\n * component sits on so the slicing illusion masks cleanly; leave unset\n * (transparent) on the page canvas. @default '' (transparent)\n */\n readonly background = input('');\n\n protected readonly beforeShadow = computed(() => (this.enableShadows() ? '5px 0 var(--wr-color-info)' : 'none'));\n protected readonly afterShadow = computed(() => (this.enableShadows() ? '-5px 0 var(--wr-color-danger)' : 'none'));\n}\n","<!--\n Two elements on purpose. The torn clones are `::before` / `::after` whose glyphs\n come from `content: attr(data-text)`, and CSS generated content IS exposed to the\n accessibility tree — so with the pseudos on the host the string was announced\n three times, and a heading or link wrapping this computed its name as\n \"404 404 404\". The clones now hang off their own `aria-hidden` layer instead:\n the same split `wr-blur-text` and `wr-circular-text` use — one readable copy,\n one decorative layer taken out of the tree with a single `aria-hidden`.\n\n `__clones` comes second and is absolutely positioned, so it contributes nothing\n to layout and still paints over the label, exactly as the host's own pseudos did.\n-->\n<span class=\"wr-glitch-text__label\">{{ text() }}</span>\n<span class=\"wr-glitch-text__clones\" aria-hidden=\"true\" [attr.data-text]=\"text()\"></span>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;AAAA;;;;;;;;;;;;;AAaG;AAOH;;;;;;;;;;;;AAYG;MAgBU,YAAY,CAAA;;IAEd,IAAI,GAAG,KAAK,CAAC,QAAQ;6EAAU;;AAG/B,IAAA,KAAK,GAAG,KAAK,CAAC,CAAC,EAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,OAAA,EAAA,8BAAA,EAAA,CAAA,EAAI,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,EAAA,CAAG;;IAG3C,aAAa,GAAG,KAAK,CAAC,IAAI,qFAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;;IAGjE,aAAa,GAAG,KAAK,CAAC,IAAI,qFAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;AAE1E;;;;AAIG;IACM,UAAU,GAAG,KAAK,CAAC,EAAE;mFAAC;AAEZ,IAAA,YAAY,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,aAAa,EAAE,GAAG,4BAA4B,GAAG,MAAM,CAAC;qFAAC;AAC7F,IAAA,WAAW,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,aAAa,EAAE,GAAG,+BAA+B,GAAG,MAAM,CAAC;oFAAC;uGArBvG,YAAY,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAZ,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAY,ooCChDzB,+4BAcA,EAAA,MAAA,EAAA,CAAA,unFAAA,CAAA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FDkCa,YAAY,EAAA,UAAA,EAAA,CAAA;kBAfxB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,gBAAgB,EAAA,aAAA,EAGX,iBAAiB,CAAC,IAAI,EAAA,IAAA,EAC/B;AACJ,wBAAA,KAAK,EAAE,gBAAgB;AACvB,wBAAA,oCAAoC,EAAE,iBAAiB;AACvD,wBAAA,0CAA0C,EAAE,qBAAqB;AACjE,wBAAA,yCAAyC,EAAE,qBAAqB;AAChE,wBAAA,wCAAwC,EAAE,gBAAgB;AAC1D,wBAAA,uCAAuC,EAAE,eAAe;AACxD,wBAAA,6BAA6B,EAAE,sBAAsB;AACtD,qBAAA,EAAA,QAAA,EAAA,+4BAAA,EAAA,MAAA,EAAA,CAAA,unFAAA,CAAA,EAAA;;;AE9CH;;AAEG;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-gradient-text-testing.mjs","sources":["../../../projects/lib/gradient-text/testing/wr-gradient-text-harness.ts","../../../projects/lib/gradient-text/testing/ngwr-gradient-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 } from '@angular/cdk/testing';\n\nimport type { WrGradientTextDirection } from 'ngwr/gradient-text';\n\nimport type { WrGradientTextHarnessFilters } from './interfaces';\n\nconst GRADIENT = /^linear-gradient\\((.*)\\)$/s;\n\n/** Read one declaration out of an inline `style` attribute, or `null` when it is not there. */\nfunction inlineStyle(style: string | null, property: string): string | null {\n for (const declaration of (style ?? '').split(';')) {\n const colon = declaration.indexOf(':');\n if (colon === -1) continue;\n if (declaration.slice(0, colon).trim() !== property) continue;\n return declaration.slice(colon + 1).trim();\n }\n return null;\n}\n\n/** Split a gradient's argument list on top-level commas — a stop may be `rgb(1, 2, 3)`. */\nfunction splitStops(args: string): string[] {\n const parts: string[] = [];\n let depth = 0;\n let current = '';\n\n for (const character of args) {\n if (character === '(') depth++;\n if (character === ')') depth--;\n\n if (character === ',' && depth === 0) {\n parts.push(current.trim());\n current = '';\n } else {\n current += character;\n }\n }\n\n parts.push(current.trim());\n return parts;\n}\n\n/**\n * Test harness for `<wr-gradient-text>`.\n *\n * **The gradient string is the component.** Everything `[colors]` and `[direction]`\n * decide ends up in one inline custom property — the angle keyword, the stop list, and\n * the first colour repeated at the end, which is what makes the loop seamless instead of\n * snapping back at the wrap. {@link getColors} returns that list AS WRITTEN, wrap stop\n * included, because the duplicate is the thing worth asserting and stripping it would\n * hide the day it goes missing.\n *\n * **The three custom properties are read off the `style` ATTRIBUTE, not through\n * `getCssValue()`, and that is not a jsdom concession.** `.wr-gradient-text` declares\n * its own `--wr-gradient-text-image`, `-size` and `-duration`, spelling out the same\n * defaults the component computes — the three built-in stops with their wrap stop,\n * `300% 100%`, `8s`. So with the stylesheet loaded a computed read answers with a\n * perfectly healthy-looking value at exactly the moment the host binding is what broke,\n * and every throw below goes unreachable. What the component wrote is the only value\n * nothing else can forge.\n *\n * **Direction is two facts, not one.** The modifier class says which way the gradient\n * travels and {@link getBackgroundSize} says which axis it was stretched along, and they\n * are computed separately: a direction change that forgets the size leaves every class\n * looking right while the effect is squashed flat. Note too that `horizontal` writes NO\n * class at all — {@link getDirection} resolves the absence, which is the mapping a\n * refactor breaks most easily.\n *\n * **What the harness deliberately does not offer**, and why. There is no `isAnimating()`\n * / `isPaused()`: the sweep is a stylesheet keyframe, and a unit test loads no\n * stylesheet, so the answer would be the same constant for a working component and a\n * broken one. There is no `getBackgroundPosition()`: the swept value exists only\n * mid-animation, and the static declaration it would return corresponds to no frame the\n * user ever sees. {@link pausesOnHover} claims the CLASS and not the pause, deliberately\n * — `:hover` is a selector, and the DOM after a dispatched mouse event is identical to\n * the DOM before it. There is no `respectsReducedMotion()`: the only handling is a\n * `@media` block with no TypeScript counterpart, so nothing in the DOM changes in either\n * state. And there is nothing that reports the painted text colour: the fill comes from\n * `background-clip: text` over a `color: transparent` declared in the stylesheet, so a\n * read would describe the inherited colour and call the text readable where the browser\n * paints a gradient.\n *\n * @example\n * ```ts\n * const gradient = await loader.getHarness(WrGradientTextHarness);\n *\n * expect(await gradient.getColors()).toEqual(['#111111', '#222222', '#111111']);\n * expect(await gradient.getBackgroundSize()).toBe('300% 100%');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrGradientTextHarness extends ComponentHarness {\n static hostSelector = 'wr-gradient-text';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrGradientTextHarnessFilters = {}): HarnessPredicate<WrGradientTextHarness> {\n return new HarnessPredicate(WrGradientTextHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getText(), text))\n .addOption(\n 'direction',\n options.direction,\n async (harness, direction) => (await harness.getDirection()) === direction\n )\n .addOption('border', options.border, async (harness, border) => (await harness.hasBorderRing()) === border);\n }\n\n private readonly text = this.locatorFor('.wr-gradient-text__text');\n private readonly ring = this.locatorForOptional('.wr-gradient-text__border');\n\n /**\n * The projected text, trimmed.\n *\n * Read from the text span rather than from the host, so the answer stays correct on the\n * day the decorative ring gains content of its own — it is a sibling of this span, and\n * the host's text would include both.\n */\n async getText(): Promise<string> {\n return (await this.text()).text();\n }\n\n /**\n * Which way the gradient travels.\n *\n * `horizontal` is reported for the absence of both modifiers, because the component\n * writes no `--horizontal` class — there is nothing to read for the default, only the\n * two exceptions.\n */\n async getDirection(): Promise<WrGradientTextDirection> {\n const host = await this.host();\n if (await host.hasClass('wr-gradient-text--vertical')) return 'vertical';\n if (await host.hasClass('wr-gradient-text--diagonal')) return 'diagonal';\n return 'horizontal';\n }\n\n /**\n * The whole gradient the component computed, as written.\n *\n * Unresolved on purpose: a consumer may pass theme tokens, and resolving `var()` needs\n * the stylesheet. The stops come back spelled as they were written — a custom property\n * holds an unparsed token stream, so `#111111` stays `#111111` rather than being\n * re-serialised as `rgb()` the way it would be inside a standard `background-image`.\n * Whitespace is the exception, so prefer {@link getColors} over string-equalling the\n * whole gradient.\n */\n async getGradient(): Promise<string> {\n return this.published('--wr-gradient-text-image', 'getGradient');\n }\n\n /**\n * The gradient's colour stops, in order, INCLUDING the repeated first colour at the end.\n *\n * So the list is one longer than the `[colors]` input, and that extra entry is the\n * point: it is what lets the sweep wrap without a visible snap, and nothing else in the\n * DOM records it. An empty `[colors]` falls back to the three built-in stops, which\n * shows up here as four entries rather than one.\n *\n * Throws rather than guessing when the property holds something that is not a\n * `linear-gradient()`, because a list of fragments parsed out of the wreckage would\n * look like a colour set. A host that publishes no gradient at all throws one step\n * earlier, from {@link getGradient}.\n */\n async getColors(): Promise<string[]> {\n const gradient = await this.getGradient();\n const match = GRADIENT.exec(gradient);\n\n if (!match) {\n throw new Error(\n `WrGradientTextHarness.getColors(): \\`--wr-gradient-text-image\\` holds \"${gradient}\", which is not ` +\n 'a linear-gradient(). The component writes that property inline on the host on every render, so ' +\n 'anything else there is the bug rather than a gradient with no stops.'\n );\n }\n\n // The first argument is the angle keyword ('to right' / 'to bottom' / 'to bottom right').\n return splitStops(match[1]).slice(1);\n }\n\n /**\n * How far the gradient is stretched, as the `background-size` pair.\n *\n * The other half of direction: the gradient has to be oversized along the axis it\n * travels — `300% 100%` horizontal, `100% 300%` vertical, `300% 300%` diagonal — or\n * there is nothing outside the box to slide in. Worth asserting beside\n * {@link getDirection}, since the two are computed independently and only agree by\n * intent.\n */\n async getBackgroundSize(): Promise<string> {\n return this.published('--wr-gradient-text-size', 'getBackgroundSize');\n }\n\n /**\n * One full sweep in seconds, from `[animationSpeed]`.\n *\n * Proves the input reaches CSS at all, `numAttr` coercion included — a string attribute\n * arrives as a number and garbage falls back to the documented default. Throws when the\n * property holds something that is not a number of seconds; a host that publishes no\n * duration at all throws from the read itself.\n */\n async getAnimationDurationSeconds(): Promise<number> {\n const raw = await this.published('--wr-gradient-text-duration', 'getAnimationDurationSeconds');\n const seconds = Number.parseFloat(raw);\n\n if (Number.isNaN(seconds)) {\n throw new Error(\n `WrGradientTextHarness.getAnimationDurationSeconds(): \\`--wr-gradient-text-duration\\` holds ` +\n `\"${raw}\", which is not a number of seconds. The component writes it inline on the host from ` +\n '`[animationSpeed]`.'\n );\n }\n return seconds;\n }\n\n /**\n * Whether the decorative ring element was rendered (`showBorder`).\n *\n * Strictly more than the `wr-gradient-text--border` class says: the class is a host\n * binding and the ring is an `@if` branch in the template, two mechanisms reading one\n * input, and they can drift. This asks whether the element that paints the pill is\n * actually there.\n */\n async hasBorderRing(): Promise<boolean> {\n return (await this.ring()) !== null;\n }\n\n /**\n * Whether the ring is hidden from assistive tech, or `null` when there is no ring.\n *\n * The ring is an empty, nameless span. Paired with {@link hasBorderRing} this is the\n * component's whole a11y contract: if it ever loses `aria-hidden`, a screen reader stops\n * on an element with nothing to say. `null` rather than `false` for the absent case, so\n * \"no ring\" cannot be mistaken for \"an exposed ring\".\n */\n async isBorderDecorative(): Promise<boolean | null> {\n const ring = await this.ring();\n return ring === null ? null : (await ring.getAttribute('aria-hidden')) === 'true';\n }\n\n /**\n * Whether the sweep bounces instead of restarting (`yoyo`).\n *\n * Defaults to `true` here, unlike every other boolean on this component and unlike the\n * same-named input on `wr-shiny-text` — worth pinning in both directions. The class is\n * the only thing the `animation-direction: alternate` rule keys off.\n */\n async isYoyo(): Promise<boolean> {\n return (await this.host()).hasClass('wr-gradient-text--yoyo');\n }\n\n /**\n * Whether the opt-in reached the class the `:hover` rule needs.\n *\n * Named for the class and not for the pause on purpose: the pause itself is a CSS\n * selector match, which no unit test can observe. What this catches is the input never\n * arriving, which is the half that can go wrong in TypeScript.\n */\n async pausesOnHover(): Promise<boolean> {\n return (await this.host()).hasClass('wr-gradient-text--pause-on-hover');\n }\n\n /** One of the host's own custom properties. See the class note on why this is not `getCssValue()`. */\n private async published(property: string, method: string): Promise<string> {\n const style = await (await this.host()).getAttribute('style');\n const value = inlineStyle(style, property);\n\n if (value === null) {\n throw new Error(\n `WrGradientTextHarness.${method}(): the host publishes no \\`${property}\\` (style: \"${style ?? ''}\"). ` +\n 'The component writes all three of its custom properties inline on every render, so an absent one ' +\n 'means the host binding is gone — and the stylesheet declares the same default, so a computed read ' +\n 'would have answered as if nothing were wrong.'\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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"}
1
+ {"version":3,"file":"ngwr-gradient-text-testing.mjs","sources":["../../../projects/lib/gradient-text/testing/wr-gradient-text-harness.ts","../../../projects/lib/gradient-text/testing/ngwr-gradient-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 } from '@angular/cdk/testing';\n\nimport type { WrGradientTextDirection } from 'ngwr/gradient-text';\n\nimport type { WrGradientTextHarnessFilters } from './interfaces';\n\nconst GRADIENT = /^linear-gradient\\((.*)\\)$/s;\n\n/** Read one declaration out of an inline `style` attribute, or `null` when it is not there. */\nfunction inlineStyle(style: string | null, property: string): string | null {\n for (const declaration of (style ?? '').split(';')) {\n const colon = declaration.indexOf(':');\n if (colon === -1) continue;\n if (declaration.slice(0, colon).trim() !== property) continue;\n return declaration.slice(colon + 1).trim();\n }\n return null;\n}\n\n/** Split a gradient's argument list on top-level commas — a stop may be `rgb(1, 2, 3)`. */\nfunction splitStops(args: string): string[] {\n const parts: string[] = [];\n let depth = 0;\n let current = '';\n\n for (const character of args) {\n if (character === '(') depth++;\n if (character === ')') depth--;\n\n if (character === ',' && depth === 0) {\n parts.push(current.trim());\n current = '';\n } else {\n current += character;\n }\n }\n\n parts.push(current.trim());\n return parts;\n}\n\n/**\n * Test harness for `<wr-gradient-text>`.\n *\n * **The gradient string is the component.** Everything `[colors]` and `[direction]`\n * decide ends up in one inline custom property — the angle keyword, the stop list, and\n * the first colour repeated at the end, which is what makes the loop seamless instead of\n * snapping back at the wrap. {@link getColors} returns that list AS WRITTEN, wrap stop\n * included, because the duplicate is the thing worth asserting and stripping it would\n * hide the day it goes missing.\n *\n * **The three custom properties are read off the `style` ATTRIBUTE, not through\n * `getCssValue()`, and that is not a jsdom concession.** `.wr-gradient-text` declares\n * its own `--wr-gradient-text-image`, `-size` and `-duration`, spelling out the same\n * defaults the component computes — the three built-in stops with their wrap stop,\n * `300% 100%`, `8s`. So with the stylesheet loaded a computed read answers with a\n * perfectly healthy-looking value at exactly the moment the host binding is what broke,\n * and every throw below goes unreachable. What the component wrote is the only value\n * nothing else can forge.\n *\n * **Direction is two facts, not one.** The modifier class says which way the gradient\n * travels and {@link getBackgroundSize} says which axis it was stretched along, and they\n * are computed separately: a direction change that forgets the size leaves every class\n * looking right while the effect is squashed flat. Note too that `horizontal` writes NO\n * class at all — {@link getDirection} resolves the absence, which is the mapping a\n * refactor breaks most easily.\n *\n * **What the harness deliberately does not offer**, and why. There is no `isAnimating()`\n * / `isPaused()`: the sweep is a stylesheet keyframe, and a unit test loads no\n * stylesheet, so the answer would be the same constant for a working component and a\n * broken one. There is no `getBackgroundPosition()`: the swept value exists only\n * mid-animation, and the static declaration it would return corresponds to no frame the\n * user ever sees. {@link pausesOnHover} claims the CLASS and not the pause, deliberately\n * — `:hover` is a selector, and the DOM after a dispatched mouse event is identical to\n * the DOM before it. There is no `respectsReducedMotion()`: the only handling is a\n * `@media` block with no TypeScript counterpart, so nothing in the DOM changes in either\n * state. And there is nothing that reports the painted text colour: the fill comes from\n * `background-clip: text` over a `color: transparent` declared in the stylesheet, so a\n * read would describe the inherited colour and call the text readable where the browser\n * paints a gradient.\n *\n * @example\n * ```ts\n * const gradient = await loader.getHarness(WrGradientTextHarness);\n *\n * expect(await gradient.getColors()).toEqual(['#111111', '#222222', '#111111']);\n * expect(await gradient.getBackgroundSize()).toBe('300% 100%');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrGradientTextHarness extends ComponentHarness {\n static hostSelector = 'wr-gradient-text';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrGradientTextHarnessFilters = {}): HarnessPredicate<WrGradientTextHarness> {\n return new HarnessPredicate(WrGradientTextHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getText(), text))\n .addOption(\n 'direction',\n options.direction,\n async (harness, direction) => (await harness.getDirection()) === direction\n )\n .addOption('border', options.border, async (harness, border) => (await harness.hasBorderRing()) === border);\n }\n\n private readonly text = this.locatorFor('.wr-gradient-text__text');\n private readonly ring = this.locatorForOptional('.wr-gradient-text__border');\n\n /**\n * The projected text, trimmed.\n *\n * Read from the text span rather than from the host, so the answer stays correct on the\n * day the decorative ring gains content of its own — it is a sibling of this span, and\n * the host's text would include both.\n */\n async getText(): Promise<string> {\n return (await this.text()).text();\n }\n\n /**\n * Which way the gradient travels.\n *\n * `horizontal` is reported for the absence of both modifiers, because the component\n * writes no `--horizontal` class — there is nothing to read for the default, only the\n * two exceptions.\n */\n async getDirection(): Promise<WrGradientTextDirection> {\n const host = await this.host();\n if (await host.hasClass('wr-gradient-text--vertical')) return 'vertical';\n if (await host.hasClass('wr-gradient-text--diagonal')) return 'diagonal';\n return 'horizontal';\n }\n\n /**\n * The whole gradient the component computed, as written.\n *\n * Unresolved on purpose: a consumer may pass theme tokens, and resolving `var()` needs\n * the stylesheet. The stops come back spelled as they were written — a custom property\n * holds an unparsed token stream, so `#111111` stays `#111111` rather than being\n * re-serialised as `rgb()` the way it would be inside a standard `background-image`.\n * Whitespace is the exception, so prefer {@link getColors} over string-equalling the\n * whole gradient.\n */\n async getGradient(): Promise<string> {\n return this.published('--wr-gradient-text-image', 'getGradient');\n }\n\n /**\n * The gradient's colour stops, in order, INCLUDING the repeated first colour at the end.\n *\n * So the list is one longer than the `[colors]` input, and that extra entry is the\n * point: it is what lets the sweep wrap without a visible snap, and nothing else in the\n * DOM records it. An empty `[colors]` falls back to the three built-in stops, which\n * shows up here as four entries rather than one.\n *\n * Throws rather than guessing when the property holds something that is not a\n * `linear-gradient()`, because a list of fragments parsed out of the wreckage would\n * look like a colour set. A host that publishes no gradient at all throws one step\n * earlier, from {@link getGradient}.\n */\n async getColors(): Promise<string[]> {\n const gradient = await this.getGradient();\n const match = GRADIENT.exec(gradient);\n\n if (!match) {\n throw new Error(\n `WrGradientTextHarness.getColors(): \\`--wr-gradient-text-image\\` holds \"${gradient}\", which is not ` +\n 'a linear-gradient(). The component writes that property inline on the host on every render, so ' +\n 'anything else there is the bug rather than a gradient with no stops.'\n );\n }\n\n // The first argument is the angle keyword ('to right' / 'to bottom' / 'to bottom right').\n return splitStops(match[1]).slice(1);\n }\n\n /**\n * How far the gradient is stretched, as the `background-size` pair.\n *\n * The other half of direction: the gradient has to be oversized along the axis it\n * travels — `300% 100%` horizontal, `100% 300%` vertical, `300% 300%` diagonal — or\n * there is nothing outside the box to slide in. Worth asserting beside\n * {@link getDirection}, since the two are computed independently and only agree by\n * intent.\n */\n async getBackgroundSize(): Promise<string> {\n return this.published('--wr-gradient-text-size', 'getBackgroundSize');\n }\n\n /**\n * One full sweep in seconds, from `[animationSpeed]`.\n *\n * Proves the input reaches CSS at all, `numAttr` coercion included — a string attribute\n * arrives as a number and garbage falls back to the documented default. Throws when the\n * property holds something that is not a number of seconds; a host that publishes no\n * duration at all throws from the read itself.\n */\n async getAnimationDurationSeconds(): Promise<number> {\n const raw = await this.published('--wr-gradient-text-duration', 'getAnimationDurationSeconds');\n const seconds = Number.parseFloat(raw);\n\n if (Number.isNaN(seconds)) {\n throw new Error(\n `WrGradientTextHarness.getAnimationDurationSeconds(): \\`--wr-gradient-text-duration\\` holds ` +\n `\"${raw}\", which is not a number of seconds. The component writes it inline on the host from ` +\n '`[animationSpeed]`.'\n );\n }\n return seconds;\n }\n\n /**\n * Whether the decorative ring element was rendered (`showBorder`).\n *\n * Strictly more than the `wr-gradient-text--border` class says: the class is a host\n * binding and the ring is an `@if` branch in the template, two mechanisms reading one\n * input, and they can drift. This asks whether the element that paints the pill is\n * actually there.\n */\n async hasBorderRing(): Promise<boolean> {\n return (await this.ring()) !== null;\n }\n\n /**\n * Whether the ring is hidden from assistive tech, or `null` when there is no ring.\n *\n * The ring is an empty, nameless span. Paired with {@link hasBorderRing} this is the\n * component's whole a11y contract: if it ever loses `aria-hidden`, a screen reader stops\n * on an element with nothing to say. `null` rather than `false` for the absent case, so\n * \"no ring\" cannot be mistaken for \"an exposed ring\".\n */\n async isBorderDecorative(): Promise<boolean | null> {\n const ring = await this.ring();\n return ring === null ? null : (await ring.getAttribute('aria-hidden')) === 'true';\n }\n\n /**\n * Whether the sweep bounces instead of restarting (`yoyo`).\n *\n * Defaults to `true` here, unlike every other boolean on this component and unlike the\n * same-named input on `wr-shiny-text` — worth pinning in both directions. The class is\n * the only thing the `animation-direction: alternate` rule keys off.\n */\n async isYoyo(): Promise<boolean> {\n return (await this.host()).hasClass('wr-gradient-text--yoyo');\n }\n\n /**\n * Whether the opt-in reached the class the `:hover` rule needs.\n *\n * Named for the class and not for the pause on purpose: the pause itself is a CSS\n * selector match, which no unit test can observe. What this catches is the input never\n * arriving, which is the half that can go wrong in TypeScript.\n */\n async pausesOnHover(): Promise<boolean> {\n return (await this.host()).hasClass('wr-gradient-text--pause-on-hover');\n }\n\n /** One of the host's own custom properties. See the class note on why this is not `getCssValue()`. */\n private async published(property: string, method: string): Promise<string> {\n const style = await (await this.host()).getAttribute('style');\n const value = inlineStyle(style, property);\n\n if (value === null) {\n throw new Error(\n `WrGradientTextHarness.${method}(): the host publishes no \\`${property}\\` (style: \"${style ?? ''}\"). ` +\n 'The component writes all three of its custom properties inline on every render, so an absent one ' +\n 'means the host binding is gone — and the stylesheet declares the same default, so a computed read ' +\n 'would have answered as if nothing were wrong.'\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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"}
@@ -90,10 +90,10 @@ class WrGradientText {
90
90
  return 'to right';
91
91
  }
92
92
  }
93
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrGradientText, deps: [], target: i0.ɵɵFactoryTarget.Component });
94
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: WrGradientText, isStandalone: true, selector: "wr-gradient-text", inputs: { colors: { classPropertyName: "colors", publicName: "colors", isSignal: true, isRequired: false, transformFunction: null }, animationSpeed: { classPropertyName: "animationSpeed", publicName: "animationSpeed", isSignal: true, isRequired: false, transformFunction: null }, showBorder: { classPropertyName: "showBorder", publicName: "showBorder", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, pauseOnHover: { classPropertyName: "pauseOnHover", publicName: "pauseOnHover", isSignal: true, isRequired: false, transformFunction: null }, yoyo: { classPropertyName: "yoyo", publicName: "yoyo", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.wr-gradient-text--border": "showBorder()", "class.wr-gradient-text--pause-on-hover": "pauseOnHover()", "class.wr-gradient-text--yoyo": "yoyo()", "class.wr-gradient-text--vertical": "direction() === 'vertical'", "class.wr-gradient-text--diagonal": "direction() === 'diagonal'", "style.--wr-gradient-text-image": "gradient()", "style.--wr-gradient-text-size": "size()", "style.--wr-gradient-text-duration": "animationSpeed() + 's'" }, classAttribute: "wr-gradient-text" }, ngImport: i0, template: "@if (showBorder()) {\n <span class=\"wr-gradient-text__border\" aria-hidden=\"true\"></span>\n}\n<span class=\"wr-gradient-text__text\">\n <ng-content />\n</span>\n", styles: [".wr-gradient-text{--wr-gradient-text-image: linear-gradient(to right, #5227ff, #ff9ffc, #b497cf, #5227ff);--wr-gradient-text-size: 300% 100%;--wr-gradient-text-duration: 8s;position:relative;display:inline-flex;align-items:center;justify-content:center;margin:0;font-weight:500;line-height:1.25;overflow:hidden}.wr-gradient-text--border{padding:.35rem .75rem;border-radius:1.25rem}.wr-gradient-text__text{position:relative;z-index:2;display:inline-block;background-image:var(--wr-gradient-text-image);background-size:var(--wr-gradient-text-size);background-position:0% 50%;-webkit-background-clip:text;background-clip:text;color:transparent;animation:wr-gradient-text-horizontal var(--wr-gradient-text-duration) linear infinite}.wr-gradient-text__border{position:absolute;inset:0;border-radius:inherit;background-image:var(--wr-gradient-text-image);background-size:var(--wr-gradient-text-size);background-position:0% 50%;animation:wr-gradient-text-horizontal var(--wr-gradient-text-duration) linear infinite;z-index:0;pointer-events:none}.wr-gradient-text__border:before{content:\"\";position:absolute;inset:1px;border-radius:inherit;background-color:var(--wr-color-surface, #fff);z-index:1}.wr-gradient-text--vertical .wr-gradient-text__text,.wr-gradient-text--vertical .wr-gradient-text__border{background-position:50% 0%;animation-name:wr-gradient-text-vertical}.wr-gradient-text--diagonal .wr-gradient-text__text,.wr-gradient-text--diagonal .wr-gradient-text__border{background-position:0% 50%;animation-name:wr-gradient-text-horizontal}.wr-gradient-text--yoyo .wr-gradient-text__text,.wr-gradient-text--yoyo .wr-gradient-text__border{animation-direction:alternate}.wr-gradient-text--pause-on-hover:hover .wr-gradient-text__text,.wr-gradient-text--pause-on-hover:hover .wr-gradient-text__border{animation-play-state:paused}@keyframes wr-gradient-text-horizontal{0%{background-position:0% 50%}to{background-position:100% 50%}}@keyframes wr-gradient-text-vertical{0%{background-position:50% 0%}to{background-position:50% 100%}}@media(prefers-reduced-motion:reduce){.wr-gradient-text__text,.wr-gradient-text__border{animation:none!important}}\n"], encapsulation: i0.ViewEncapsulation.None });
93
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrGradientText, deps: [], target: i0.ɵɵFactoryTarget.Component });
94
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: WrGradientText, isStandalone: true, selector: "wr-gradient-text", inputs: { colors: { classPropertyName: "colors", publicName: "colors", isSignal: true, isRequired: false, transformFunction: null }, animationSpeed: { classPropertyName: "animationSpeed", publicName: "animationSpeed", isSignal: true, isRequired: false, transformFunction: null }, showBorder: { classPropertyName: "showBorder", publicName: "showBorder", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, pauseOnHover: { classPropertyName: "pauseOnHover", publicName: "pauseOnHover", isSignal: true, isRequired: false, transformFunction: null }, yoyo: { classPropertyName: "yoyo", publicName: "yoyo", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.wr-gradient-text--border": "showBorder()", "class.wr-gradient-text--pause-on-hover": "pauseOnHover()", "class.wr-gradient-text--yoyo": "yoyo()", "class.wr-gradient-text--vertical": "direction() === 'vertical'", "class.wr-gradient-text--diagonal": "direction() === 'diagonal'", "style.--wr-gradient-text-image": "gradient()", "style.--wr-gradient-text-size": "size()", "style.--wr-gradient-text-duration": "animationSpeed() + 's'" }, classAttribute: "wr-gradient-text" }, ngImport: i0, template: "@if (showBorder()) {\n <span class=\"wr-gradient-text__border\" aria-hidden=\"true\"></span>\n}\n<span class=\"wr-gradient-text__text\">\n <ng-content />\n</span>\n", styles: [".wr-gradient-text{--wr-gradient-text-image: linear-gradient(to right, #5227ff, #ff9ffc, #b497cf, #5227ff);--wr-gradient-text-size: 300% 100%;--wr-gradient-text-duration: 8s;position:relative;display:inline-flex;align-items:center;justify-content:center;margin:0;font-weight:500;line-height:1.25;overflow:hidden}.wr-gradient-text--border{padding:.35rem .75rem;border-radius:1.25rem}.wr-gradient-text__text{position:relative;z-index:2;display:inline-block;background-image:var(--wr-gradient-text-image);background-size:var(--wr-gradient-text-size);background-position:0% 50%;-webkit-background-clip:text;background-clip:text;color:transparent;animation:wr-gradient-text-horizontal var(--wr-gradient-text-duration) linear infinite}.wr-gradient-text__border{position:absolute;inset:0;border-radius:inherit;background-image:var(--wr-gradient-text-image);background-size:var(--wr-gradient-text-size);background-position:0% 50%;animation:wr-gradient-text-horizontal var(--wr-gradient-text-duration) linear infinite;z-index:0;pointer-events:none}.wr-gradient-text__border:before{content:\"\";position:absolute;inset:1px;border-radius:inherit;background-color:var(--wr-color-surface, #fff);z-index:1}.wr-gradient-text--vertical .wr-gradient-text__text,.wr-gradient-text--vertical .wr-gradient-text__border{background-position:50% 0%;animation-name:wr-gradient-text-vertical}.wr-gradient-text--diagonal .wr-gradient-text__text,.wr-gradient-text--diagonal .wr-gradient-text__border{background-position:0% 50%;animation-name:wr-gradient-text-horizontal}.wr-gradient-text--yoyo .wr-gradient-text__text,.wr-gradient-text--yoyo .wr-gradient-text__border{animation-direction:alternate}.wr-gradient-text--pause-on-hover:hover .wr-gradient-text__text,.wr-gradient-text--pause-on-hover:hover .wr-gradient-text__border{animation-play-state:paused}@keyframes wr-gradient-text-horizontal{0%{background-position:0% 50%}to{background-position:100% 50%}}@keyframes wr-gradient-text-vertical{0%{background-position:50% 0%}to{background-position:50% 100%}}@media(prefers-reduced-motion:reduce){.wr-gradient-text__text,.wr-gradient-text__border{animation:none!important}}\n"], encapsulation: i0.ViewEncapsulation.None });
95
95
  }
96
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrGradientText, decorators: [{
96
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrGradientText, decorators: [{
97
97
  type: Component,
98
98
  args: [{ selector: 'wr-gradient-text', encapsulation: ViewEncapsulation.None, host: {
99
99
  class: 'wr-gradient-text',
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-gradient-text.mjs","sources":["../../../projects/lib/gradient-text/gradient-text.ts","../../../projects/lib/gradient-text/gradient-text.html","../../../projects/lib/gradient-text/ngwr-gradient-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 GradientText effect by David Haz / reactbits.dev.\n * Original: https://www.reactbits.dev/text-animations/gradient-text\n *\n * The reactbits version uses `motion/react` to drive `background-position`\n * via rAF. This port is pure CSS — same effect via keyframes on\n * `background-position`, parameterised by inputs.\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\nimport { isSafeCssValue, numAttr } from 'ngwr/utils';\n\nimport type { WrGradientTextDirection } from './interfaces';\n\nconst DEFAULT_COLORS: readonly string[] = ['#5227FF', '#FF9FFC', '#B497CF'];\n\n/**\n * Animated multi-stop gradient text. The gradient slides across the text\n * via `background-clip: text`. Optional `[showBorder]` wraps the text in\n * a dark pill with the same animated gradient as a border ring.\n *\n * @example\n * ```html\n * <wr-gradient-text>Hello, ngwr!</wr-gradient-text>\n * <wr-gradient-text\n * [colors]=\"['#5227FF', '#FF9FFC', '#B497CF']\"\n * direction=\"diagonal\"\n * [animationSpeed]=\"6\"\n * [showBorder]=\"true\"\n * >\n * Premium feature\n * </wr-gradient-text>\n * ```\n *\n * @see https://www.reactbits.dev/text-animations/gradient-text\n */\n@Component({\n selector: 'wr-gradient-text',\n templateUrl: './gradient-text.html',\n styleUrl: './gradient-text.scss',\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-gradient-text',\n '[class.wr-gradient-text--border]': 'showBorder()',\n '[class.wr-gradient-text--pause-on-hover]': 'pauseOnHover()',\n '[class.wr-gradient-text--yoyo]': 'yoyo()',\n '[class.wr-gradient-text--vertical]': \"direction() === 'vertical'\",\n '[class.wr-gradient-text--diagonal]': \"direction() === 'diagonal'\",\n '[style.--wr-gradient-text-image]': 'gradient()',\n '[style.--wr-gradient-text-size]': 'size()',\n '[style.--wr-gradient-text-duration]': \"animationSpeed() + 's'\",\n },\n})\nexport class WrGradientText {\n /** Gradient stops. @default ['#5227FF', '#FF9FFC', '#B497CF'] */\n readonly colors = input<readonly string[]>(DEFAULT_COLORS);\n\n /** Seconds per full sweep (or per half if `[yoyo]` is on). @default 8 */\n readonly animationSpeed = input(8, { transform: numAttr(8) });\n\n /** Wrap the text in a dark pill with the gradient as a border. @default false */\n readonly showBorder = input(false, { transform: coerceBooleanProperty });\n\n /** Gradient slide direction. @default 'horizontal' */\n readonly direction = input<WrGradientTextDirection>('horizontal');\n\n /** Pause the animation while hovered. @default false */\n readonly pauseOnHover = input(false, { transform: coerceBooleanProperty });\n\n /** Bounce back-and-forth instead of restarting. @default true */\n readonly yoyo = input(true, { transform: coerceBooleanProperty });\n\n protected readonly gradient = computed(() => {\n // The stops are JOINED into the gradient below and published as\n // `--wr-gradient-text-image`, which the stylesheet substitutes into\n // `background-image`. An entry that closes the library's own paren escapes\n // into a sibling background layer, so one unsafe stop discards the palette\n // rather than being repaired in place — a half-validated gradient is worse\n // to look at than the default one.\n const raw = this.colors();\n const cols = raw.length > 0 && raw.every(color => isSafeCssValue(color)) ? raw : DEFAULT_COLORS;\n // Duplicate the first colour at the end for seamless looping.\n const stops = [...cols, cols[0]].join(', ');\n const angle = this.directionAngle();\n return `linear-gradient(${angle}, ${stops})`;\n });\n\n protected readonly size = computed(() => {\n switch (this.direction()) {\n case 'vertical':\n return '100% 300%';\n case 'diagonal':\n return '300% 300%';\n case 'horizontal':\n default:\n return '300% 100%';\n }\n });\n\n private directionAngle(): string {\n switch (this.direction()) {\n case 'vertical':\n return 'to bottom';\n case 'diagonal':\n return 'to bottom right';\n case 'horizontal':\n default:\n return 'to right';\n }\n }\n}\n\nexport type { WrGradientTextDirection } from './interfaces';\n","@if (showBorder()) {\n <span class=\"wr-gradient-text__border\" aria-hidden=\"true\"></span>\n}\n<span class=\"wr-gradient-text__text\">\n <ng-content />\n</span>\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-gradient-text.mjs","sources":["../../../projects/lib/gradient-text/gradient-text.ts","../../../projects/lib/gradient-text/gradient-text.html","../../../projects/lib/gradient-text/ngwr-gradient-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 GradientText effect by David Haz / reactbits.dev.\n * Original: https://www.reactbits.dev/text-animations/gradient-text\n *\n * The reactbits version uses `motion/react` to drive `background-position`\n * via rAF. This port is pure CSS — same effect via keyframes on\n * `background-position`, parameterised by inputs.\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\nimport { isSafeCssValue, numAttr } from 'ngwr/utils';\n\nimport type { WrGradientTextDirection } from './interfaces';\n\nconst DEFAULT_COLORS: readonly string[] = ['#5227FF', '#FF9FFC', '#B497CF'];\n\n/**\n * Animated multi-stop gradient text. The gradient slides across the text\n * via `background-clip: text`. Optional `[showBorder]` wraps the text in\n * a dark pill with the same animated gradient as a border ring.\n *\n * @example\n * ```html\n * <wr-gradient-text>Hello, ngwr!</wr-gradient-text>\n * <wr-gradient-text\n * [colors]=\"['#5227FF', '#FF9FFC', '#B497CF']\"\n * direction=\"diagonal\"\n * [animationSpeed]=\"6\"\n * [showBorder]=\"true\"\n * >\n * Premium feature\n * </wr-gradient-text>\n * ```\n *\n * @see https://www.reactbits.dev/text-animations/gradient-text\n */\n@Component({\n selector: 'wr-gradient-text',\n templateUrl: './gradient-text.html',\n styleUrl: './gradient-text.scss',\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-gradient-text',\n '[class.wr-gradient-text--border]': 'showBorder()',\n '[class.wr-gradient-text--pause-on-hover]': 'pauseOnHover()',\n '[class.wr-gradient-text--yoyo]': 'yoyo()',\n '[class.wr-gradient-text--vertical]': \"direction() === 'vertical'\",\n '[class.wr-gradient-text--diagonal]': \"direction() === 'diagonal'\",\n '[style.--wr-gradient-text-image]': 'gradient()',\n '[style.--wr-gradient-text-size]': 'size()',\n '[style.--wr-gradient-text-duration]': \"animationSpeed() + 's'\",\n },\n})\nexport class WrGradientText {\n /** Gradient stops. @default ['#5227FF', '#FF9FFC', '#B497CF'] */\n readonly colors = input<readonly string[]>(DEFAULT_COLORS);\n\n /** Seconds per full sweep (or per half if `[yoyo]` is on). @default 8 */\n readonly animationSpeed = input(8, { transform: numAttr(8) });\n\n /** Wrap the text in a dark pill with the gradient as a border. @default false */\n readonly showBorder = input(false, { transform: coerceBooleanProperty });\n\n /** Gradient slide direction. @default 'horizontal' */\n readonly direction = input<WrGradientTextDirection>('horizontal');\n\n /** Pause the animation while hovered. @default false */\n readonly pauseOnHover = input(false, { transform: coerceBooleanProperty });\n\n /** Bounce back-and-forth instead of restarting. @default true */\n readonly yoyo = input(true, { transform: coerceBooleanProperty });\n\n protected readonly gradient = computed(() => {\n // The stops are JOINED into the gradient below and published as\n // `--wr-gradient-text-image`, which the stylesheet substitutes into\n // `background-image`. An entry that closes the library's own paren escapes\n // into a sibling background layer, so one unsafe stop discards the palette\n // rather than being repaired in place — a half-validated gradient is worse\n // to look at than the default one.\n const raw = this.colors();\n const cols = raw.length > 0 && raw.every(color => isSafeCssValue(color)) ? raw : DEFAULT_COLORS;\n // Duplicate the first colour at the end for seamless looping.\n const stops = [...cols, cols[0]].join(', ');\n const angle = this.directionAngle();\n return `linear-gradient(${angle}, ${stops})`;\n });\n\n protected readonly size = computed(() => {\n switch (this.direction()) {\n case 'vertical':\n return '100% 300%';\n case 'diagonal':\n return '300% 300%';\n case 'horizontal':\n default:\n return '300% 100%';\n }\n });\n\n private directionAngle(): string {\n switch (this.direction()) {\n case 'vertical':\n return 'to bottom';\n case 'diagonal':\n return 'to bottom right';\n case 'horizontal':\n default:\n return 'to right';\n }\n }\n}\n\nexport type { WrGradientTextDirection } from './interfaces';\n","@if (showBorder()) {\n <span class=\"wr-gradient-text__border\" aria-hidden=\"true\"></span>\n}\n<span class=\"wr-gradient-text__text\">\n <ng-content />\n</span>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;AAAA;;;;;;;;;;;;AAYG;AASH,MAAM,cAAc,GAAsB,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC;AAE3E;;;;;;;;;;;;;;;;;;;AAmBG;MAkBU,cAAc,CAAA;;IAEhB,MAAM,GAAG,KAAK,CAAoB,cAAc;+EAAC;;AAGjD,IAAA,cAAc,GAAG,KAAK,CAAC,CAAC,EAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,gBAAA,EAAA,8BAAA,EAAA,CAAA,EAAI,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,EAAA,CAAG;;IAGpD,UAAU,GAAG,KAAK,CAAC,KAAK,kFAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;;IAG/D,SAAS,GAAG,KAAK,CAA0B,YAAY;kFAAC;;IAGxD,YAAY,GAAG,KAAK,CAAC,KAAK,oFAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;;IAGjE,IAAI,GAAG,KAAK,CAAC,IAAI,4EAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;AAE9C,IAAA,QAAQ,GAAG,QAAQ,CAAC,MAAK;;;;;;;AAO1C,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE;AACzB,QAAA,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,cAAc,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,GAAG,cAAc;;AAE/F,QAAA,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;AAC3C,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,EAAE;AACnC,QAAA,OAAO,CAAA,gBAAA,EAAmB,KAAK,CAAA,EAAA,EAAK,KAAK,GAAG;IAC9C,CAAC;iFAAC;AAEiB,IAAA,IAAI,GAAG,QAAQ,CAAC,MAAK;AACtC,QAAA,QAAQ,IAAI,CAAC,SAAS,EAAE;AACtB,YAAA,KAAK,UAAU;AACb,gBAAA,OAAO,WAAW;AACpB,YAAA,KAAK,UAAU;AACb,gBAAA,OAAO,WAAW;AACpB,YAAA,KAAK,YAAY;AACjB,YAAA;AACE,gBAAA,OAAO,WAAW;;IAExB,CAAC;6EAAC;IAEM,cAAc,GAAA;AACpB,QAAA,QAAQ,IAAI,CAAC,SAAS,EAAE;AACtB,YAAA,KAAK,UAAU;AACb,gBAAA,OAAO,WAAW;AACpB,YAAA,KAAK,UAAU;AACb,gBAAA,OAAO,iBAAiB;AAC1B,YAAA,KAAK,YAAY;AACjB,YAAA;AACE,gBAAA,OAAO,UAAU;;IAEvB;uGAxDW,cAAc,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAd,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,cAAc,u2CC5D3B,wKAMA,EAAA,MAAA,EAAA,CAAA,mmEAAA,CAAA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FDsDa,cAAc,EAAA,UAAA,EAAA,CAAA;kBAjB1B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,kBAAkB,EAAA,aAAA,EAGb,iBAAiB,CAAC,IAAI,EAAA,IAAA,EAC/B;AACJ,wBAAA,KAAK,EAAE,kBAAkB;AACzB,wBAAA,kCAAkC,EAAE,cAAc;AAClD,wBAAA,0CAA0C,EAAE,gBAAgB;AAC5D,wBAAA,gCAAgC,EAAE,QAAQ;AAC1C,wBAAA,oCAAoC,EAAE,4BAA4B;AAClE,wBAAA,oCAAoC,EAAE,4BAA4B;AAClE,wBAAA,kCAAkC,EAAE,YAAY;AAChD,wBAAA,iCAAiC,EAAE,QAAQ;AAC3C,wBAAA,qCAAqC,EAAE,wBAAwB;AAChE,qBAAA,EAAA,QAAA,EAAA,wKAAA,EAAA,MAAA,EAAA,CAAA,mmEAAA,CAAA,EAAA;;;AE1DH;;AAEG;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-graph-testing.mjs","sources":["../../../projects/lib/graph/testing/wr-graph-node-harness.ts","../../../projects/lib/graph/testing/wr-graph-harness.ts","../../../projects/lib/graph/testing/ngwr-graph-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, type ComponentHarnessConstructor, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrGraphNodeHarnessFilters } from './interfaces';\n\n/** The visually hidden relation sentence the graph writes into every node that has an edge. */\nconst SR_ONLY = '.wr-graph__sr-only';\n\n/**\n * The node's OWN sentence. Anchored with `:scope >` because the graph writes it as a\n * direct child, after whatever the template drew — so a `<wr-graph>` nested inside a\n * node template would otherwise hand its first node's sentence to the outer node.\n */\nconst RELATION = `:scope > ${SR_ONLY}`;\n\n/**\n * What {@link WrGraphNodeHarness.getText} leaves out: the node's own sentence, and any\n * `<wr-graph>` a template drew inside the node. That inner graph is a graph of its own —\n * its cards are its nodes, read through its own harness — so counting its text as the\n * outer node's would let a RegExp label match the outer graph by a node it does not own.\n */\nconst NOT_DRAWN_BY_NODE = `${SR_ONLY}, wr-graph`;\n\n/**\n * Test harness for one node of a `<wr-graph>` — a `role=\"listitem\"` block.\n *\n * A node answers two questions, and they are kept apart on purpose. {@link getText} is\n * what is DRAWN: the default card, or whatever a `wrGraphNode` template rendered.\n * {@link getRelationText} is what is ANNOUNCED about its place in the graph: the\n * lines are `aria-hidden`, so the visually hidden sentence naming the parents and the\n * children is the only form the structure takes for a screen reader. A spec that read\n * the node's whole text would get both glued together, and would keep passing on a\n * graph whose relation sentences named the wrong neighbours.\n *\n * There is no `getId()`. The component writes a node's id nowhere in the DOM — only\n * an edge carries ids, on its `data-from` / `data-to` — so a node is addressed by what\n * it draws, and {@link WrGraphHarness.getEdges} is where ids are read.\n *\n * @example\n * ```ts\n * const birch = await graph.getNode({ label: 'Birch' });\n *\n * expect(await birch.getText()).toBe('Birch');\n * expect(await birch.getRelationText()).toBe('Parents: Atlas. Children: Dune.');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrGraphNodeHarness extends ComponentHarness {\n static hostSelector = '.wr-graph__node';\n\n /**\n * Build a predicate that narrows the query.\n *\n * Typed on `this`, so the predicate is built for whichever node harness it is called\n * on — {@link WrGraphHarness.getNodes} narrows its own, direct-child query through\n * the same options.\n */\n static with<T extends WrGraphNodeHarness>(\n this: ComponentHarnessConstructor<T>,\n options: WrGraphNodeHarnessFilters = {}\n ): HarnessPredicate<T> {\n return new HarnessPredicate(this, options).addOption('label', options.label, (harness, label) =>\n HarnessPredicate.stringMatches(harness.getText(), label)\n );\n }\n\n /**\n * The text the node DRAWS, trimmed, with the relation sentence left out.\n *\n * The sentence is a real text node inside the block — visually hidden, not removed —\n * so a plain text read would end in `Parents: Atlas. Children: Dune.`. It is excluded\n * rather than subtracted.\n *\n * A `<wr-graph>` a template nests inside the node is left out whole, cards and\n * sentences alike: it is a graph of its own, and its nodes answer to its own harness.\n * So a node drawing `Harbor` above an inner graph of `Kestrel` and `Lark` reads\n * `Harbor`, and a `/Lark/` label does not match it.\n */\n async getText(): Promise<string> {\n return (await this.host()).text({ exclude: NOT_DRAWN_BY_NODE });\n }\n\n /**\n * The sentence a screen reader hears about the node's place in the graph — its\n * parents, then its children, as the catalog words them — or `null` for a node with\n * no edges, which the graph gives no sentence at all.\n *\n * `null` rather than `''`: the component never writes an empty sentence, so an empty\n * string could only mean the element exists and says nothing, which is a different\n * failure from a node that has nothing to say.\n */\n async getRelationText(): Promise<string | null> {\n const relation = await this.locatorForOptional(RELATION)();\n return relation ? relation.text() : null;\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrGraphHarnessFilters, WrGraphNodeHarnessFilters } from './interfaces';\nimport { WrGraphNodeHarness } from './wr-graph-node-harness';\n\n/**\n * Every part of the graph is reached down one fixed chain of direct children. A node\n * template may draw anything, including another `<wr-graph>`, and a descendant query\n * from the outer host would count the inner graph's nodes and edges as its own.\n */\nconst VIEWPORT = ':scope > .wr-graph__viewport';\nconst STAGE = `${VIEWPORT} > .wr-graph__stage`;\nconst EDGE = `${STAGE} > .wr-graph__edges > .wr-graph__edge`;\nconst NODE = `${STAGE} > .wr-graph__nodes > .wr-graph__node`;\n\n/**\n * The same node harness, anchored to one graph. Not exported: queried from any other\n * root the `:scope` would be the wrong element, so a consumer loads\n * {@link WrGraphNodeHarness} and this class only ever comes back from\n * {@link WrGraphHarness.getNodes}.\n */\nclass WrGraphOwnNodeHarness extends WrGraphNodeHarness {\n static override hostSelector = NODE;\n}\n\n/**\n * The filters as a failure message should show them. Not `JSON.stringify`: it prints a\n * RegExp as `{}`, so a caller who had already passed one was shown nothing and then told\n * to use a RegExp. A RegExp reads as its literal, `/source/flags`; everything else as JSON.\n */\nfunction describeFilters(filters: WrGraphNodeHarnessFilters): string {\n const entries: [string, unknown][] = Object.entries(filters);\n const shown = entries\n .filter(([, value]) => value !== undefined)\n .map(([key, value]) => `${key}: ${value instanceof RegExp ? String(value) : JSON.stringify(value)}`);\n return shown.length ? `{ ${shown.join(', ')} }` : '{}';\n}\n\n/**\n * Test harness for `<wr-graph>` — blocks joined by parent-child lines, laid out in\n * layers, display only.\n *\n * The graph is read the way assistive technology reads it: the viewport's name, the\n * nodes as a list in READING order (layer, then inline position — which is DOM order),\n * each node's drawn text and its relation sentence, and the edges as the `from` / `to`\n * ids the component writes on each line. Those are what break when the data wiring\n * does.\n *\n * Three methods are deliberately absent, and each would answer the same for a working\n * graph and a broken one:\n *\n * - **No edge path `d`.** A path is a rendering detail: rounded to two decimals, mirrored\n * under RTL, and moved by every node size and gap. The same rule as\n * `WrLineChartHarness`.\n * - **No node positions.** jsdom lays nothing out, so every measured box is 0×0. The\n * inline `transform` IS the component's own write and would be readable, but its\n * numbers belong to a layout that is internal in this release — a harness method\n * returning them would make every layout improvement a breaking change to consumer\n * specs. Coordinates are pinned in the layout's own pure spec, where the arithmetic is.\n * - **No \"is scrollable\".** Overflow is layout: in jsdom `scrollWidth` and `clientWidth`\n * are both 0 for a graph that fits and for one ten times wider. The viewport's\n * `tabindex` is no proxy either — it is always `0`, precisely so the tab stop never\n * depends on a measurement.\n *\n * An empty graph and a graph whose layout drew nothing render the same DOM, so the\n * harness cannot tell them apart. {@link getNodes} answers `[]` for both, which is the\n * honest way to assert emptiness; {@link getEdges} refuses instead, because \"no edges\"\n * would be vacuously true of a graph that drew nothing at all.\n *\n * @example\n * ```ts\n * const graph = await loader.getHarness(WrGraphHarness.with({ ariaLabel: 'Team structure' }));\n *\n * expect(await graph.getEdges()).toEqual([{ from: 'atlas', to: 'birch' }]);\n * const birch = await graph.getNode({ label: 'Birch' });\n * expect(await birch.getRelationText()).toBe('Parents: Atlas.');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrGraphHarness extends ComponentHarness {\n static hostSelector = 'wr-graph';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrGraphHarnessFilters = {}): HarnessPredicate<WrGraphHarness> {\n return new HarnessPredicate(WrGraphHarness, options)\n .addOption('ariaLabel', options.ariaLabel, (harness, label) =>\n HarnessPredicate.stringMatches(harness.getAccessibleName(), label)\n )\n .addOption(\n 'nodeLabel',\n options.nodeLabel,\n async (harness, label) => (await harness.getNodes({ label })).length > 0\n );\n }\n\n /**\n * The viewport's accessible name — the scroller is the graph's one tab stop and the\n * element that carries it, never the host.\n */\n async getAccessibleName(): Promise<string | null> {\n return (await this.locatorFor(VIEWPORT)()).getAttribute('aria-label');\n }\n\n /**\n * This graph's nodes, in reading order, narrowed by the filters.\n *\n * Only the graph's own: a graph drawn inside one of its node templates has nodes of\n * its own, and they are not this graph's. `[]` for a graph that draws no node — see\n * the class note for why that answer cannot tell empty data from a layout that drew\n * nothing.\n */\n async getNodes(filters: WrGraphNodeHarnessFilters = {}): Promise<WrGraphNodeHarness[]> {\n return this.locatorForAll(WrGraphOwnNodeHarness.with(filters))();\n }\n\n /** The first node matching the filters, or a failure that says what the graph draws. */\n async getNode(filters: WrGraphNodeHarnessFilters): Promise<WrGraphNodeHarness> {\n const [node] = await this.getNodes(filters);\n if (node) return node;\n\n const nodes = await this.getNodes();\n if (!nodes.length) {\n throw new Error(\n `WrGraphHarness.getNode(): no node matched ${describeFilters(filters)} because the graph draws no nodes ` +\n 'at all — its nodes are empty, or its layout returned nothing it could draw.'\n );\n }\n const texts = await Promise.all(nodes.map(each => each.getText()));\n throw new Error(\n `WrGraphHarness.getNode(): no node matched ${describeFilters(filters)}. The graph draws ` +\n `[${texts.join(', ')}] — a label is matched against the DRAWN text, so a template that renders more ` +\n 'than the label needs a RegExp.'\n );\n }\n\n /**\n * One `{ from, to }` per line drawn, read off the path's `data-from` / `data-to` —\n * node ids, the only place the DOM writes one — in the order the edges were given,\n * with the edges the graph skipped left out. Parallel edges each appear, since each is\n * its own line.\n *\n * Throws on a graph that draws no nodes, rather than answering `[]`: that answer would\n * be the same for empty data and for a layout that drew nothing, and a spec asserting\n * \"no edges\" would pass on both.\n */\n async getEdges(): Promise<{ from: string; to: string }[]> {\n if (!(await this.locatorForAll(NODE)()).length) {\n throw new Error(\n 'WrGraphHarness.getEdges(): the graph draws no nodes, so an empty edge list would be vacuously true — ' +\n 'it reads the same for empty data and for a layout that drew nothing. Assert getNodes() is empty ' +\n 'when an empty graph is what the spec means.'\n );\n }\n\n const paths = await this.locatorForAll(EDGE)();\n return Promise.all(\n paths.map(async (path, index) => {\n const [from, to] = await Promise.all([path.getAttribute('data-from'), path.getAttribute('data-to')]);\n if (from === null || to === null) {\n throw new Error(\n `WrGraphHarness.getEdges(): line #${index} carries no data-from / data-to, so which nodes it joins ` +\n 'is not written anywhere a test can read.'\n );\n }\n return { from, to };\n })\n );\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ngwr-graph-testing.mjs","sources":["../../../projects/lib/graph/testing/wr-graph-node-harness.ts","../../../projects/lib/graph/testing/wr-graph-harness.ts","../../../projects/lib/graph/testing/ngwr-graph-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, type ComponentHarnessConstructor, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrGraphNodeHarnessFilters } from './interfaces';\n\n/** The visually hidden relation sentence the graph writes into every node that has an edge. */\nconst SR_ONLY = '.wr-graph__sr-only';\n\n/**\n * The node's OWN sentence. Anchored with `:scope >` because the graph writes it as a\n * direct child, after whatever the template drew — so a `<wr-graph>` nested inside a\n * node template would otherwise hand its first node's sentence to the outer node.\n */\nconst RELATION = `:scope > ${SR_ONLY}`;\n\n/**\n * What {@link WrGraphNodeHarness.getText} leaves out: the node's own sentence, and any\n * `<wr-graph>` a template drew inside the node. That inner graph is a graph of its own —\n * its cards are its nodes, read through its own harness — so counting its text as the\n * outer node's would let a RegExp label match the outer graph by a node it does not own.\n */\nconst NOT_DRAWN_BY_NODE = `${SR_ONLY}, wr-graph`;\n\n/**\n * Test harness for one node of a `<wr-graph>` — a `role=\"listitem\"` block.\n *\n * A node answers two questions, and they are kept apart on purpose. {@link getText} is\n * what is DRAWN: the default card, or whatever a `wrGraphNode` template rendered.\n * {@link getRelationText} is what is ANNOUNCED about its place in the graph: the\n * lines are `aria-hidden`, so the visually hidden sentence naming the parents and the\n * children is the only form the structure takes for a screen reader. A spec that read\n * the node's whole text would get both glued together, and would keep passing on a\n * graph whose relation sentences named the wrong neighbours.\n *\n * There is no `getId()`. The component writes a node's id nowhere in the DOM — only\n * an edge carries ids, on its `data-from` / `data-to` — so a node is addressed by what\n * it draws, and {@link WrGraphHarness.getEdges} is where ids are read.\n *\n * @example\n * ```ts\n * const birch = await graph.getNode({ label: 'Birch' });\n *\n * expect(await birch.getText()).toBe('Birch');\n * expect(await birch.getRelationText()).toBe('Parents: Atlas. Children: Dune.');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrGraphNodeHarness extends ComponentHarness {\n static hostSelector = '.wr-graph__node';\n\n /**\n * Build a predicate that narrows the query.\n *\n * Typed on `this`, so the predicate is built for whichever node harness it is called\n * on — {@link WrGraphHarness.getNodes} narrows its own, direct-child query through\n * the same options.\n */\n static with<T extends WrGraphNodeHarness>(\n this: ComponentHarnessConstructor<T>,\n options: WrGraphNodeHarnessFilters = {}\n ): HarnessPredicate<T> {\n return new HarnessPredicate(this, options).addOption('label', options.label, (harness, label) =>\n HarnessPredicate.stringMatches(harness.getText(), label)\n );\n }\n\n /**\n * The text the node DRAWS, trimmed, with the relation sentence left out.\n *\n * The sentence is a real text node inside the block — visually hidden, not removed —\n * so a plain text read would end in `Parents: Atlas. Children: Dune.`. It is excluded\n * rather than subtracted.\n *\n * A `<wr-graph>` a template nests inside the node is left out whole, cards and\n * sentences alike: it is a graph of its own, and its nodes answer to its own harness.\n * So a node drawing `Harbor` above an inner graph of `Kestrel` and `Lark` reads\n * `Harbor`, and a `/Lark/` label does not match it.\n */\n async getText(): Promise<string> {\n return (await this.host()).text({ exclude: NOT_DRAWN_BY_NODE });\n }\n\n /**\n * The sentence a screen reader hears about the node's place in the graph — its\n * parents, then its children, as the catalog words them — or `null` for a node with\n * no edges, which the graph gives no sentence at all.\n *\n * `null` rather than `''`: the component never writes an empty sentence, so an empty\n * string could only mean the element exists and says nothing, which is a different\n * failure from a node that has nothing to say.\n */\n async getRelationText(): Promise<string | null> {\n const relation = await this.locatorForOptional(RELATION)();\n return relation ? relation.text() : null;\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrGraphHarnessFilters, WrGraphNodeHarnessFilters } from './interfaces';\nimport { WrGraphNodeHarness } from './wr-graph-node-harness';\n\n/**\n * Every part of the graph is reached down one fixed chain of direct children. A node\n * template may draw anything, including another `<wr-graph>`, and a descendant query\n * from the outer host would count the inner graph's nodes and edges as its own.\n */\nconst VIEWPORT = ':scope > .wr-graph__viewport';\nconst STAGE = `${VIEWPORT} > .wr-graph__stage`;\nconst EDGE = `${STAGE} > .wr-graph__edges > .wr-graph__edge`;\nconst NODE = `${STAGE} > .wr-graph__nodes > .wr-graph__node`;\n\n/**\n * The same node harness, anchored to one graph. Not exported: queried from any other\n * root the `:scope` would be the wrong element, so a consumer loads\n * {@link WrGraphNodeHarness} and this class only ever comes back from\n * {@link WrGraphHarness.getNodes}.\n */\nclass WrGraphOwnNodeHarness extends WrGraphNodeHarness {\n static override hostSelector = NODE;\n}\n\n/**\n * The filters as a failure message should show them. Not `JSON.stringify`: it prints a\n * RegExp as `{}`, so a caller who had already passed one was shown nothing and then told\n * to use a RegExp. A RegExp reads as its literal, `/source/flags`; everything else as JSON.\n */\nfunction describeFilters(filters: WrGraphNodeHarnessFilters): string {\n const entries: [string, unknown][] = Object.entries(filters);\n const shown = entries\n .filter(([, value]) => value !== undefined)\n .map(([key, value]) => `${key}: ${value instanceof RegExp ? String(value) : JSON.stringify(value)}`);\n return shown.length ? `{ ${shown.join(', ')} }` : '{}';\n}\n\n/**\n * Test harness for `<wr-graph>` — blocks joined by parent-child lines, laid out in\n * layers, display only.\n *\n * The graph is read the way assistive technology reads it: the viewport's name, the\n * nodes as a list in READING order (layer, then inline position — which is DOM order),\n * each node's drawn text and its relation sentence, and the edges as the `from` / `to`\n * ids the component writes on each line. Those are what break when the data wiring\n * does.\n *\n * Three methods are deliberately absent, and each would answer the same for a working\n * graph and a broken one:\n *\n * - **No edge path `d`.** A path is a rendering detail: rounded to two decimals, mirrored\n * under RTL, and moved by every node size and gap. The same rule as\n * `WrLineChartHarness`.\n * - **No node positions.** jsdom lays nothing out, so every measured box is 0×0. The\n * inline `transform` IS the component's own write and would be readable, but its\n * numbers belong to a layout that is internal in this release — a harness method\n * returning them would make every layout improvement a breaking change to consumer\n * specs. Coordinates are pinned in the layout's own pure spec, where the arithmetic is.\n * - **No \"is scrollable\".** Overflow is layout: in jsdom `scrollWidth` and `clientWidth`\n * are both 0 for a graph that fits and for one ten times wider. The viewport's\n * `tabindex` is no proxy either — it is always `0`, precisely so the tab stop never\n * depends on a measurement.\n *\n * An empty graph and a graph whose layout drew nothing render the same DOM, so the\n * harness cannot tell them apart. {@link getNodes} answers `[]` for both, which is the\n * honest way to assert emptiness; {@link getEdges} refuses instead, because \"no edges\"\n * would be vacuously true of a graph that drew nothing at all.\n *\n * @example\n * ```ts\n * const graph = await loader.getHarness(WrGraphHarness.with({ ariaLabel: 'Team structure' }));\n *\n * expect(await graph.getEdges()).toEqual([{ from: 'atlas', to: 'birch' }]);\n * const birch = await graph.getNode({ label: 'Birch' });\n * expect(await birch.getRelationText()).toBe('Parents: Atlas.');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrGraphHarness extends ComponentHarness {\n static hostSelector = 'wr-graph';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrGraphHarnessFilters = {}): HarnessPredicate<WrGraphHarness> {\n return new HarnessPredicate(WrGraphHarness, options)\n .addOption('ariaLabel', options.ariaLabel, (harness, label) =>\n HarnessPredicate.stringMatches(harness.getAccessibleName(), label)\n )\n .addOption(\n 'nodeLabel',\n options.nodeLabel,\n async (harness, label) => (await harness.getNodes({ label })).length > 0\n );\n }\n\n /**\n * The viewport's accessible name — the scroller is the graph's one tab stop and the\n * element that carries it, never the host.\n */\n async getAccessibleName(): Promise<string | null> {\n return (await this.locatorFor(VIEWPORT)()).getAttribute('aria-label');\n }\n\n /**\n * This graph's nodes, in reading order, narrowed by the filters.\n *\n * Only the graph's own: a graph drawn inside one of its node templates has nodes of\n * its own, and they are not this graph's. `[]` for a graph that draws no node — see\n * the class note for why that answer cannot tell empty data from a layout that drew\n * nothing.\n */\n async getNodes(filters: WrGraphNodeHarnessFilters = {}): Promise<WrGraphNodeHarness[]> {\n return this.locatorForAll(WrGraphOwnNodeHarness.with(filters))();\n }\n\n /** The first node matching the filters, or a failure that says what the graph draws. */\n async getNode(filters: WrGraphNodeHarnessFilters): Promise<WrGraphNodeHarness> {\n const [node] = await this.getNodes(filters);\n if (node) return node;\n\n const nodes = await this.getNodes();\n if (!nodes.length) {\n throw new Error(\n `WrGraphHarness.getNode(): no node matched ${describeFilters(filters)} because the graph draws no nodes ` +\n 'at all — its nodes are empty, or its layout returned nothing it could draw.'\n );\n }\n const texts = await Promise.all(nodes.map(each => each.getText()));\n throw new Error(\n `WrGraphHarness.getNode(): no node matched ${describeFilters(filters)}. The graph draws ` +\n `[${texts.join(', ')}] — a label is matched against the DRAWN text, so a template that renders more ` +\n 'than the label needs a RegExp.'\n );\n }\n\n /**\n * One `{ from, to }` per line drawn, read off the path's `data-from` / `data-to` —\n * node ids, the only place the DOM writes one — in the order the edges were given,\n * with the edges the graph skipped left out. Parallel edges each appear, since each is\n * its own line.\n *\n * Throws on a graph that draws no nodes, rather than answering `[]`: that answer would\n * be the same for empty data and for a layout that drew nothing, and a spec asserting\n * \"no edges\" would pass on both.\n */\n async getEdges(): Promise<{ from: string; to: string }[]> {\n if (!(await this.locatorForAll(NODE)()).length) {\n throw new Error(\n 'WrGraphHarness.getEdges(): the graph draws no nodes, so an empty edge list would be vacuously true — ' +\n 'it reads the same for empty data and for a layout that drew nothing. Assert getNodes() is empty ' +\n 'when an empty graph is what the spec means.'\n );\n }\n\n const paths = await this.locatorForAll(EDGE)();\n return Promise.all(\n paths.map(async (path, index) => {\n const [from, to] = await Promise.all([path.getAttribute('data-from'), path.getAttribute('data-to')]);\n if (from === null || to === null) {\n throw new Error(\n `WrGraphHarness.getEdges(): line #${index} carries no data-from / data-to, so which nodes it joins ` +\n 'is not written anywhere a test can read.'\n );\n }\n return { from, to };\n })\n );\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -30,10 +30,10 @@ import { numAttr } from 'ngwr/utils';
30
30
  */
31
31
  class WrGraphNodeTemplate {
32
32
  template = inject(TemplateRef);
33
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrGraphNodeTemplate, deps: [], target: i0.ɵɵFactoryTarget.Directive });
34
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.6", type: WrGraphNodeTemplate, isStandalone: true, selector: "ng-template[wrGraphNode]", ngImport: i0 });
33
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrGraphNodeTemplate, deps: [], target: i0.ɵɵFactoryTarget.Directive });
34
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.7", type: WrGraphNodeTemplate, isStandalone: true, selector: "ng-template[wrGraphNode]", ngImport: i0 });
35
35
  }
36
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrGraphNodeTemplate, decorators: [{
36
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrGraphNodeTemplate, decorators: [{
37
37
  type: Directive,
38
38
  args: [{
39
39
  selector: 'ng-template[wrGraphNode]',
@@ -1094,10 +1094,10 @@ class WrGraph {
1094
1094
  });
1095
1095
  }
1096
1096
  }
1097
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrGraph, deps: [], target: i0.ɵɵFactoryTarget.Component });
1098
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: WrGraph, isStandalone: true, selector: "wr-graph", inputs: { nodes: { classPropertyName: "nodes", publicName: "nodes", isSignal: true, isRequired: false, transformFunction: null }, edges: { classPropertyName: "edges", publicName: "edges", isSignal: true, isRequired: false, transformFunction: null }, nodeWidth: { classPropertyName: "nodeWidth", publicName: "nodeWidth", isSignal: true, isRequired: false, transformFunction: null }, nodeHeight: { classPropertyName: "nodeHeight", publicName: "nodeHeight", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "wr-graph" }, queries: [{ propertyName: "nodeTemplate", first: true, predicate: WrGraphNodeTemplate, descendants: true, isSignal: true }], ngImport: i0, template: "@let graph = view();\n<!-- The scroller is the one tab stop: a named group, so its label is allowed, and focusable so a keyboard can scroll it. -->\n<div class=\"wr-graph__viewport\" role=\"group\" tabindex=\"0\" [attr.aria-label]=\"label()\">\n <div class=\"wr-graph__stage\" [style.width.px]=\"graph.width\" [style.height.px]=\"graph.height\">\n <svg\n class=\"wr-graph__edges\"\n aria-hidden=\"true\"\n focusable=\"false\"\n [attr.width]=\"graph.width\"\n [attr.height]=\"graph.height\"\n >\n <defs>\n <marker\n [attr.id]=\"markerId\"\n viewBox=\"0 0 10 10\"\n refX=\"10\"\n refY=\"5\"\n markerUnits=\"userSpaceOnUse\"\n [attr.markerWidth]=\"arrowSize\"\n [attr.markerHeight]=\"arrowSize\"\n orient=\"auto\"\n >\n <polygon points=\"0,1 10,5 0,9\" />\n </marker>\n </defs>\n @for (edge of graph.edges; track edge.id) {\n <path\n class=\"wr-graph__edge\"\n [attr.d]=\"edge.d\"\n [attr.data-from]=\"edge.from\"\n [attr.data-to]=\"edge.to\"\n [attr.marker-end]=\"markerUrl\"\n />\n }\n </svg>\n <!-- Items are DIRECT children of the list: a wrapper carrying ARIA or a tabindex would break ownership. -->\n <div class=\"wr-graph__nodes\" [attr.role]=\"graph.nodes.length ? 'list' : null\">\n @for (entry of graph.nodes; track entry.node.id) {\n @let relation = relations().get(entry.node.id);\n <div\n class=\"wr-graph__node\"\n role=\"listitem\"\n [style.width.px]=\"entry.width\"\n [style.height.px]=\"entry.height\"\n [style.transform]=\"entry.transform\"\n >\n @if (nodeTemplate(); as custom) {\n <ng-container [ngTemplateOutlet]=\"custom.template\" [ngTemplateOutletContext]=\"{ $implicit: entry.node }\" />\n } @else {\n <div class=\"wr-graph__card\">{{ entry.node.label }}</div>\n }\n @if (relation) {\n <span class=\"wr-graph__sr-only\">{{ relation }}</span>\n }\n </div>\n }\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], encapsulation: i0.ViewEncapsulation.None });
1097
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrGraph, deps: [], target: i0.ɵɵFactoryTarget.Component });
1098
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: WrGraph, isStandalone: true, selector: "wr-graph", inputs: { nodes: { classPropertyName: "nodes", publicName: "nodes", isSignal: true, isRequired: false, transformFunction: null }, edges: { classPropertyName: "edges", publicName: "edges", isSignal: true, isRequired: false, transformFunction: null }, nodeWidth: { classPropertyName: "nodeWidth", publicName: "nodeWidth", isSignal: true, isRequired: false, transformFunction: null }, nodeHeight: { classPropertyName: "nodeHeight", publicName: "nodeHeight", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "wr-graph" }, queries: [{ propertyName: "nodeTemplate", first: true, predicate: WrGraphNodeTemplate, descendants: true, isSignal: true }], ngImport: i0, template: "@let graph = view();\n<!-- The scroller is the one tab stop: a named group, so its label is allowed, and focusable so a keyboard can scroll it. -->\n<div class=\"wr-graph__viewport\" role=\"group\" tabindex=\"0\" [attr.aria-label]=\"label()\">\n <div class=\"wr-graph__stage\" [style.width.px]=\"graph.width\" [style.height.px]=\"graph.height\">\n <svg\n class=\"wr-graph__edges\"\n aria-hidden=\"true\"\n focusable=\"false\"\n [attr.width]=\"graph.width\"\n [attr.height]=\"graph.height\"\n >\n <defs>\n <marker\n [attr.id]=\"markerId\"\n viewBox=\"0 0 10 10\"\n refX=\"10\"\n refY=\"5\"\n markerUnits=\"userSpaceOnUse\"\n [attr.markerWidth]=\"arrowSize\"\n [attr.markerHeight]=\"arrowSize\"\n orient=\"auto\"\n >\n <polygon points=\"0,1 10,5 0,9\" />\n </marker>\n </defs>\n @for (edge of graph.edges; track edge.id) {\n <path\n class=\"wr-graph__edge\"\n [attr.d]=\"edge.d\"\n [attr.data-from]=\"edge.from\"\n [attr.data-to]=\"edge.to\"\n [attr.marker-end]=\"markerUrl\"\n />\n }\n </svg>\n <!-- Items are DIRECT children of the list: a wrapper carrying ARIA or a tabindex would break ownership. -->\n <div class=\"wr-graph__nodes\" [attr.role]=\"graph.nodes.length ? 'list' : null\">\n @for (entry of graph.nodes; track entry.node.id) {\n @let relation = relations().get(entry.node.id);\n <div\n class=\"wr-graph__node\"\n role=\"listitem\"\n [style.width.px]=\"entry.width\"\n [style.height.px]=\"entry.height\"\n [style.transform]=\"entry.transform\"\n >\n @if (nodeTemplate(); as custom) {\n <ng-container [ngTemplateOutlet]=\"custom.template\" [ngTemplateOutletContext]=\"{ $implicit: entry.node }\" />\n } @else {\n <div class=\"wr-graph__card\">{{ entry.node.label }}</div>\n }\n @if (relation) {\n <span class=\"wr-graph__sr-only\">{{ relation }}</span>\n }\n </div>\n }\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], encapsulation: i0.ViewEncapsulation.None });
1099
1099
  }
1100
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrGraph, decorators: [{
1100
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrGraph, decorators: [{
1101
1101
  type: Component,
1102
1102
  args: [{ selector: 'wr-graph', imports: [NgTemplateOutlet], encapsulation: ViewEncapsulation.None, host: {
1103
1103
  class: 'wr-graph',