ngwr 14.7.0 → 14.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (482) hide show
  1. package/README.md +66 -332
  2. package/alert/styles/_index.scss +10 -0
  3. package/avatar/styles/_index.scss +18 -7
  4. package/badge/styles/_index.scss +7 -0
  5. package/badge/styles/_tag.scss +7 -1
  6. package/bar-chart/styles/_index.scss +1 -0
  7. package/button/styles/_index.scss +6 -0
  8. package/calendar-heatmap/styles/_index.scss +1 -0
  9. package/cascader/styles/_rules.scss +87 -15
  10. package/checkbox/styles/_index.scss +4 -0
  11. package/command-palette/styles/_index.scss +1 -1
  12. package/date-picker/styles/_index.scss +30 -0
  13. package/density/styles/_index.scss +4 -2
  14. package/divider/styles/_index.scss +2 -0
  15. package/donut-chart/styles/_index.scss +10 -0
  16. package/dropdown/styles/_index.scss +67 -5
  17. package/editor/styles/_index.scss +441 -0
  18. package/event-calendar/styles/_index.scss +209 -35
  19. package/fesm2022/ngwr-action-sheet-testing.mjs.map +1 -1
  20. package/fesm2022/ngwr-action-sheet.mjs +3 -3
  21. package/fesm2022/ngwr-action-sheet.mjs.map +1 -1
  22. package/fesm2022/ngwr-affix.mjs +3 -3
  23. package/fesm2022/ngwr-affix.mjs.map +1 -1
  24. package/fesm2022/ngwr-alert.mjs +3 -3
  25. package/fesm2022/ngwr-alert.mjs.map +1 -1
  26. package/fesm2022/ngwr-anchor.mjs +3 -3
  27. package/fesm2022/ngwr-anchor.mjs.map +1 -1
  28. package/fesm2022/ngwr-aurora.mjs +3 -3
  29. package/fesm2022/ngwr-aurora.mjs.map +1 -1
  30. package/fesm2022/ngwr-avatar.mjs +3 -3
  31. package/fesm2022/ngwr-avatar.mjs.map +1 -1
  32. package/fesm2022/ngwr-back-top.mjs +3 -3
  33. package/fesm2022/ngwr-back-top.mjs.map +1 -1
  34. package/fesm2022/ngwr-badge.mjs +28 -13
  35. package/fesm2022/ngwr-badge.mjs.map +1 -1
  36. package/fesm2022/ngwr-bar-chart-testing.mjs.map +1 -1
  37. package/fesm2022/ngwr-bar-chart.mjs +31 -6
  38. package/fesm2022/ngwr-bar-chart.mjs.map +1 -1
  39. package/fesm2022/ngwr-blur-text-testing.mjs.map +1 -1
  40. package/fesm2022/ngwr-blur-text.mjs +3 -3
  41. package/fesm2022/ngwr-blur-text.mjs.map +1 -1
  42. package/fesm2022/ngwr-border-glow-testing.mjs.map +1 -1
  43. package/fesm2022/ngwr-border-glow.mjs +3 -3
  44. package/fesm2022/ngwr-border-glow.mjs.map +1 -1
  45. package/fesm2022/ngwr-breadcrumbs.mjs +6 -6
  46. package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
  47. package/fesm2022/ngwr-burger.mjs +3 -3
  48. package/fesm2022/ngwr-burger.mjs.map +1 -1
  49. package/fesm2022/ngwr-button-testing.mjs.map +1 -1
  50. package/fesm2022/ngwr-button.mjs +6 -6
  51. package/fesm2022/ngwr-button.mjs.map +1 -1
  52. package/fesm2022/ngwr-calendar-heatmap-testing.mjs.map +1 -1
  53. package/fesm2022/ngwr-calendar-heatmap.mjs +61 -6
  54. package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
  55. package/fesm2022/ngwr-calendar-testing.mjs.map +1 -1
  56. package/fesm2022/ngwr-calendar.mjs +3 -3
  57. package/fesm2022/ngwr-calendar.mjs.map +1 -1
  58. package/fesm2022/ngwr-card.mjs +9 -9
  59. package/fesm2022/ngwr-card.mjs.map +1 -1
  60. package/fesm2022/ngwr-carousel-testing.mjs.map +1 -1
  61. package/fesm2022/ngwr-carousel.mjs +6 -6
  62. package/fesm2022/ngwr-carousel.mjs.map +1 -1
  63. package/fesm2022/ngwr-cascader-testing.mjs.map +1 -1
  64. package/fesm2022/ngwr-cascader.mjs +4 -4
  65. package/fesm2022/ngwr-cascader.mjs.map +1 -1
  66. package/fesm2022/ngwr-checkbox-testing.mjs.map +1 -1
  67. package/fesm2022/ngwr-checkbox.mjs +18 -8
  68. package/fesm2022/ngwr-checkbox.mjs.map +1 -1
  69. package/fesm2022/ngwr-circular-text-testing.mjs.map +1 -1
  70. package/fesm2022/ngwr-circular-text.mjs +3 -3
  71. package/fesm2022/ngwr-circular-text.mjs.map +1 -1
  72. package/fesm2022/ngwr-click-spark.mjs +3 -3
  73. package/fesm2022/ngwr-click-spark.mjs.map +1 -1
  74. package/fesm2022/ngwr-clipboard.mjs +3 -3
  75. package/fesm2022/ngwr-clipboard.mjs.map +1 -1
  76. package/fesm2022/ngwr-collapse-testing.mjs.map +1 -1
  77. package/fesm2022/ngwr-collapse.mjs +9 -9
  78. package/fesm2022/ngwr-collapse.mjs.map +1 -1
  79. package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -1
  80. package/fesm2022/ngwr-color-picker.mjs +6 -6
  81. package/fesm2022/ngwr-color-picker.mjs.map +1 -1
  82. package/fesm2022/ngwr-command-palette-testing.mjs.map +1 -1
  83. package/fesm2022/ngwr-command-palette.mjs +3 -3
  84. package/fesm2022/ngwr-command-palette.mjs.map +1 -1
  85. package/fesm2022/ngwr-compare.mjs +3 -3
  86. package/fesm2022/ngwr-compare.mjs.map +1 -1
  87. package/fesm2022/ngwr-confetti.mjs +3 -3
  88. package/fesm2022/ngwr-confetti.mjs.map +1 -1
  89. package/fesm2022/ngwr-config.mjs.map +1 -1
  90. package/fesm2022/ngwr-context-menu-testing.mjs.map +1 -1
  91. package/fesm2022/ngwr-context-menu.mjs +12 -12
  92. package/fesm2022/ngwr-context-menu.mjs.map +1 -1
  93. package/fesm2022/ngwr-cookie.mjs +3 -3
  94. package/fesm2022/ngwr-cookie.mjs.map +1 -1
  95. package/fesm2022/ngwr-counter.mjs +6 -6
  96. package/fesm2022/ngwr-counter.mjs.map +1 -1
  97. package/fesm2022/ngwr-date-adapters-fns.mjs +3 -3
  98. package/fesm2022/ngwr-date-adapters-fns.mjs.map +1 -1
  99. package/fesm2022/ngwr-date-adapters-luxon.mjs +3 -3
  100. package/fesm2022/ngwr-date-adapters-luxon.mjs.map +1 -1
  101. package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
  102. package/fesm2022/ngwr-date-picker.mjs +721 -62
  103. package/fesm2022/ngwr-date-picker.mjs.map +1 -1
  104. package/fesm2022/ngwr-date.mjs +3 -3
  105. package/fesm2022/ngwr-date.mjs.map +1 -1
  106. package/fesm2022/ngwr-decrypt-text-testing.mjs.map +1 -1
  107. package/fesm2022/ngwr-decrypt-text.mjs +3 -3
  108. package/fesm2022/ngwr-decrypt-text.mjs.map +1 -1
  109. package/fesm2022/ngwr-density.mjs +6 -6
  110. package/fesm2022/ngwr-density.mjs.map +1 -1
  111. package/fesm2022/ngwr-descriptions.mjs +6 -6
  112. package/fesm2022/ngwr-descriptions.mjs.map +1 -1
  113. package/fesm2022/ngwr-dialog-testing.mjs.map +1 -1
  114. package/fesm2022/ngwr-dialog.mjs +15 -15
  115. package/fesm2022/ngwr-dialog.mjs.map +1 -1
  116. package/fesm2022/ngwr-directives.mjs +12 -12
  117. package/fesm2022/ngwr-directives.mjs.map +1 -1
  118. package/fesm2022/ngwr-divider.mjs +3 -3
  119. package/fesm2022/ngwr-divider.mjs.map +1 -1
  120. package/fesm2022/ngwr-donut-chart-testing.mjs.map +1 -1
  121. package/fesm2022/ngwr-donut-chart.mjs +48 -6
  122. package/fesm2022/ngwr-donut-chart.mjs.map +1 -1
  123. package/fesm2022/ngwr-drag-drop.mjs +6 -6
  124. package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
  125. package/fesm2022/ngwr-drawer-testing.mjs.map +1 -1
  126. package/fesm2022/ngwr-drawer.mjs +18 -18
  127. package/fesm2022/ngwr-drawer.mjs.map +1 -1
  128. package/fesm2022/ngwr-dropdown-testing.mjs.map +1 -1
  129. package/fesm2022/ngwr-dropdown.mjs +174 -25
  130. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  131. package/fesm2022/ngwr-editor-testing.mjs +568 -0
  132. package/fesm2022/ngwr-editor-testing.mjs.map +1 -0
  133. package/fesm2022/ngwr-editor.mjs +2601 -0
  134. package/fesm2022/ngwr-editor.mjs.map +1 -0
  135. package/fesm2022/ngwr-empty.mjs +3 -3
  136. package/fesm2022/ngwr-empty.mjs.map +1 -1
  137. package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -1
  138. package/fesm2022/ngwr-event-calendar.mjs +14 -7
  139. package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
  140. package/fesm2022/ngwr-falling-text-testing.mjs.map +1 -1
  141. package/fesm2022/ngwr-falling-text.mjs +3 -3
  142. package/fesm2022/ngwr-falling-text.mjs.map +1 -1
  143. package/fesm2022/ngwr-file-upload-testing.mjs.map +1 -1
  144. package/fesm2022/ngwr-file-upload.mjs +3 -3
  145. package/fesm2022/ngwr-file-upload.mjs.map +1 -1
  146. package/fesm2022/ngwr-form-testing.mjs.map +1 -1
  147. package/fesm2022/ngwr-form.mjs +61 -10
  148. package/fesm2022/ngwr-form.mjs.map +1 -1
  149. package/fesm2022/ngwr-fuzzy-text-testing.mjs.map +1 -1
  150. package/fesm2022/ngwr-fuzzy-text.mjs +3 -3
  151. package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
  152. package/fesm2022/ngwr-gauge-testing.mjs.map +1 -1
  153. package/fesm2022/ngwr-gauge.mjs +35 -6
  154. package/fesm2022/ngwr-gauge.mjs.map +1 -1
  155. package/fesm2022/ngwr-glitch-text-testing.mjs.map +1 -1
  156. package/fesm2022/ngwr-glitch-text.mjs +3 -3
  157. package/fesm2022/ngwr-glitch-text.mjs.map +1 -1
  158. package/fesm2022/ngwr-gradient-text-testing.mjs.map +1 -1
  159. package/fesm2022/ngwr-gradient-text.mjs +3 -3
  160. package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
  161. package/fesm2022/ngwr-graph-testing.mjs.map +1 -1
  162. package/fesm2022/ngwr-graph.mjs +6 -6
  163. package/fesm2022/ngwr-graph.mjs.map +1 -1
  164. package/fesm2022/ngwr-hotkey.mjs +6 -6
  165. package/fesm2022/ngwr-hotkey.mjs.map +1 -1
  166. package/fesm2022/ngwr-i18n-ar.mjs +31 -0
  167. package/fesm2022/ngwr-i18n-ar.mjs.map +1 -1
  168. package/fesm2022/ngwr-i18n-cs.mjs +31 -0
  169. package/fesm2022/ngwr-i18n-cs.mjs.map +1 -1
  170. package/fesm2022/ngwr-i18n-de.mjs +31 -0
  171. package/fesm2022/ngwr-i18n-de.mjs.map +1 -1
  172. package/fesm2022/ngwr-i18n-en.mjs +39 -0
  173. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  174. package/fesm2022/ngwr-i18n-es.mjs +31 -0
  175. package/fesm2022/ngwr-i18n-es.mjs.map +1 -1
  176. package/fesm2022/ngwr-i18n-fr.mjs +31 -0
  177. package/fesm2022/ngwr-i18n-fr.mjs.map +1 -1
  178. package/fesm2022/ngwr-i18n-he.mjs +31 -0
  179. package/fesm2022/ngwr-i18n-he.mjs.map +1 -1
  180. package/fesm2022/ngwr-i18n-hi.mjs +31 -0
  181. package/fesm2022/ngwr-i18n-hi.mjs.map +1 -1
  182. package/fesm2022/ngwr-i18n-id.mjs +31 -0
  183. package/fesm2022/ngwr-i18n-id.mjs.map +1 -1
  184. package/fesm2022/ngwr-i18n-it.mjs +31 -0
  185. package/fesm2022/ngwr-i18n-it.mjs.map +1 -1
  186. package/fesm2022/ngwr-i18n-ja.mjs +31 -0
  187. package/fesm2022/ngwr-i18n-ja.mjs.map +1 -1
  188. package/fesm2022/ngwr-i18n-ko.mjs +31 -0
  189. package/fesm2022/ngwr-i18n-ko.mjs.map +1 -1
  190. package/fesm2022/ngwr-i18n-nl.mjs +31 -0
  191. package/fesm2022/ngwr-i18n-nl.mjs.map +1 -1
  192. package/fesm2022/ngwr-i18n-pl.mjs +31 -0
  193. package/fesm2022/ngwr-i18n-pl.mjs.map +1 -1
  194. package/fesm2022/ngwr-i18n-pt.mjs +31 -0
  195. package/fesm2022/ngwr-i18n-pt.mjs.map +1 -1
  196. package/fesm2022/ngwr-i18n-ru.mjs +31 -0
  197. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  198. package/fesm2022/ngwr-i18n-sv.mjs +31 -0
  199. package/fesm2022/ngwr-i18n-sv.mjs.map +1 -1
  200. package/fesm2022/ngwr-i18n-tr.mjs +31 -0
  201. package/fesm2022/ngwr-i18n-tr.mjs.map +1 -1
  202. package/fesm2022/ngwr-i18n-uk.mjs +31 -0
  203. package/fesm2022/ngwr-i18n-uk.mjs.map +1 -1
  204. package/fesm2022/ngwr-i18n-vi.mjs +31 -0
  205. package/fesm2022/ngwr-i18n-vi.mjs.map +1 -1
  206. package/fesm2022/ngwr-i18n-zh-TW.mjs +31 -0
  207. package/fesm2022/ngwr-i18n-zh-TW.mjs.map +1 -1
  208. package/fesm2022/ngwr-i18n-zh.mjs +31 -0
  209. package/fesm2022/ngwr-i18n-zh.mjs.map +1 -1
  210. package/fesm2022/ngwr-i18n.mjs +9 -9
  211. package/fesm2022/ngwr-i18n.mjs.map +1 -1
  212. package/fesm2022/ngwr-icon-adapters-feather.mjs.map +1 -1
  213. package/fesm2022/ngwr-icon-adapters-lucide.mjs.map +1 -1
  214. package/fesm2022/ngwr-icon.mjs +6 -6
  215. package/fesm2022/ngwr-icon.mjs.map +1 -1
  216. package/fesm2022/ngwr-image-cropper-testing.mjs.map +1 -1
  217. package/fesm2022/ngwr-image-cropper.mjs +3 -3
  218. package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
  219. package/fesm2022/ngwr-input-number-testing.mjs.map +1 -1
  220. package/fesm2022/ngwr-input-number.mjs +15 -3
  221. package/fesm2022/ngwr-input-number.mjs.map +1 -1
  222. package/fesm2022/ngwr-input-otp-testing.mjs.map +1 -1
  223. package/fesm2022/ngwr-input-otp.mjs +3 -3
  224. package/fesm2022/ngwr-input-otp.mjs.map +1 -1
  225. package/fesm2022/ngwr-input-testing.mjs.map +1 -1
  226. package/fesm2022/ngwr-input.mjs +15 -15
  227. package/fesm2022/ngwr-input.mjs.map +1 -1
  228. package/fesm2022/ngwr-keyboard.mjs +3 -3
  229. package/fesm2022/ngwr-keyboard.mjs.map +1 -1
  230. package/fesm2022/ngwr-knob-testing.mjs.map +1 -1
  231. package/fesm2022/ngwr-knob.mjs +3 -3
  232. package/fesm2022/ngwr-knob.mjs.map +1 -1
  233. package/fesm2022/ngwr-layout.mjs +15 -15
  234. package/fesm2022/ngwr-layout.mjs.map +1 -1
  235. package/fesm2022/ngwr-lightbox-testing.mjs.map +1 -1
  236. package/fesm2022/ngwr-lightbox.mjs +3 -3
  237. package/fesm2022/ngwr-lightbox.mjs.map +1 -1
  238. package/fesm2022/ngwr-line-chart-testing.mjs.map +1 -1
  239. package/fesm2022/ngwr-line-chart.mjs +18 -7
  240. package/fesm2022/ngwr-line-chart.mjs.map +1 -1
  241. package/fesm2022/ngwr-list.mjs +6 -6
  242. package/fesm2022/ngwr-list.mjs.map +1 -1
  243. package/fesm2022/ngwr-loading-bar-router.mjs.map +1 -1
  244. package/fesm2022/ngwr-loading-bar.mjs +6 -6
  245. package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
  246. package/fesm2022/ngwr-markdown-testing.mjs.map +1 -1
  247. package/fesm2022/ngwr-markdown.mjs +798 -17
  248. package/fesm2022/ngwr-markdown.mjs.map +1 -1
  249. package/fesm2022/ngwr-marquee-testing.mjs.map +1 -1
  250. package/fesm2022/ngwr-marquee.mjs +3 -3
  251. package/fesm2022/ngwr-marquee.mjs.map +1 -1
  252. package/fesm2022/ngwr-media.mjs +3 -3
  253. package/fesm2022/ngwr-media.mjs.map +1 -1
  254. package/fesm2022/ngwr-mention-testing.mjs.map +1 -1
  255. package/fesm2022/ngwr-mention.mjs +6 -6
  256. package/fesm2022/ngwr-mention.mjs.map +1 -1
  257. package/fesm2022/ngwr-meta.mjs +6 -6
  258. package/fesm2022/ngwr-meta.mjs.map +1 -1
  259. package/fesm2022/ngwr-meter-group-testing.mjs +7 -3
  260. package/fesm2022/ngwr-meter-group-testing.mjs.map +1 -1
  261. package/fesm2022/ngwr-meter-group.mjs +26 -6
  262. package/fesm2022/ngwr-meter-group.mjs.map +1 -1
  263. package/fesm2022/ngwr-overlay.mjs +6 -6
  264. package/fesm2022/ngwr-overlay.mjs.map +1 -1
  265. package/fesm2022/ngwr-page-header.mjs +13 -5
  266. package/fesm2022/ngwr-page-header.mjs.map +1 -1
  267. package/fesm2022/ngwr-pagination-testing.mjs.map +1 -1
  268. package/fesm2022/ngwr-pagination.mjs +3 -3
  269. package/fesm2022/ngwr-pagination.mjs.map +1 -1
  270. package/fesm2022/ngwr-pipes.mjs +21 -21
  271. package/fesm2022/ngwr-pipes.mjs.map +1 -1
  272. package/fesm2022/ngwr-platform.mjs +9 -9
  273. package/fesm2022/ngwr-platform.mjs.map +1 -1
  274. package/fesm2022/ngwr-popconfirm-testing.mjs.map +1 -1
  275. package/fesm2022/ngwr-popconfirm.mjs +6 -6
  276. package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
  277. package/fesm2022/ngwr-popover-testing.mjs.map +1 -1
  278. package/fesm2022/ngwr-popover.mjs +578 -16
  279. package/fesm2022/ngwr-popover.mjs.map +1 -1
  280. package/fesm2022/ngwr-progress.mjs +3 -3
  281. package/fesm2022/ngwr-progress.mjs.map +1 -1
  282. package/fesm2022/ngwr-pull-to-refresh.mjs +3 -3
  283. package/fesm2022/ngwr-pull-to-refresh.mjs.map +1 -1
  284. package/fesm2022/ngwr-qr.mjs +3 -3
  285. package/fesm2022/ngwr-qr.mjs.map +1 -1
  286. package/fesm2022/ngwr-radio-testing.mjs.map +1 -1
  287. package/fesm2022/ngwr-radio.mjs +6 -6
  288. package/fesm2022/ngwr-radio.mjs.map +1 -1
  289. package/fesm2022/ngwr-rating-testing.mjs.map +1 -1
  290. package/fesm2022/ngwr-rating.mjs +3 -3
  291. package/fesm2022/ngwr-rating.mjs.map +1 -1
  292. package/fesm2022/ngwr-result.mjs +12 -12
  293. package/fesm2022/ngwr-result.mjs.map +1 -1
  294. package/fesm2022/ngwr-rotating-text-testing.mjs.map +1 -1
  295. package/fesm2022/ngwr-rotating-text.mjs +3 -3
  296. package/fesm2022/ngwr-rotating-text.mjs.map +1 -1
  297. package/fesm2022/ngwr-schema-form.mjs +3 -3
  298. package/fesm2022/ngwr-schema-form.mjs.map +1 -1
  299. package/fesm2022/ngwr-scroll.mjs +3 -3
  300. package/fesm2022/ngwr-scroll.mjs.map +1 -1
  301. package/fesm2022/ngwr-segmented-testing.mjs.map +1 -1
  302. package/fesm2022/ngwr-segmented.mjs +3 -3
  303. package/fesm2022/ngwr-segmented.mjs.map +1 -1
  304. package/fesm2022/ngwr-select-testing.mjs.map +1 -1
  305. package/fesm2022/ngwr-select.mjs +12 -12
  306. package/fesm2022/ngwr-select.mjs.map +1 -1
  307. package/fesm2022/ngwr-shiny-text-testing.mjs.map +1 -1
  308. package/fesm2022/ngwr-shiny-text.mjs +6 -6
  309. package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
  310. package/fesm2022/ngwr-sidebar.mjs +10 -3
  311. package/fesm2022/ngwr-sidebar.mjs.map +1 -1
  312. package/fesm2022/ngwr-skeleton.mjs +3 -3
  313. package/fesm2022/ngwr-skeleton.mjs.map +1 -1
  314. package/fesm2022/ngwr-slider-testing.mjs.map +1 -1
  315. package/fesm2022/ngwr-slider.mjs +3 -3
  316. package/fesm2022/ngwr-slider.mjs.map +1 -1
  317. package/fesm2022/ngwr-sparkline-testing.mjs.map +1 -1
  318. package/fesm2022/ngwr-sparkline.mjs +84 -10
  319. package/fesm2022/ngwr-sparkline.mjs.map +1 -1
  320. package/fesm2022/ngwr-speed-dial-testing.mjs.map +1 -1
  321. package/fesm2022/ngwr-speed-dial.mjs +3 -3
  322. package/fesm2022/ngwr-speed-dial.mjs.map +1 -1
  323. package/fesm2022/ngwr-spinner.mjs +3 -3
  324. package/fesm2022/ngwr-spinner.mjs.map +1 -1
  325. package/fesm2022/ngwr-splash-cursor-testing.mjs.map +1 -1
  326. package/fesm2022/ngwr-splash-cursor.mjs +3 -3
  327. package/fesm2022/ngwr-splash-cursor.mjs.map +1 -1
  328. package/fesm2022/ngwr-split-text-testing.mjs.map +1 -1
  329. package/fesm2022/ngwr-split-text.mjs +3 -3
  330. package/fesm2022/ngwr-split-text.mjs.map +1 -1
  331. package/fesm2022/ngwr-splitter-testing.mjs.map +1 -1
  332. package/fesm2022/ngwr-splitter.mjs +3 -3
  333. package/fesm2022/ngwr-splitter.mjs.map +1 -1
  334. package/fesm2022/ngwr-spotlight-card-testing.mjs.map +1 -1
  335. package/fesm2022/ngwr-spotlight-card.mjs +6 -6
  336. package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
  337. package/fesm2022/ngwr-squircle.mjs +6 -6
  338. package/fesm2022/ngwr-squircle.mjs.map +1 -1
  339. package/fesm2022/ngwr-star-border-testing.mjs.map +1 -1
  340. package/fesm2022/ngwr-star-border.mjs +3 -3
  341. package/fesm2022/ngwr-star-border.mjs.map +1 -1
  342. package/fesm2022/ngwr-statistic.mjs +9 -9
  343. package/fesm2022/ngwr-statistic.mjs.map +1 -1
  344. package/fesm2022/ngwr-stepper-testing.mjs.map +1 -1
  345. package/fesm2022/ngwr-stepper.mjs +6 -6
  346. package/fesm2022/ngwr-stepper.mjs.map +1 -1
  347. package/fesm2022/ngwr-storage.mjs +3 -3
  348. package/fesm2022/ngwr-storage.mjs.map +1 -1
  349. package/fesm2022/ngwr-switch-testing.mjs.map +1 -1
  350. package/fesm2022/ngwr-switch.mjs +3 -3
  351. package/fesm2022/ngwr-switch.mjs.map +1 -1
  352. package/fesm2022/ngwr-table-testing.mjs.map +1 -1
  353. package/fesm2022/ngwr-table.mjs +18 -18
  354. package/fesm2022/ngwr-table.mjs.map +1 -1
  355. package/fesm2022/ngwr-tabs-router.mjs +3 -3
  356. package/fesm2022/ngwr-tabs-router.mjs.map +1 -1
  357. package/fesm2022/ngwr-tabs-testing.mjs.map +1 -1
  358. package/fesm2022/ngwr-tabs.mjs +6 -6
  359. package/fesm2022/ngwr-tabs.mjs.map +1 -1
  360. package/fesm2022/ngwr-textarea-testing.mjs.map +1 -1
  361. package/fesm2022/ngwr-textarea.mjs +3 -3
  362. package/fesm2022/ngwr-textarea.mjs.map +1 -1
  363. package/fesm2022/ngwr-theme.mjs +3 -3
  364. package/fesm2022/ngwr-theme.mjs.map +1 -1
  365. package/fesm2022/ngwr-tilt-card-testing.mjs.map +1 -1
  366. package/fesm2022/ngwr-tilt-card.mjs +6 -6
  367. package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
  368. package/fesm2022/ngwr-timeline.mjs +6 -6
  369. package/fesm2022/ngwr-timeline.mjs.map +1 -1
  370. package/fesm2022/ngwr-toast-testing.mjs.map +1 -1
  371. package/fesm2022/ngwr-toast.mjs +9 -9
  372. package/fesm2022/ngwr-toast.mjs.map +1 -1
  373. package/fesm2022/ngwr-toolbar.mjs +10 -3
  374. package/fesm2022/ngwr-toolbar.mjs.map +1 -1
  375. package/fesm2022/ngwr-tour-testing.mjs.map +1 -1
  376. package/fesm2022/ngwr-tour.mjs +6 -6
  377. package/fesm2022/ngwr-tour.mjs.map +1 -1
  378. package/fesm2022/ngwr-transfer-testing.mjs.map +1 -1
  379. package/fesm2022/ngwr-transfer.mjs +3 -3
  380. package/fesm2022/ngwr-transfer.mjs.map +1 -1
  381. package/fesm2022/ngwr-tree-testing.mjs.map +1 -1
  382. package/fesm2022/ngwr-tree.mjs +3 -3
  383. package/fesm2022/ngwr-tree.mjs.map +1 -1
  384. package/fesm2022/ngwr-typewriter-testing.mjs.map +1 -1
  385. package/fesm2022/ngwr-typewriter.mjs +3 -3
  386. package/fesm2022/ngwr-typewriter.mjs.map +1 -1
  387. package/fesm2022/ngwr-typography.mjs +7 -3
  388. package/fesm2022/ngwr-typography.mjs.map +1 -1
  389. package/fesm2022/ngwr-utils.mjs.map +1 -1
  390. package/fesm2022/ngwr-validators.mjs.map +1 -1
  391. package/fesm2022/ngwr-version.mjs +1 -1
  392. package/fesm2022/ngwr-version.mjs.map +1 -1
  393. package/fesm2022/ngwr-virtual-scroll.mjs +3 -3
  394. package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -1
  395. package/fesm2022/ngwr-waves-testing.mjs.map +1 -1
  396. package/fesm2022/ngwr-waves.mjs +3 -3
  397. package/fesm2022/ngwr-waves.mjs.map +1 -1
  398. package/fesm2022/ngwr-window-testing.mjs.map +1 -1
  399. package/fesm2022/ngwr-window.mjs +12 -12
  400. package/fesm2022/ngwr-window.mjs.map +1 -1
  401. package/form/styles/_index.scss +16 -1
  402. package/gauge/styles/_index.scss +1 -0
  403. package/i18n/ar.json +32 -1
  404. package/i18n/cs.json +32 -1
  405. package/i18n/de.json +32 -1
  406. package/i18n/en.json +32 -1
  407. package/i18n/es.json +32 -1
  408. package/i18n/fr.json +32 -1
  409. package/i18n/he.json +32 -1
  410. package/i18n/hi.json +32 -1
  411. package/i18n/id.json +32 -1
  412. package/i18n/it.json +32 -1
  413. package/i18n/ja.json +32 -1
  414. package/i18n/ko.json +32 -1
  415. package/i18n/nl.json +32 -1
  416. package/i18n/pl.json +32 -1
  417. package/i18n/pt.json +32 -1
  418. package/i18n/ru.json +32 -1
  419. package/i18n/sv.json +32 -1
  420. package/i18n/tr.json +32 -1
  421. package/i18n/uk.json +32 -1
  422. package/i18n/vi.json +32 -1
  423. package/i18n/zh-TW.json +32 -1
  424. package/i18n/zh.json +32 -1
  425. package/input/styles/_index.scss +24 -1
  426. package/input-number/styles/_index.scss +69 -0
  427. package/input-otp/styles/_index.scss +5 -5
  428. package/line-chart/styles/_index.scss +30 -2
  429. package/llms-full.txt +14 -4
  430. package/llms.txt +188 -132
  431. package/mcp/providers.js +2 -2
  432. package/mcp/server.js +1 -1
  433. package/meter-group/styles/_index.scss +1 -0
  434. package/overlay/styles/_arrow.scss +30 -2
  435. package/package.json +43 -2
  436. package/page-header/styles/_index.scss +37 -1
  437. package/pagination/styles/_index.scss +9 -5
  438. package/popover/styles/_index.scss +73 -5
  439. package/progress/styles/_index.scss +4 -0
  440. package/radio/styles/_index.scss +4 -1
  441. package/schematics/use/symbol-map.json +1 -0
  442. package/select/styles/_index.scss +51 -12
  443. package/sidebar/styles/_index.scss +44 -2
  444. package/skeleton/styles/_index.scss +3 -0
  445. package/skills/ngwr/SKILL.md +5 -5
  446. package/skills/ngwr/references/catalog.md +2 -0
  447. package/skills/ngwr/references/setup.md +3 -3
  448. package/sparkline/styles/_index.scss +19 -0
  449. package/styles.scss +1 -0
  450. package/table/styles/_index.scss +7 -0
  451. package/textarea/styles/_index.scss +12 -3
  452. package/theme/styles/_colors.scss +39 -13
  453. package/theme/styles/_contrast.scss +9 -7
  454. package/toast/styles/_index.scss +4 -0
  455. package/toolbar/styles/_index.scss +35 -1
  456. package/tree/styles/_index.scss +46 -7
  457. package/types/ngwr-badge.d.ts +22 -7
  458. package/types/ngwr-bar-chart.d.ts +19 -1
  459. package/types/ngwr-calendar-heatmap.d.ts +29 -1
  460. package/types/ngwr-checkbox.d.ts +2 -0
  461. package/types/ngwr-config.d.ts +12 -0
  462. package/types/ngwr-date-picker.d.ts +308 -43
  463. package/types/ngwr-donut-chart.d.ts +24 -1
  464. package/types/ngwr-dropdown.d.ts +22 -2
  465. package/types/ngwr-editor-testing.d.ts +378 -0
  466. package/types/ngwr-editor.d.ts +449 -0
  467. package/types/ngwr-event-calendar.d.ts +8 -0
  468. package/types/ngwr-form.d.ts +35 -5
  469. package/types/ngwr-gauge.d.ts +20 -1
  470. package/types/ngwr-input-number.d.ts +7 -0
  471. package/types/ngwr-line-chart.d.ts +9 -1
  472. package/types/ngwr-markdown.d.ts +61 -2
  473. package/types/ngwr-meter-group-testing.d.ts +3 -2
  474. package/types/ngwr-meter-group.d.ts +14 -1
  475. package/types/ngwr-page-header.d.ts +10 -2
  476. package/types/ngwr-popover.d.ts +154 -3
  477. package/types/ngwr-sidebar.d.ts +7 -0
  478. package/types/ngwr-sparkline.d.ts +43 -1
  479. package/types/ngwr-toolbar.d.ts +7 -0
  480. package/types/ngwr-typography.d.ts +4 -0
  481. package/types/ngwr-version.d.ts +1 -1
  482. package/typography/styles/_index.scss +16 -0
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-icon.mjs","sources":["../../../projects/lib/icon/tokens/icon.token.ts","../../../projects/lib/icon/icon-registry.ts","../../../projects/lib/icon/utils/sanitize-icon.ts","../../../projects/lib/icon/utils/validate-icon.ts","../../../projects/lib/icon/icon.ts","../../../projects/lib/icon/providers/provide-wr-icons.ts","../../../projects/lib/icon/svg-icon.ts","../../../projects/lib/icon/ngwr-icon.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 { InjectionToken } from '@angular/core';\n\nimport type { WrIconDef } from '../interfaces';\n\n/**\n * Multi-provider token for registering icons with the icon registry.\n *\n * Use {@link provideWrIcons} instead of providing this token directly.\n *\n * @internal\n */\nexport const WR_ICONS = new InjectionToken<readonly WrIconDef[][]>('WR_ICONS');\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 { Injectable, inject } from '@angular/core';\n\nimport type { WrIconDef } from './interfaces';\nimport { WR_ICONS } from './tokens';\n\n/**\n * One link in the icon-registry chain. {@link provideWrIcons} provides an\n * instance alongside its icons, so every injector level that registers icons\n * gets its own link whose parent is the next registering level above it.\n *\n * This exists because Angular does not merge `multi: true` providers across\n * injector levels — an element-level `provideWrIcons()` *replaces* the visible\n * `WR_ICONS` collection rather than adding to it. Reading only the closest\n * contribution silently hid every icon registered further up, which contradicts\n * the \"each call adds to the registry\" contract. Walking the chain restores it\n * for any number of levels.\n *\n * @internal\n */\n@Injectable()\nexport class WrIconRegistry {\n /** The next registering level up, if any. */\n private readonly parent = inject(WrIconRegistry, { skipSelf: true, optional: true });\n\n /** Icons registered at *this* level only. */\n private readonly own = inject(WR_ICONS, { self: true, optional: true }) ?? [];\n\n /**\n * Every icon visible from this level, nearest last so a local registration\n * wins over an identically named one further up.\n */\n resolve(): Map<string, WrIconDef> {\n const map = this.parent?.resolve() ?? new Map<string, WrIconDef>();\n for (const set of this.own) {\n for (const icon of set) map.set(icon.name, icon);\n }\n return map;\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\nconst SVG_NS = 'http://www.w3.org/2000/svg';\nconst XLINK_NS = 'http://www.w3.org/1999/xlink';\n\n/**\n * SVG elements an icon may be built from.\n *\n * Derived from the real sets rather than guessed: across the 12 459 SVG files\n * under `icon/_svg` (Lucide, Phosphor, Tabler, Heroicons), the whole of\n * `feather-icons`' `icons.json` and every `lucide` IconNode, only eight\n * elements ever appear — `svg`, `path`, `circle`, `line`, `rect`, `polyline`,\n * `polygon`, `ellipse`. The rest of this list is headroom for the icons a\n * consumer draws themselves: grouping, `<defs>` with gradients, a clip path, a\n * mask, a `<title>`, real `<text>`.\n *\n * What is deliberately absent is the entire scripting surface:\n *\n * - `<script>` — obvious, and the one vector `innerHTML` already refused;\n * - `<foreignObject>` — an XHTML escape hatch, so `<img onerror>` inside an\n * otherwise well-formed icon;\n * - `<image>` — its `onerror` fires on any unreachable `href`, which is the\n * cheapest handler to land, and no real icon set uses it;\n * - `<a>` — a `javascript:` href inside a glyph the user is invited to click;\n * - `<animate>` / `<set>` / `<animateTransform>` / `<animateMotion>` — SMIL can\n * retarget another element's `href` at a `javascript:` URL;\n * - `<style>` — arbitrary CSS in the app's origin, including `url()` requests;\n * - `<filter>` and the `fe*` primitives — `<feImage href>` is another external\n * fetch, and no icon set in the corpus filters.\n *\n * @internal\n */\nconst ELEMENTS: ReadonlySet<string> = new Set([\n 'svg',\n 'g',\n 'defs',\n 'symbol',\n 'use',\n 'title',\n 'desc',\n 'path',\n 'circle',\n 'ellipse',\n 'line',\n 'polyline',\n 'polygon',\n 'rect',\n 'text',\n 'tspan',\n 'textPath',\n 'linearGradient',\n 'radialGradient',\n 'stop',\n 'clipPath',\n 'mask',\n 'pattern',\n]);\n\n/**\n * Attributes an icon may carry, allowed on every element it is allowed on.\n *\n * An allowlist rather than a blocklist, so `onload`, `onerror`, `onclick` and\n * every other handler — including ones no browser has shipped yet — are gone by\n * construction rather than by enumeration.\n *\n * `style` is NOT here, and that is a decision rather than an oversight: not one\n * icon in the corpus uses it, and a `style` attribute is arbitrary CSS in the\n * app's origin. Anything a `style` would have done, a presentation attribute\n * does.\n *\n * @internal\n */\nconst ATTRIBUTES: ReadonlySet<string> = new Set([\n // geometry\n 'viewBox',\n 'preserveAspectRatio',\n 'transform',\n 'transform-origin',\n 'd',\n 'pathLength',\n 'cx',\n 'cy',\n 'r',\n 'rx',\n 'ry',\n 'x',\n 'y',\n 'x1',\n 'y1',\n 'x2',\n 'y2',\n 'dx',\n 'dy',\n 'width',\n 'height',\n 'points',\n // paint servers, clips and masks\n 'gradientUnits',\n 'gradientTransform',\n 'spreadMethod',\n 'offset',\n 'fx',\n 'fy',\n 'fr',\n 'patternUnits',\n 'patternContentUnits',\n 'patternTransform',\n 'maskUnits',\n 'maskContentUnits',\n 'clipPathUnits',\n // presentation\n 'fill',\n 'fill-opacity',\n 'fill-rule',\n 'stroke',\n 'stroke-width',\n 'stroke-linecap',\n 'stroke-linejoin',\n 'stroke-miterlimit',\n 'stroke-dasharray',\n 'stroke-dashoffset',\n 'stroke-opacity',\n 'opacity',\n 'color',\n 'clip-path',\n 'clip-rule',\n 'mask',\n 'paint-order',\n 'vector-effect',\n 'shape-rendering',\n 'stop-color',\n 'stop-opacity',\n 'display',\n 'visibility',\n 'overflow',\n 'isolation',\n 'mix-blend-mode',\n // text\n 'startOffset',\n 'textLength',\n 'lengthAdjust',\n 'text-anchor',\n 'dominant-baseline',\n 'alignment-baseline',\n 'baseline-shift',\n 'font-family',\n 'font-size',\n 'font-stretch',\n 'font-style',\n 'font-variant',\n 'font-weight',\n 'letter-spacing',\n 'word-spacing',\n 'writing-mode',\n 'direction',\n // identity, accessibility and document plumbing\n 'id',\n 'class',\n 'role',\n 'aria-hidden',\n 'aria-label',\n 'aria-labelledby',\n 'aria-describedby',\n 'aria-roledescription',\n 'focusable',\n 'lang',\n 'version',\n 'baseProfile',\n 'xmlns',\n 'xmlns:xlink',\n 'xml:space',\n 'xml:lang',\n // resolved against ONLY_FRAGMENT below, never free-form\n 'href',\n 'xlink:href',\n]);\n\n/**\n * `href` may point at a fragment in the same document and nowhere else — that\n * is the whole legitimate use inside an icon (`<use href=\"#glyph\">`), and it\n * excludes `javascript:`, `data:` and every remote fetch in one rule.\n */\nconst FRAGMENT_ONLY: ReadonlySet<string> = new Set(['href', 'xlink:href']);\n\n/** A same-document fragment reference: `#id`, and nothing that is not one. */\nconst FRAGMENT_RE = /^#[^\\s]+$/;\n\n/**\n * `url(` inside a presentation attribute is a reference to a paint server, a\n * clip path or a mask. Those are fragment references too; a remote one is at\n * best a request the page did not ask for.\n */\nconst URL_FN_RE = /url\\(/i;\nconst URL_FRAGMENT_RE = /^url\\(\\s*(['\"]?)#[^\\s'\")]+\\1\\s*\\)$/i;\n\n/**\n * Icons are shallow — the deepest in the corpus nests three levels. The cap is\n * there so a hostile registration cannot recurse this function into a stack\n * overflow; content below it is dropped, not truncated silently, because\n * {@link WrIconStripReport} names what went.\n */\nconst MAX_DEPTH = 16;\n\n/**\n * What {@link sanitizeIcon} refused, for the dev-mode warning. Collecting is\n * opt-in so the render path does no bookkeeping in production.\n *\n * @internal\n */\ninterface WrIconStripReport {\n readonly elements: Set<string>;\n readonly attributes: Set<string>;\n}\n\n/**\n * Build a live `<svg>` element out of a registered icon's markup, keeping only\n * what an icon legitimately needs.\n *\n * `<wr-icon>` used to assign `icon.data` to `innerHTML`, which executes an\n * `onload` on the root, an `<image onerror>`, an `<img>` smuggled in after a\n * closed `</svg>`, and anything inside a `<foreignObject>` — with the app's own\n * origin, in a production build. The library already refuses exactly this next\n * door: `<wr-markdown>` escapes raw HTML because its input is untrusted by\n * construction and one `<img onerror>` is the whole cost of being wrong. Icon\n * data arrives from `provideWrIcons()`, which a multi-tenant app or a fetched\n * icon pack fills at runtime, so it is untrusted the same way.\n *\n * Three properties do the work, and none of them is a filter over known-bad\n * strings:\n *\n * 1. **Parsed inert, never inserted.** The markup is parsed into a document\n * with no browsing context, so nothing loads and nothing runs even before it\n * is inspected.\n * 2. **Only the first `<svg>` survives.** An icon IS an SVG element, so\n * everything beside the root is dropped — which is what makes\n * `</svg><img src=x onerror=…>` inert regardless of what the payload says,\n * and it is the same shape as the `feather()` shell escape.\n * 3. **Rebuilt, not filtered.** Every element and attribute that lands in the\n * document is one this file created from an allowlist. Nothing is\n * transferred from the parsed tree, so there is no attribute an unfamiliar\n * payload can smuggle past a matcher.\n *\n * A refused element takes its subtree with it rather than having its children\n * hoisted into its parent. Hoisting would preserve more of a hand-drawn icon\n * wrapped in something unsupported, and it is exactly the kind of cleverness\n * that makes \"what does the output contain\" depend on what the parser saw. The\n * blunt rule is the reviewable one, and the dev-mode warning names what went.\n *\n * `DomSanitizer.sanitize(SecurityContext.HTML, …)` is not an option here, which\n * is worth recording so it is not re-proposed: Angular's HTML allowlist\n * (`VOID_ELEMENTS`, `BLOCK_ELEMENTS`, `INLINE_ELEMENTS`) contains no SVG\n * element at all. Run against real Lucide, Feather and Heroicons icons it does\n * not merely drop presentation attributes — it returns the empty string.\n *\n * @returns the rebuilt element, or `null` when the data holds no `<svg>` root.\n * @internal\n */\nfunction sanitizeIcon(doc: Document, data: string, report?: WrIconStripReport): SVGElement | null {\n const source = parseInert(doc, data);\n return source ? copy(doc, source, report, 0) : null;\n}\n\n/**\n * Parse into a document that cannot run anything.\n *\n * `DOMParser` is the path every browser and the unit-test DOM take: it takes a\n * plain string, so it is not a Trusted Types sink, and its result has no\n * browsing context. It does not exist under SSR — `@angular/platform-server`\n * renders into domino — so the server falls back to a detached `<template>`,\n * whose content is a separate inert document for the same reason. That branch\n * is unreachable in a browser, which is what keeps the innerHTML write out of\n * the only place Trusted Types is enforced.\n *\n * Both parsers were checked to agree on the part that matters: the HTML\n * parser's foreign-content adjustment restores the camel case of `viewBox`,\n * `clipPath`, `linearGradient`, `gradientTransform`, `textPath` and puts\n * `xlink:href` in the xlink namespace.\n */\nfunction parseInert(doc: Document, data: string): Element | null {\n const parser = doc.defaultView?.DOMParser;\n if (parser) return firstSvg(new parser().parseFromString(data, 'text/html').body);\n\n const template = doc.createElement('template');\n template.innerHTML = data;\n return firstSvg(template.content);\n}\n\n/**\n * The first `<svg>` in document order, found by walking rather than by\n * `querySelector`.\n *\n * Two reasons, and the second is the one that matters. A control that decides\n * what may execute should not route that decision through a selector engine —\n * `localName === 'svg'` is a comparison anyone can check, and CSS parsing is a\n * layer with its own history. The first reason is merely that it is faster:\n * jsdom's engine costs 4 ms on a document this size, which the unit suite pays\n * on every rendered icon.\n */\nfunction firstSvg(root: Node): Element | null {\n const children = root.childNodes;\n for (let i = 0, n = children.length; i < n; i++) {\n const child = children[i];\n if (child.nodeType !== 1) continue;\n const element = child as Element;\n if (element.localName === 'svg') return element;\n const nested = firstSvg(element);\n if (nested) return nested;\n }\n return null;\n}\n\n/** Recreate one element, then its allowed children, in the live document. */\nfunction copy(doc: Document, source: Element, report: WrIconStripReport | undefined, depth: number): SVGElement {\n const el = doc.createElementNS(SVG_NS, source.localName);\n\n const attributes = source.attributes;\n for (let i = 0, n = attributes.length; i < n; i++) {\n const { name, value } = attributes[i];\n if (!isAllowedAttribute(name, value)) {\n report?.attributes.add(name);\n continue;\n }\n if (name === 'xlink:href') el.setAttributeNS(XLINK_NS, name, value);\n else el.setAttribute(name, value);\n }\n\n if (depth >= MAX_DEPTH) {\n // Named, not silent — the cap is the one rule here that can bite an icon\n // nobody meant as an attack, and a truncated glyph should say so.\n if (source.childNodes.length > 0) report?.elements.add(`… deeper than ${MAX_DEPTH} levels`);\n return el;\n }\n\n const children = source.childNodes;\n for (let i = 0, n = children.length; i < n; i++) {\n const child = children[i];\n // Text, so `<title>`, `<desc>` and `<text>` keep saying what they said.\n // Comments, CDATA and processing instructions are dropped: an icon has no\n // use for them and a comment is a place to hide a payload from a reader.\n if (child.nodeType === 3) {\n el.appendChild(doc.createTextNode(child.nodeValue ?? ''));\n continue;\n }\n if (child.nodeType !== 1) continue;\n\n const element = child as Element;\n if (!ELEMENTS.has(element.localName)) {\n report?.elements.add(element.localName);\n continue;\n }\n el.appendChild(copy(doc, element, report, depth + 1));\n }\n\n return el;\n}\n\nfunction isAllowedAttribute(name: string, value: string): boolean {\n // `data-*` is a styling and testing hook that can never be behavioural.\n if (name.startsWith('data-')) return true;\n if (!ATTRIBUTES.has(name)) return false;\n if (FRAGMENT_ONLY.has(name)) return FRAGMENT_RE.test(value.trim());\n if (URL_FN_RE.test(value)) return URL_FRAGMENT_RE.test(value.trim());\n return true;\n}\n\nexport { sanitizeIcon, type WrIconStripReport };\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 type { WrIconDef } from '../interfaces';\n\nconst VIEW_BOX_RE = /<svg\\b[^>]*\\bviewBox\\s*=/i;\nconst SVG_ROOT_RE = /<svg\\b/i;\n\n/**\n * Dev-mode sanity check for a registered icon — a **rendering** check, and\n * explicitly not a security control.\n *\n * It warns about the two things that make an icon come out wrong: no `<svg>`\n * root, and no `viewBox`, which stops the glyph scaling to the host. It reads\n * the string with two regexes and cannot tell a shape from a script — every\n * payload in the icon corpus passes it, because each one does carry an `<svg>`\n * root with a `viewBox`. It also runs only under `isDevMode()`, so it is absent\n * from the build an attacker would meet.\n *\n * What actually keeps hostile markup out of the DOM is {@link sanitizeIcon},\n * which runs on every render in every build: `<wr-icon>` rebuilds the glyph\n * from an allowlist instead of assigning to `innerHTML`. Anything this function\n * would have to reject is already gone by then, and the component says so in\n * dev mode when it drops something.\n *\n * Production builds drop the call entirely via `isDevMode()` tree-shaking.\n *\n * @internal\n */\nexport function validateIcon(icon: WrIconDef): void {\n if (!SVG_ROOT_RE.test(icon.data)) {\n // eslint-disable-next-line no-console -- dev-mode validation\n console.warn(\n `[NGWR] Icon \"${icon.name}\" data does not contain an <svg> root element. ` +\n `Custom icons must be valid SVG markup.`\n );\n return;\n }\n\n if (!VIEW_BOX_RE.test(icon.data)) {\n // eslint-disable-next-line no-console -- dev-mode validation\n console.warn(\n `[NGWR] Icon \"${icon.name}\" has no viewBox attribute — it won't scale ` +\n `to the host size. Add viewBox=\"0 0 W H\" to the <svg> root.`\n );\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { DOCUMENT } from '@angular/common';\nimport { Component, ElementRef, ViewEncapsulation, computed, effect, inject, input, isDevMode } from '@angular/core';\n\nimport { WrIconRegistry } from './icon-registry';\nimport type { WrIconDef, WrIconName } from './interfaces';\nimport { type WrIconStripReport, sanitizeIcon } from './utils';\n\n/**\n * Renders a registered SVG icon.\n *\n * Icons must be registered via {@link provideWrIcons} before use —\n * either at the application root or on any ancestor component.\n *\n * @example\n * ```html\n * <wr-icon name=\"home\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/icon\n */\n@Component({\n selector: 'wr-icon',\n template: '',\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-icon',\n '[attr.data-icon]': 'name()',\n },\n})\nexport class WrIcon {\n readonly name = input.required<WrIconName>();\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly doc = inject(DOCUMENT);\n\n /**\n * Every icon visible from this position, walked up the whole chain of\n * registering injector levels.\n *\n * Angular's `inject(WR_ICONS)` returns only the **closest** injector's\n * contribution — `multi` providers are not merged across levels — so a\n * component that registers its own icons used to shadow, not extend, what an\n * ancestor registered. {@link WrIconRegistry} is provided once per\n * registering level and links to its parent, so nesting any number of levels\n * now adds up, with the nearest registration winning a name collision.\n *\n * Optional: with no `provideWrIcons()` anywhere the map is simply empty and\n * every name reports as unregistered.\n */\n private readonly iconRegistry = inject(WrIconRegistry, { optional: true });\n\n private readonly registry = computed<ReadonlyMap<WrIconName, WrIconDef>>(\n () => (this.iconRegistry?.resolve() ?? new Map()) as ReadonlyMap<WrIconName, WrIconDef>\n );\n\n private readonly icon = computed(() => this.registry().get(this.name()));\n\n constructor() {\n effect(() => {\n const icon = this.icon();\n const name = this.name();\n\n if (!icon) {\n if (isDevMode()) {\n // Reported, never thrown. An exception here escapes into\n // `runEffectsInView`, which abandons the remaining effects of the\n // whole view — one unknown name would blank every `<wr-icon>` after\n // it while the console blamed only the first. Error level (not\n // `badgeLog`) so it survives a console filtered to errors.\n // eslint-disable-next-line no-console -- misconfiguration must be visible, but must not throw\n console.error(\n `[NGWR] No icon named \"${name}\" is registered. Did you forget to call provideWrIcons()? ` +\n `If the name IS registered, check the injector level: a component-level provideWrIcons() ` +\n `shadows icons registered on an ancestor component.`\n );\n }\n this.clear();\n return;\n }\n\n // Rebuilt from an allowlist rather than assigned to `innerHTML`, which\n // ran an `onload` on the root, an `<image onerror>`, an `<img>` smuggled\n // in after a closed `</svg>` and anything inside a `<foreignObject>` —\n // in production builds, with the app's own origin. `provideWrIcons()`\n // data is untrusted the same way `<wr-markdown>`'s input is: a tenant\n // logo or a fetched icon pack is a normal way to fill it. Building nodes\n // also keeps `<wr-icon>` clear of Trusted Types, which refuses the\n // `innerHTML` write outright.\n const report = isDevMode() ? { elements: new Set<string>(), attributes: new Set<string>() } : undefined;\n const svg = sanitizeIcon(this.doc, icon.data, report);\n\n if (!svg) {\n if (isDevMode()) {\n // eslint-disable-next-line no-console -- misconfiguration must be visible, but must not throw\n console.error(\n `[NGWR] Icon \"${name}\" has no <svg> root element and cannot be rendered. ` +\n `Custom icons must be valid SVG markup.`\n );\n }\n this.clear();\n return;\n }\n\n // Every hand-written inline SVG in the library carries aria-hidden;\n // registered icons went in without it, so a decorative glyph could be\n // announced — usually as a bare \"graphic\" beside the label it decorates.\n // Consumers that need a name put it on the interactive host instead.\n svg.setAttribute('aria-hidden', 'true');\n\n this.clear();\n this.host.nativeElement.appendChild(svg);\n\n if (report) this.reportStripped(name, icon, report);\n });\n }\n\n /**\n * `replaceChildren()` would say this in one call, but domino — the DOM\n * `@angular/platform-server` renders into — does not implement it, and\n * `innerHTML = ''` is the write Trusted Types refuses.\n */\n private clear(): void {\n const el = this.host.nativeElement;\n while (el.firstChild) el.removeChild(el.firstChild);\n }\n\n /**\n * Say what the allowlist removed, once per registered icon.\n *\n * A silently stripped icon is the failure mode this rebuild could introduce:\n * a consumer's own glyph renders blank or wrong and nothing anywhere says\n * why. Keyed on the definition object, so a page with fifty instances of the\n * same icon warns once and an icon nobody renders never warns at all.\n */\n private reportStripped(name: string, icon: WrIconDef, report: WrIconStripReport): void {\n if (WrIcon.reported.has(icon)) return;\n if (report.elements.size === 0 && report.attributes.size === 0) return;\n WrIcon.reported.add(icon);\n\n const removed = [\n report.elements.size ? `elements: ${[...report.elements].join(', ')}` : '',\n report.attributes.size ? `attributes: ${[...report.attributes].join(', ')}` : '',\n ].filter(Boolean);\n\n // eslint-disable-next-line no-console -- a stripped icon must not be silent\n console.warn(\n `[NGWR] Icon \"${name}\" was rendered without part of its markup — ${removed.join('; ')}. ` +\n `Registered icon data is treated as untrusted and rebuilt from an allowlist of SVG ` +\n `shape, paint and text elements, so event handlers, <script>, <style>, <image>, ` +\n `<foreignObject>, <a>, SMIL animation and non-fragment hrefs never reach the DOM.`\n );\n }\n\n private static readonly reported = new WeakSet<WrIconDef>();\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 { type Provider, isDevMode } from '@angular/core';\n\nimport { WrIconRegistry } from '../icon-registry';\nimport type { WrIconDef } from '../interfaces';\nimport { WR_ICONS } from '../tokens';\nimport { validateIcon } from '../utils';\n\n/**\n * Registers a set of icons for use by `<wr-icon>`.\n *\n * Can be called multiple times — each call adds to the registry.\n * Works at both the application root and at the component level,\n * so a feature module or a single component can bring its own icons\n * without polluting the global config.\n *\n * In dev mode, each icon is validated for common issues (missing\n * `viewBox`, malformed root). Validation is dropped from production\n * builds via `isDevMode()` tree-shaking, and it is a rendering check rather\n * than a security one — see {@link validateIcon}.\n *\n * Icon `data` is treated as **untrusted** at render: `<wr-icon>` rebuilds the\n * glyph from an allowlist instead of writing the string to `innerHTML`, so a\n * set fetched at runtime or supplied by a tenant cannot execute in the app's\n * origin. Dev mode names anything the allowlist removed.\n *\n * @example\n * ```ts\n * // App-level registration\n * import { home, user, cog, provideWrIcons } from 'ngwr/icon';\n *\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideWrIcons([home, user, cog]),\n * ],\n * };\n * ```\n *\n * @example\n * ```ts\n * // Component-level registration\n * import { logoGithub, logoNpm, provideWrIcons, WrIconDef } from 'ngwr/icon';\n *\n * @Component({\n * selector: 'ngwr-header',\n * imports: [WrIcon],\n * providers: [provideWrIcons([logoGithub, logoNpm])],\n * template: `<wr-icon name=\"logo-github\" />`,\n * })\n * export class HeaderComponent {}\n * ```\n */\nexport function provideWrIcons(icons: WrIconDef[]): Provider[] {\n if (isDevMode()) {\n for (const icon of icons) validateIcon(icon);\n }\n return [\n { provide: WR_ICONS, useValue: icons, multi: true },\n // A registry link per registering level. Angular does not merge `multi`\n // providers across injectors, so without this chain an element-level\n // registration would shadow everything registered above it instead of\n // adding to it.\n WrIconRegistry,\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 type { WrIconDef } from './interfaces';\n\n/**\n * Wrap a raw SVG string in the {@link WrIconDef} envelope. Use this when\n * importing icons from sources that ship full `<svg>` files — Tabler,\n * Phosphor, Heroicons, Iconoir, Radix, Bootstrap Icons, your designer.\n *\n * Pair with a bundler's raw-SVG import (Vite's `?raw`, Webpack's\n * `raw-loader`, esbuild's `--loader:.svg=text`) so the SVG file becomes\n * a string at build time.\n *\n * For sets with a non-SVG shape (Lucide's IconNode tuples, Feather's\n * inner-only SVG), use the dedicated adapter under\n * `ngwr/icon/adapters/<set>` instead.\n *\n * @example\n * ```ts\n * import { svgIcon, provideWrIcons } from 'ngwr/icon';\n * // Vite: the `?raw` suffix returns the file as a string.\n * import plusSvg from '@tabler/icons/icons/plus.svg?raw';\n * import phosphorPlusSvg from '@phosphor-icons/core/assets/regular/plus.svg?raw';\n *\n * bootstrapApplication(AppComponent, {\n * providers: [\n * provideWrIcons([\n * svgIcon('plus', plusSvg),\n * svgIcon('plus-phosphor', phosphorPlusSvg),\n * ]),\n * ],\n * });\n * ```\n *\n * **`svg` is treated as untrusted.** It is a string, so it is a natural place\n * for a tenant's uploaded logo or an icon pack fetched from an API to end up,\n * and `<wr-icon>` used to write it to `innerHTML`. It no longer does: the glyph\n * is rebuilt from an allowlist of SVG shape, paint and text elements, so\n * `<script>`, `<style>`, `<image>`, `<foreignObject>`, `<a>`, SMIL animation,\n * every `on*` handler, `style` attributes and non-fragment `href`s are dropped\n * before anything reaches the DOM. In dev mode the component names what it\n * removed, so a legitimate icon that loses something says so out loud.\n */\nexport function svgIcon(name: string, svg: string): WrIconDef {\n return { name, data: svg };\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-icon.mjs","sources":["../../../projects/lib/icon/tokens/icon.token.ts","../../../projects/lib/icon/icon-registry.ts","../../../projects/lib/icon/utils/sanitize-icon.ts","../../../projects/lib/icon/utils/validate-icon.ts","../../../projects/lib/icon/icon.ts","../../../projects/lib/icon/providers/provide-wr-icons.ts","../../../projects/lib/icon/svg-icon.ts","../../../projects/lib/icon/ngwr-icon.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 { InjectionToken } from '@angular/core';\n\nimport type { WrIconDef } from '../interfaces';\n\n/**\n * Multi-provider token for registering icons with the icon registry.\n *\n * Use {@link provideWrIcons} instead of providing this token directly.\n *\n * @internal\n */\nexport const WR_ICONS = new InjectionToken<readonly WrIconDef[][]>('WR_ICONS');\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 { Injectable, inject } from '@angular/core';\n\nimport type { WrIconDef } from './interfaces';\nimport { WR_ICONS } from './tokens';\n\n/**\n * One link in the icon-registry chain. {@link provideWrIcons} provides an\n * instance alongside its icons, so every injector level that registers icons\n * gets its own link whose parent is the next registering level above it.\n *\n * This exists because Angular does not merge `multi: true` providers across\n * injector levels — an element-level `provideWrIcons()` *replaces* the visible\n * `WR_ICONS` collection rather than adding to it. Reading only the closest\n * contribution silently hid every icon registered further up, which contradicts\n * the \"each call adds to the registry\" contract. Walking the chain restores it\n * for any number of levels.\n *\n * @internal\n */\n@Injectable()\nexport class WrIconRegistry {\n /** The next registering level up, if any. */\n private readonly parent = inject(WrIconRegistry, { skipSelf: true, optional: true });\n\n /** Icons registered at *this* level only. */\n private readonly own = inject(WR_ICONS, { self: true, optional: true }) ?? [];\n\n /**\n * Every icon visible from this level, nearest last so a local registration\n * wins over an identically named one further up.\n */\n resolve(): Map<string, WrIconDef> {\n const map = this.parent?.resolve() ?? new Map<string, WrIconDef>();\n for (const set of this.own) {\n for (const icon of set) map.set(icon.name, icon);\n }\n return map;\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\nconst SVG_NS = 'http://www.w3.org/2000/svg';\nconst XLINK_NS = 'http://www.w3.org/1999/xlink';\n\n/**\n * SVG elements an icon may be built from.\n *\n * Derived from the real sets rather than guessed: across the 12 459 SVG files\n * under `icon/_svg` (Lucide, Phosphor, Tabler, Heroicons), the whole of\n * `feather-icons`' `icons.json` and every `lucide` IconNode, only eight\n * elements ever appear — `svg`, `path`, `circle`, `line`, `rect`, `polyline`,\n * `polygon`, `ellipse`. The rest of this list is headroom for the icons a\n * consumer draws themselves: grouping, `<defs>` with gradients, a clip path, a\n * mask, a `<title>`, real `<text>`.\n *\n * What is deliberately absent is the entire scripting surface:\n *\n * - `<script>` — obvious, and the one vector `innerHTML` already refused;\n * - `<foreignObject>` — an XHTML escape hatch, so `<img onerror>` inside an\n * otherwise well-formed icon;\n * - `<image>` — its `onerror` fires on any unreachable `href`, which is the\n * cheapest handler to land, and no real icon set uses it;\n * - `<a>` — a `javascript:` href inside a glyph the user is invited to click;\n * - `<animate>` / `<set>` / `<animateTransform>` / `<animateMotion>` — SMIL can\n * retarget another element's `href` at a `javascript:` URL;\n * - `<style>` — arbitrary CSS in the app's origin, including `url()` requests;\n * - `<filter>` and the `fe*` primitives — `<feImage href>` is another external\n * fetch, and no icon set in the corpus filters.\n *\n * @internal\n */\nconst ELEMENTS: ReadonlySet<string> = new Set([\n 'svg',\n 'g',\n 'defs',\n 'symbol',\n 'use',\n 'title',\n 'desc',\n 'path',\n 'circle',\n 'ellipse',\n 'line',\n 'polyline',\n 'polygon',\n 'rect',\n 'text',\n 'tspan',\n 'textPath',\n 'linearGradient',\n 'radialGradient',\n 'stop',\n 'clipPath',\n 'mask',\n 'pattern',\n]);\n\n/**\n * Attributes an icon may carry, allowed on every element it is allowed on.\n *\n * An allowlist rather than a blocklist, so `onload`, `onerror`, `onclick` and\n * every other handler — including ones no browser has shipped yet — are gone by\n * construction rather than by enumeration.\n *\n * `style` is NOT here, and that is a decision rather than an oversight: not one\n * icon in the corpus uses it, and a `style` attribute is arbitrary CSS in the\n * app's origin. Anything a `style` would have done, a presentation attribute\n * does.\n *\n * @internal\n */\nconst ATTRIBUTES: ReadonlySet<string> = new Set([\n // geometry\n 'viewBox',\n 'preserveAspectRatio',\n 'transform',\n 'transform-origin',\n 'd',\n 'pathLength',\n 'cx',\n 'cy',\n 'r',\n 'rx',\n 'ry',\n 'x',\n 'y',\n 'x1',\n 'y1',\n 'x2',\n 'y2',\n 'dx',\n 'dy',\n 'width',\n 'height',\n 'points',\n // paint servers, clips and masks\n 'gradientUnits',\n 'gradientTransform',\n 'spreadMethod',\n 'offset',\n 'fx',\n 'fy',\n 'fr',\n 'patternUnits',\n 'patternContentUnits',\n 'patternTransform',\n 'maskUnits',\n 'maskContentUnits',\n 'clipPathUnits',\n // presentation\n 'fill',\n 'fill-opacity',\n 'fill-rule',\n 'stroke',\n 'stroke-width',\n 'stroke-linecap',\n 'stroke-linejoin',\n 'stroke-miterlimit',\n 'stroke-dasharray',\n 'stroke-dashoffset',\n 'stroke-opacity',\n 'opacity',\n 'color',\n 'clip-path',\n 'clip-rule',\n 'mask',\n 'paint-order',\n 'vector-effect',\n 'shape-rendering',\n 'stop-color',\n 'stop-opacity',\n 'display',\n 'visibility',\n 'overflow',\n 'isolation',\n 'mix-blend-mode',\n // text\n 'startOffset',\n 'textLength',\n 'lengthAdjust',\n 'text-anchor',\n 'dominant-baseline',\n 'alignment-baseline',\n 'baseline-shift',\n 'font-family',\n 'font-size',\n 'font-stretch',\n 'font-style',\n 'font-variant',\n 'font-weight',\n 'letter-spacing',\n 'word-spacing',\n 'writing-mode',\n 'direction',\n // identity, accessibility and document plumbing\n 'id',\n 'class',\n 'role',\n 'aria-hidden',\n 'aria-label',\n 'aria-labelledby',\n 'aria-describedby',\n 'aria-roledescription',\n 'focusable',\n 'lang',\n 'version',\n 'baseProfile',\n 'xmlns',\n 'xmlns:xlink',\n 'xml:space',\n 'xml:lang',\n // resolved against ONLY_FRAGMENT below, never free-form\n 'href',\n 'xlink:href',\n]);\n\n/**\n * `href` may point at a fragment in the same document and nowhere else — that\n * is the whole legitimate use inside an icon (`<use href=\"#glyph\">`), and it\n * excludes `javascript:`, `data:` and every remote fetch in one rule.\n */\nconst FRAGMENT_ONLY: ReadonlySet<string> = new Set(['href', 'xlink:href']);\n\n/** A same-document fragment reference: `#id`, and nothing that is not one. */\nconst FRAGMENT_RE = /^#[^\\s]+$/;\n\n/**\n * `url(` inside a presentation attribute is a reference to a paint server, a\n * clip path or a mask. Those are fragment references too; a remote one is at\n * best a request the page did not ask for.\n */\nconst URL_FN_RE = /url\\(/i;\nconst URL_FRAGMENT_RE = /^url\\(\\s*(['\"]?)#[^\\s'\")]+\\1\\s*\\)$/i;\n\n/**\n * Icons are shallow — the deepest in the corpus nests three levels. The cap is\n * there so a hostile registration cannot recurse this function into a stack\n * overflow; content below it is dropped, not truncated silently, because\n * {@link WrIconStripReport} names what went.\n */\nconst MAX_DEPTH = 16;\n\n/**\n * What {@link sanitizeIcon} refused, for the dev-mode warning. Collecting is\n * opt-in so the render path does no bookkeeping in production.\n *\n * @internal\n */\ninterface WrIconStripReport {\n readonly elements: Set<string>;\n readonly attributes: Set<string>;\n}\n\n/**\n * Build a live `<svg>` element out of a registered icon's markup, keeping only\n * what an icon legitimately needs.\n *\n * `<wr-icon>` used to assign `icon.data` to `innerHTML`, which executes an\n * `onload` on the root, an `<image onerror>`, an `<img>` smuggled in after a\n * closed `</svg>`, and anything inside a `<foreignObject>` — with the app's own\n * origin, in a production build. The library already refuses exactly this next\n * door: `<wr-markdown>` escapes raw HTML because its input is untrusted by\n * construction and one `<img onerror>` is the whole cost of being wrong. Icon\n * data arrives from `provideWrIcons()`, which a multi-tenant app or a fetched\n * icon pack fills at runtime, so it is untrusted the same way.\n *\n * Three properties do the work, and none of them is a filter over known-bad\n * strings:\n *\n * 1. **Parsed inert, never inserted.** The markup is parsed into a document\n * with no browsing context, so nothing loads and nothing runs even before it\n * is inspected.\n * 2. **Only the first `<svg>` survives.** An icon IS an SVG element, so\n * everything beside the root is dropped — which is what makes\n * `</svg><img src=x onerror=…>` inert regardless of what the payload says,\n * and it is the same shape as the `feather()` shell escape.\n * 3. **Rebuilt, not filtered.** Every element and attribute that lands in the\n * document is one this file created from an allowlist. Nothing is\n * transferred from the parsed tree, so there is no attribute an unfamiliar\n * payload can smuggle past a matcher.\n *\n * A refused element takes its subtree with it rather than having its children\n * hoisted into its parent. Hoisting would preserve more of a hand-drawn icon\n * wrapped in something unsupported, and it is exactly the kind of cleverness\n * that makes \"what does the output contain\" depend on what the parser saw. The\n * blunt rule is the reviewable one, and the dev-mode warning names what went.\n *\n * `DomSanitizer.sanitize(SecurityContext.HTML, …)` is not an option here, which\n * is worth recording so it is not re-proposed: Angular's HTML allowlist\n * (`VOID_ELEMENTS`, `BLOCK_ELEMENTS`, `INLINE_ELEMENTS`) contains no SVG\n * element at all. Run against real Lucide, Feather and Heroicons icons it does\n * not merely drop presentation attributes — it returns the empty string.\n *\n * @returns the rebuilt element, or `null` when the data holds no `<svg>` root.\n * @internal\n */\nfunction sanitizeIcon(doc: Document, data: string, report?: WrIconStripReport): SVGElement | null {\n const source = parseInert(doc, data);\n return source ? copy(doc, source, report, 0) : null;\n}\n\n/**\n * Parse into a document that cannot run anything.\n *\n * `DOMParser` is the path every browser and the unit-test DOM take: it takes a\n * plain string, so it is not a Trusted Types sink, and its result has no\n * browsing context. It does not exist under SSR — `@angular/platform-server`\n * renders into domino — so the server falls back to a detached `<template>`,\n * whose content is a separate inert document for the same reason. That branch\n * is unreachable in a browser, which is what keeps the innerHTML write out of\n * the only place Trusted Types is enforced.\n *\n * Both parsers were checked to agree on the part that matters: the HTML\n * parser's foreign-content adjustment restores the camel case of `viewBox`,\n * `clipPath`, `linearGradient`, `gradientTransform`, `textPath` and puts\n * `xlink:href` in the xlink namespace.\n */\nfunction parseInert(doc: Document, data: string): Element | null {\n const parser = doc.defaultView?.DOMParser;\n if (parser) return firstSvg(new parser().parseFromString(data, 'text/html').body);\n\n const template = doc.createElement('template');\n template.innerHTML = data;\n return firstSvg(template.content);\n}\n\n/**\n * The first `<svg>` in document order, found by walking rather than by\n * `querySelector`.\n *\n * Two reasons, and the second is the one that matters. A control that decides\n * what may execute should not route that decision through a selector engine —\n * `localName === 'svg'` is a comparison anyone can check, and CSS parsing is a\n * layer with its own history. The first reason is merely that it is faster:\n * jsdom's engine costs 4 ms on a document this size, which the unit suite pays\n * on every rendered icon.\n */\nfunction firstSvg(root: Node): Element | null {\n const children = root.childNodes;\n for (let i = 0, n = children.length; i < n; i++) {\n const child = children[i];\n if (child.nodeType !== 1) continue;\n const element = child as Element;\n if (element.localName === 'svg') return element;\n const nested = firstSvg(element);\n if (nested) return nested;\n }\n return null;\n}\n\n/** Recreate one element, then its allowed children, in the live document. */\nfunction copy(doc: Document, source: Element, report: WrIconStripReport | undefined, depth: number): SVGElement {\n const el = doc.createElementNS(SVG_NS, source.localName);\n\n const attributes = source.attributes;\n for (let i = 0, n = attributes.length; i < n; i++) {\n const { name, value } = attributes[i];\n if (!isAllowedAttribute(name, value)) {\n report?.attributes.add(name);\n continue;\n }\n if (name === 'xlink:href') el.setAttributeNS(XLINK_NS, name, value);\n else el.setAttribute(name, value);\n }\n\n if (depth >= MAX_DEPTH) {\n // Named, not silent — the cap is the one rule here that can bite an icon\n // nobody meant as an attack, and a truncated glyph should say so.\n if (source.childNodes.length > 0) report?.elements.add(`… deeper than ${MAX_DEPTH} levels`);\n return el;\n }\n\n const children = source.childNodes;\n for (let i = 0, n = children.length; i < n; i++) {\n const child = children[i];\n // Text, so `<title>`, `<desc>` and `<text>` keep saying what they said.\n // Comments, CDATA and processing instructions are dropped: an icon has no\n // use for them and a comment is a place to hide a payload from a reader.\n if (child.nodeType === 3) {\n el.appendChild(doc.createTextNode(child.nodeValue ?? ''));\n continue;\n }\n if (child.nodeType !== 1) continue;\n\n const element = child as Element;\n if (!ELEMENTS.has(element.localName)) {\n report?.elements.add(element.localName);\n continue;\n }\n el.appendChild(copy(doc, element, report, depth + 1));\n }\n\n return el;\n}\n\nfunction isAllowedAttribute(name: string, value: string): boolean {\n // `data-*` is a styling and testing hook that can never be behavioural.\n if (name.startsWith('data-')) return true;\n if (!ATTRIBUTES.has(name)) return false;\n if (FRAGMENT_ONLY.has(name)) return FRAGMENT_RE.test(value.trim());\n if (URL_FN_RE.test(value)) return URL_FRAGMENT_RE.test(value.trim());\n return true;\n}\n\nexport { sanitizeIcon, type WrIconStripReport };\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 type { WrIconDef } from '../interfaces';\n\nconst VIEW_BOX_RE = /<svg\\b[^>]*\\bviewBox\\s*=/i;\nconst SVG_ROOT_RE = /<svg\\b/i;\n\n/**\n * Dev-mode sanity check for a registered icon — a **rendering** check, and\n * explicitly not a security control.\n *\n * It warns about the two things that make an icon come out wrong: no `<svg>`\n * root, and no `viewBox`, which stops the glyph scaling to the host. It reads\n * the string with two regexes and cannot tell a shape from a script — every\n * payload in the icon corpus passes it, because each one does carry an `<svg>`\n * root with a `viewBox`. It also runs only under `isDevMode()`, so it is absent\n * from the build an attacker would meet.\n *\n * What actually keeps hostile markup out of the DOM is {@link sanitizeIcon},\n * which runs on every render in every build: `<wr-icon>` rebuilds the glyph\n * from an allowlist instead of assigning to `innerHTML`. Anything this function\n * would have to reject is already gone by then, and the component says so in\n * dev mode when it drops something.\n *\n * Production builds drop the call entirely via `isDevMode()` tree-shaking.\n *\n * @internal\n */\nexport function validateIcon(icon: WrIconDef): void {\n if (!SVG_ROOT_RE.test(icon.data)) {\n // eslint-disable-next-line no-console -- dev-mode validation\n console.warn(\n `[NGWR] Icon \"${icon.name}\" data does not contain an <svg> root element. ` +\n `Custom icons must be valid SVG markup.`\n );\n return;\n }\n\n if (!VIEW_BOX_RE.test(icon.data)) {\n // eslint-disable-next-line no-console -- dev-mode validation\n console.warn(\n `[NGWR] Icon \"${icon.name}\" has no viewBox attribute — it won't scale ` +\n `to the host size. Add viewBox=\"0 0 W H\" to the <svg> root.`\n );\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { DOCUMENT } from '@angular/common';\nimport { Component, ElementRef, ViewEncapsulation, computed, effect, inject, input, isDevMode } from '@angular/core';\n\nimport { WrIconRegistry } from './icon-registry';\nimport type { WrIconDef, WrIconName } from './interfaces';\nimport { type WrIconStripReport, sanitizeIcon } from './utils';\n\n/**\n * Renders a registered SVG icon.\n *\n * Icons must be registered via {@link provideWrIcons} before use —\n * either at the application root or on any ancestor component.\n *\n * @example\n * ```html\n * <wr-icon name=\"home\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/icon\n */\n@Component({\n selector: 'wr-icon',\n template: '',\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-icon',\n '[attr.data-icon]': 'name()',\n },\n})\nexport class WrIcon {\n readonly name = input.required<WrIconName>();\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly doc = inject(DOCUMENT);\n\n /**\n * Every icon visible from this position, walked up the whole chain of\n * registering injector levels.\n *\n * Angular's `inject(WR_ICONS)` returns only the **closest** injector's\n * contribution — `multi` providers are not merged across levels — so a\n * component that registers its own icons used to shadow, not extend, what an\n * ancestor registered. {@link WrIconRegistry} is provided once per\n * registering level and links to its parent, so nesting any number of levels\n * now adds up, with the nearest registration winning a name collision.\n *\n * Optional: with no `provideWrIcons()` anywhere the map is simply empty and\n * every name reports as unregistered.\n */\n private readonly iconRegistry = inject(WrIconRegistry, { optional: true });\n\n private readonly registry = computed<ReadonlyMap<WrIconName, WrIconDef>>(\n () => (this.iconRegistry?.resolve() ?? new Map()) as ReadonlyMap<WrIconName, WrIconDef>\n );\n\n private readonly icon = computed(() => this.registry().get(this.name()));\n\n constructor() {\n effect(() => {\n const icon = this.icon();\n const name = this.name();\n\n if (!icon) {\n if (isDevMode()) {\n // Reported, never thrown. An exception here escapes into\n // `runEffectsInView`, which abandons the remaining effects of the\n // whole view — one unknown name would blank every `<wr-icon>` after\n // it while the console blamed only the first. Error level (not\n // `badgeLog`) so it survives a console filtered to errors.\n // eslint-disable-next-line no-console -- misconfiguration must be visible, but must not throw\n console.error(\n `[NGWR] No icon named \"${name}\" is registered. Did you forget to call provideWrIcons()? ` +\n `If the name IS registered, check the injector level: a component-level provideWrIcons() ` +\n `shadows icons registered on an ancestor component.`\n );\n }\n this.clear();\n return;\n }\n\n // Rebuilt from an allowlist rather than assigned to `innerHTML`, which\n // ran an `onload` on the root, an `<image onerror>`, an `<img>` smuggled\n // in after a closed `</svg>` and anything inside a `<foreignObject>` —\n // in production builds, with the app's own origin. `provideWrIcons()`\n // data is untrusted the same way `<wr-markdown>`'s input is: a tenant\n // logo or a fetched icon pack is a normal way to fill it. Building nodes\n // also keeps `<wr-icon>` clear of Trusted Types, which refuses the\n // `innerHTML` write outright.\n const report = isDevMode() ? { elements: new Set<string>(), attributes: new Set<string>() } : undefined;\n const svg = sanitizeIcon(this.doc, icon.data, report);\n\n if (!svg) {\n if (isDevMode()) {\n // eslint-disable-next-line no-console -- misconfiguration must be visible, but must not throw\n console.error(\n `[NGWR] Icon \"${name}\" has no <svg> root element and cannot be rendered. ` +\n `Custom icons must be valid SVG markup.`\n );\n }\n this.clear();\n return;\n }\n\n // Every hand-written inline SVG in the library carries aria-hidden;\n // registered icons went in without it, so a decorative glyph could be\n // announced — usually as a bare \"graphic\" beside the label it decorates.\n // Consumers that need a name put it on the interactive host instead.\n svg.setAttribute('aria-hidden', 'true');\n\n this.clear();\n this.host.nativeElement.appendChild(svg);\n\n if (report) this.reportStripped(name, icon, report);\n });\n }\n\n /**\n * `replaceChildren()` would say this in one call, but domino — the DOM\n * `@angular/platform-server` renders into — does not implement it, and\n * `innerHTML = ''` is the write Trusted Types refuses.\n */\n private clear(): void {\n const el = this.host.nativeElement;\n while (el.firstChild) el.removeChild(el.firstChild);\n }\n\n /**\n * Say what the allowlist removed, once per registered icon.\n *\n * A silently stripped icon is the failure mode this rebuild could introduce:\n * a consumer's own glyph renders blank or wrong and nothing anywhere says\n * why. Keyed on the definition object, so a page with fifty instances of the\n * same icon warns once and an icon nobody renders never warns at all.\n */\n private reportStripped(name: string, icon: WrIconDef, report: WrIconStripReport): void {\n if (WrIcon.reported.has(icon)) return;\n if (report.elements.size === 0 && report.attributes.size === 0) return;\n WrIcon.reported.add(icon);\n\n const removed = [\n report.elements.size ? `elements: ${[...report.elements].join(', ')}` : '',\n report.attributes.size ? `attributes: ${[...report.attributes].join(', ')}` : '',\n ].filter(Boolean);\n\n // eslint-disable-next-line no-console -- a stripped icon must not be silent\n console.warn(\n `[NGWR] Icon \"${name}\" was rendered without part of its markup — ${removed.join('; ')}. ` +\n `Registered icon data is treated as untrusted and rebuilt from an allowlist of SVG ` +\n `shape, paint and text elements, so event handlers, <script>, <style>, <image>, ` +\n `<foreignObject>, <a>, SMIL animation and non-fragment hrefs never reach the DOM.`\n );\n }\n\n private static readonly reported = new WeakSet<WrIconDef>();\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 { type Provider, isDevMode } from '@angular/core';\n\nimport { WrIconRegistry } from '../icon-registry';\nimport type { WrIconDef } from '../interfaces';\nimport { WR_ICONS } from '../tokens';\nimport { validateIcon } from '../utils';\n\n/**\n * Registers a set of icons for use by `<wr-icon>`.\n *\n * Can be called multiple times — each call adds to the registry.\n * Works at both the application root and at the component level,\n * so a feature module or a single component can bring its own icons\n * without polluting the global config.\n *\n * In dev mode, each icon is validated for common issues (missing\n * `viewBox`, malformed root). Validation is dropped from production\n * builds via `isDevMode()` tree-shaking, and it is a rendering check rather\n * than a security one — see {@link validateIcon}.\n *\n * Icon `data` is treated as **untrusted** at render: `<wr-icon>` rebuilds the\n * glyph from an allowlist instead of writing the string to `innerHTML`, so a\n * set fetched at runtime or supplied by a tenant cannot execute in the app's\n * origin. Dev mode names anything the allowlist removed.\n *\n * @example\n * ```ts\n * // App-level registration\n * import { home, user, cog, provideWrIcons } from 'ngwr/icon';\n *\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideWrIcons([home, user, cog]),\n * ],\n * };\n * ```\n *\n * @example\n * ```ts\n * // Component-level registration\n * import { logoGithub, logoNpm, provideWrIcons, WrIconDef } from 'ngwr/icon';\n *\n * @Component({\n * selector: 'ngwr-header',\n * imports: [WrIcon],\n * providers: [provideWrIcons([logoGithub, logoNpm])],\n * template: `<wr-icon name=\"logo-github\" />`,\n * })\n * export class HeaderComponent {}\n * ```\n */\nexport function provideWrIcons(icons: WrIconDef[]): Provider[] {\n if (isDevMode()) {\n for (const icon of icons) validateIcon(icon);\n }\n return [\n { provide: WR_ICONS, useValue: icons, multi: true },\n // A registry link per registering level. Angular does not merge `multi`\n // providers across injectors, so without this chain an element-level\n // registration would shadow everything registered above it instead of\n // adding to it.\n WrIconRegistry,\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 type { WrIconDef } from './interfaces';\n\n/**\n * Wrap a raw SVG string in the {@link WrIconDef} envelope. Use this when\n * importing icons from sources that ship full `<svg>` files — Tabler,\n * Phosphor, Heroicons, Iconoir, Radix, Bootstrap Icons, your designer.\n *\n * Pair with a bundler's raw-SVG import (Vite's `?raw`, Webpack's\n * `raw-loader`, esbuild's `--loader:.svg=text`) so the SVG file becomes\n * a string at build time.\n *\n * For sets with a non-SVG shape (Lucide's IconNode tuples, Feather's\n * inner-only SVG), use the dedicated adapter under\n * `ngwr/icon/adapters/<set>` instead.\n *\n * @example\n * ```ts\n * import { svgIcon, provideWrIcons } from 'ngwr/icon';\n * // Vite: the `?raw` suffix returns the file as a string.\n * import plusSvg from '@tabler/icons/icons/plus.svg?raw';\n * import phosphorPlusSvg from '@phosphor-icons/core/assets/regular/plus.svg?raw';\n *\n * bootstrapApplication(AppComponent, {\n * providers: [\n * provideWrIcons([\n * svgIcon('plus', plusSvg),\n * svgIcon('plus-phosphor', phosphorPlusSvg),\n * ]),\n * ],\n * });\n * ```\n *\n * **`svg` is treated as untrusted.** It is a string, so it is a natural place\n * for a tenant's uploaded logo or an icon pack fetched from an API to end up,\n * and `<wr-icon>` used to write it to `innerHTML`. It no longer does: the glyph\n * is rebuilt from an allowlist of SVG shape, paint and text elements, so\n * `<script>`, `<style>`, `<image>`, `<foreignObject>`, `<a>`, SMIL animation,\n * every `on*` handler, `style` attributes and non-fragment `href`s are dropped\n * before anything reaches the DOM. In dev mode the component names what it\n * removed, so a legitimate icon that loses something says so out loud.\n */\nexport function svgIcon(name: string, svg: string): WrIconDef {\n return { name, data: svg };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;AAAA;;;;;AAKG;AAMH;;;;;;AAMG;AACI,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAyB,UAAU,CAAC;;AClB9E;;;;;AAKG;AAOH;;;;;;;;;;;;;AAaG;MAEU,cAAc,CAAA;;AAER,IAAA,MAAM,GAAG,MAAM,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;AAGnE,IAAA,GAAG,GAAG,MAAM,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE;AAE7E;;;AAGG;IACH,OAAO,GAAA;AACL,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,IAAI,GAAG,EAAqB;AAClE,QAAA,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE;YAC1B,KAAK,MAAM,IAAI,IAAI,GAAG;gBAAE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;QAClD;AACA,QAAA,OAAO,GAAG;IACZ;uGAjBW,cAAc,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;2GAAd,cAAc,EAAA,CAAA;;2FAAd,cAAc,EAAA,UAAA,EAAA,CAAA;kBAD1B;;;AC1BD;;;;;AAKG;AAEH,MAAM,MAAM,GAAG,4BAA4B;AAC3C,MAAM,QAAQ,GAAG,8BAA8B;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BG;AACH,MAAM,QAAQ,GAAwB,IAAI,GAAG,CAAC;IAC5C,KAAK;IACL,GAAG;IACH,MAAM;IACN,QAAQ;IACR,KAAK;IACL,OAAO;IACP,MAAM;IACN,MAAM;IACN,QAAQ;IACR,SAAS;IACT,MAAM;IACN,UAAU;IACV,SAAS;IACT,MAAM;IACN,MAAM;IACN,OAAO;IACP,UAAU;IACV,gBAAgB;IAChB,gBAAgB;IAChB,MAAM;IACN,UAAU;IACV,MAAM;IACN,SAAS;AACV,CAAA,CAAC;AAEF;;;;;;;;;;;;;AAaG;AACH,MAAM,UAAU,GAAwB,IAAI,GAAG,CAAC;;IAE9C,SAAS;IACT,qBAAqB;IACrB,WAAW;IACX,kBAAkB;IAClB,GAAG;IACH,YAAY;IACZ,IAAI;IACJ,IAAI;IACJ,GAAG;IACH,IAAI;IACJ,IAAI;IACJ,GAAG;IACH,GAAG;IACH,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,OAAO;IACP,QAAQ;IACR,QAAQ;;IAER,eAAe;IACf,mBAAmB;IACnB,cAAc;IACd,QAAQ;IACR,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,cAAc;IACd,qBAAqB;IACrB,kBAAkB;IAClB,WAAW;IACX,kBAAkB;IAClB,eAAe;;IAEf,MAAM;IACN,cAAc;IACd,WAAW;IACX,QAAQ;IACR,cAAc;IACd,gBAAgB;IAChB,iBAAiB;IACjB,mBAAmB;IACnB,kBAAkB;IAClB,mBAAmB;IACnB,gBAAgB;IAChB,SAAS;IACT,OAAO;IACP,WAAW;IACX,WAAW;IACX,MAAM;IACN,aAAa;IACb,eAAe;IACf,iBAAiB;IACjB,YAAY;IACZ,cAAc;IACd,SAAS;IACT,YAAY;IACZ,UAAU;IACV,WAAW;IACX,gBAAgB;;IAEhB,aAAa;IACb,YAAY;IACZ,cAAc;IACd,aAAa;IACb,mBAAmB;IACnB,oBAAoB;IACpB,gBAAgB;IAChB,aAAa;IACb,WAAW;IACX,cAAc;IACd,YAAY;IACZ,cAAc;IACd,aAAa;IACb,gBAAgB;IAChB,cAAc;IACd,cAAc;IACd,WAAW;;IAEX,IAAI;IACJ,OAAO;IACP,MAAM;IACN,aAAa;IACb,YAAY;IACZ,iBAAiB;IACjB,kBAAkB;IAClB,sBAAsB;IACtB,WAAW;IACX,MAAM;IACN,SAAS;IACT,aAAa;IACb,OAAO;IACP,aAAa;IACb,WAAW;IACX,UAAU;;IAEV,MAAM;IACN,YAAY;AACb,CAAA,CAAC;AAEF;;;;AAIG;AACH,MAAM,aAAa,GAAwB,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAE1E;AACA,MAAM,WAAW,GAAG,WAAW;AAE/B;;;;AAIG;AACH,MAAM,SAAS,GAAG,QAAQ;AAC1B,MAAM,eAAe,GAAG,qCAAqC;AAE7D;;;;;AAKG;AACH,MAAM,SAAS,GAAG,EAAE;AAapB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CG;AACH,SAAS,YAAY,CAAC,GAAa,EAAE,IAAY,EAAE,MAA0B,EAAA;IAC3E,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC;AACpC,IAAA,OAAO,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,GAAG,IAAI;AACrD;AAEA;;;;;;;;;;;;;;;AAeG;AACH,SAAS,UAAU,CAAC,GAAa,EAAE,IAAY,EAAA;AAC7C,IAAA,MAAM,MAAM,GAAG,GAAG,CAAC,WAAW,EAAE,SAAS;AACzC,IAAA,IAAI,MAAM;AAAE,QAAA,OAAO,QAAQ,CAAC,IAAI,MAAM,EAAE,CAAC,eAAe,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,IAAI,CAAC;IAEjF,MAAM,QAAQ,GAAG,GAAG,CAAC,aAAa,CAAC,UAAU,CAAC;AAC9C,IAAA,QAAQ,CAAC,SAAS,GAAG,IAAI;AACzB,IAAA,OAAO,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;AACnC;AAEA;;;;;;;;;;AAUG;AACH,SAAS,QAAQ,CAAC,IAAU,EAAA;AAC1B,IAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU;AAChC,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE;AAC/C,QAAA,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC;AACzB,QAAA,IAAI,KAAK,CAAC,QAAQ,KAAK,CAAC;YAAE;QAC1B,MAAM,OAAO,GAAG,KAAgB;AAChC,QAAA,IAAI,OAAO,CAAC,SAAS,KAAK,KAAK;AAAE,YAAA,OAAO,OAAO;AAC/C,QAAA,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC;AAChC,QAAA,IAAI,MAAM;AAAE,YAAA,OAAO,MAAM;IAC3B;AACA,IAAA,OAAO,IAAI;AACb;AAEA;AACA,SAAS,IAAI,CAAC,GAAa,EAAE,MAAe,EAAE,MAAqC,EAAE,KAAa,EAAA;AAChG,IAAA,MAAM,EAAE,GAAG,GAAG,CAAC,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,SAAS,CAAC;AAExD,IAAA,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU;AACpC,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE;QACjD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC;QACrC,IAAI,CAAC,kBAAkB,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE;AACpC,YAAA,MAAM,EAAE,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;YAC5B;QACF;QACA,IAAI,IAAI,KAAK,YAAY;YAAE,EAAE,CAAC,cAAc,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC;;AAC9D,YAAA,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC;IACnC;AAEA,IAAA,IAAI,KAAK,IAAI,SAAS,EAAE;;;AAGtB,QAAA,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAA,cAAA,EAAiB,SAAS,CAAA,OAAA,CAAS,CAAC;AAC3F,QAAA,OAAO,EAAE;IACX;AAEA,IAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU;AAClC,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE;AAC/C,QAAA,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC;;;;AAIzB,QAAA,IAAI,KAAK,CAAC,QAAQ,KAAK,CAAC,EAAE;AACxB,YAAA,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,cAAc,CAAC,KAAK,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;YACzD;QACF;AACA,QAAA,IAAI,KAAK,CAAC,QAAQ,KAAK,CAAC;YAAE;QAE1B,MAAM,OAAO,GAAG,KAAgB;QAChC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE;YACpC,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC;YACvC;QACF;AACA,QAAA,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;IACvD;AAEA,IAAA,OAAO,EAAE;AACX;AAEA,SAAS,kBAAkB,CAAC,IAAY,EAAE,KAAa,EAAA;;AAErD,IAAA,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;AAAE,QAAA,OAAO,IAAI;AACzC,IAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;AAAE,QAAA,OAAO,KAAK;AACvC,IAAA,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;AAClE,IAAA,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;AACpE,IAAA,OAAO,IAAI;AACb;;AChXA;;;;;AAKG;AAIH,MAAM,WAAW,GAAG,2BAA2B;AAC/C,MAAM,WAAW,GAAG,SAAS;AAE7B;;;;;;;;;;;;;;;;;;;;AAoBG;AACG,SAAU,YAAY,CAAC,IAAe,EAAA;IAC1C,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;;AAEhC,QAAA,OAAO,CAAC,IAAI,CACV,gBAAgB,IAAI,CAAC,IAAI,CAAA,+CAAA,CAAiD;AACxE,YAAA,CAAA,sCAAA,CAAwC,CAC3C;QACD;IACF;IAEA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;;AAEhC,QAAA,OAAO,CAAC,IAAI,CACV,gBAAgB,IAAI,CAAC,IAAI,CAAA,4CAAA,CAA8C;AACrE,YAAA,CAAA,0DAAA,CAA4D,CAC/D;IACH;AACF;;AClDA;;;;;AAKG;AASH;;;;;;;;;;;;AAYG;MAUU,MAAM,CAAA;IACR,IAAI,GAAG,KAAK,CAAC,QAAQ;6EAAc;AAE3B,IAAA,IAAI,GAAG,MAAM,CAA0B,UAAU,CAAC;AAClD,IAAA,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC;AAEvC;;;;;;;;;;;;;AAaG;IACc,YAAY,GAAG,MAAM,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAEzD,IAAA,QAAQ,GAAG,QAAQ,CAClC,OAAO,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE,IAAI,IAAI,GAAG,EAAE,CAAuC;iFACxF;AAEgB,IAAA,IAAI,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;6EAAC;AAExE,IAAA,WAAA,GAAA;QACE,MAAM,CAAC,MAAK;AACV,YAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE;AACxB,YAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE;YAExB,IAAI,CAAC,IAAI,EAAE;gBACT,IAAI,SAAS,EAAE,EAAE;;;;;;;AAOf,oBAAA,OAAO,CAAC,KAAK,CACX,CAAA,sBAAA,EAAyB,IAAI,CAAA,0DAAA,CAA4D;wBACvF,CAAA,wFAAA,CAA0F;AAC1F,wBAAA,CAAA,kDAAA,CAAoD,CACvD;gBACH;gBACA,IAAI,CAAC,KAAK,EAAE;gBACZ;YACF;;;;;;;;;YAUA,MAAM,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,GAAG,EAAU,EAAE,UAAU,EAAE,IAAI,GAAG,EAAU,EAAE,GAAG,SAAS;AACvG,YAAA,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC;YAErD,IAAI,CAAC,GAAG,EAAE;gBACR,IAAI,SAAS,EAAE,EAAE;;AAEf,oBAAA,OAAO,CAAC,KAAK,CACX,CAAA,aAAA,EAAgB,IAAI,CAAA,oDAAA,CAAsD;AACxE,wBAAA,CAAA,sCAAA,CAAwC,CAC3C;gBACH;gBACA,IAAI,CAAC,KAAK,EAAE;gBACZ;YACF;;;;;AAMA,YAAA,GAAG,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC;YAEvC,IAAI,CAAC,KAAK,EAAE;YACZ,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,GAAG,CAAC;AAExC,YAAA,IAAI,MAAM;gBAAE,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC;AACrD,QAAA,CAAC,CAAC;IACJ;AAEA;;;;AAIG;IACK,KAAK,GAAA;AACX,QAAA,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa;QAClC,OAAO,EAAE,CAAC,UAAU;AAAE,YAAA,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,UAAU,CAAC;IACrD;AAEA;;;;;;;AAOG;AACK,IAAA,cAAc,CAAC,IAAY,EAAE,IAAe,EAAE,MAAyB,EAAA;AAC7E,QAAA,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE;AAC/B,QAAA,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC;YAAE;AAChE,QAAA,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;AAEzB,QAAA,MAAM,OAAO,GAAG;YACd,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAA,UAAA,EAAa,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA,CAAE,GAAG,EAAE;YAC1E,MAAM,CAAC,UAAU,CAAC,IAAI,GAAG,CAAA,YAAA,EAAe,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA,CAAE,GAAG,EAAE;AACjF,SAAA,CAAC,MAAM,CAAC,OAAO,CAAC;;AAGjB,QAAA,OAAO,CAAC,IAAI,CACV,CAAA,aAAA,EAAgB,IAAI,CAAA,4CAAA,EAA+C,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA,EAAA,CAAI;YACvF,CAAA,kFAAA,CAAoF;YACpF,CAAA,+EAAA,CAAiF;AACjF,YAAA,CAAA,gFAAA,CAAkF,CACrF;IACH;AAEQ,IAAA,OAAgB,QAAQ,GAAG,IAAI,OAAO,EAAa;uGA5HhD,MAAM,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAN,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAM,oRAPP,EAAE,EAAA,QAAA,EAAA,IAAA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FAOD,MAAM,EAAA,UAAA,EAAA,CAAA;kBATlB,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,SAAS;AACnB,oBAAA,QAAQ,EAAE,EAAE;oBACZ,aAAa,EAAE,iBAAiB,CAAC,IAAI;AACrC,oBAAA,IAAI,EAAE;AACJ,wBAAA,KAAK,EAAE,SAAS;AAChB,wBAAA,kBAAkB,EAAE,QAAQ;AAC7B,qBAAA;AACF,iBAAA;;;ACnCD;;;;;AAKG;AASH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CG;AACG,SAAU,cAAc,CAAC,KAAkB,EAAA;IAC/C,IAAI,SAAS,EAAE,EAAE;QACf,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,YAAY,CAAC,IAAI,CAAC;IAC9C;IACA,OAAO;QACL,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE;;;;;QAKnD,cAAc;KACf;AACH;;ACtEA;;;;;AAKG;AAIH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCG;AACG,SAAU,OAAO,CAAC,IAAY,EAAE,GAAW,EAAA;AAC/C,IAAA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE;AAC5B;;AClDA;;AAEG;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-image-cropper-testing.mjs","sources":["../../../projects/lib/image-cropper/testing/wr-image-cropper-harness.ts","../../../projects/lib/image-cropper/testing/ngwr-image-cropper-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, TestKey, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrCropHandle } from 'ngwr/image-cropper';\n\nimport type { WrImageCropperHarnessFilters } from './interfaces';\n\nconst ARROWS = {\n left: TestKey.LEFT_ARROW,\n right: TestKey.RIGHT_ARROW,\n up: TestKey.UP_ARROW,\n down: TestKey.DOWN_ARROW,\n} as const;\n\n/** The four directions the crop window's keyboard model understands. */\nexport type WrCropArrow = keyof typeof ARROWS;\n\n/** Where the crop window sits over the DISPLAYED image, in CSS pixels. */\nexport interface WrCropWindowBox {\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n}\n\n/**\n * Test harness for `<wr-image-cropper>`.\n *\n * **This is the smallest honest surface of any harness here, and the reason is worth\n * reading before you reach for a method that is not on it.** Almost everything the\n * cropper does is measured: it reads the `<img>`'s rendered box and its\n * `naturalWidth`, converts between display and source pixels, and moves the crop\n * window with pointer deltas. jsdom measures nothing — every rect is 0×0 and\n * `naturalWidth` is 0 — so an image never \"loads\", the crop UI never renders, and a\n * synthetic drag divides by zero. There is deliberately no drag method: it would\n * write `NaN` and report success.\n *\n * What a spec CAN do is give the image a box and a natural size itself, then call\n * {@link dispatchImageLoad} — the two numbers `onImageLoad` reads are the whole\n * stub, and from there {@link getCropBox} and {@link getHandles} are real answers,\n * because the component writes the crop window's geometry as inline pixels. The\n * KEYBOARD path needs the same stub to get the crop UI up, but nothing beyond it:\n * its step is a constant rather than a pointer delta, so {@link pressArrow} lands\n * on a number a spec can name.\n *\n * ```ts\n * // In your spec, before dispatching the load:\n * const img = fixture.nativeElement.querySelector('.wr-image-cropper__image');\n * img.getBoundingClientRect = () => ({ width: 400, height: 400, x: 0, y: 0 }) as DOMRect;\n * Object.defineProperty(img, 'naturalWidth', { value: 800 });\n * Object.defineProperty(img, 'naturalHeight', { value: 800 });\n * ```\n *\n * **There is nothing to click.** The cropper renders no buttons: the result\n * arrives on the `(cropped)` output, which emits a `Blob` after each drag end or\n * each run of arrow keys, and a consumer who wants a one-off read calls `toBlob()` /\n * `toDataUrl()` — or `cropRect()` for the geometry — on the component through a\n * `viewChild`. The same goes for `(loadError)` and `status()`, which is why\n * {@link isFailed} reads the failure off the host class instead. Outputs and instance\n * members are component API rather than DOM, so all of it is outside what a harness\n * can reach at all, which is worth knowing rather than discovering.\n *\n * @example\n * ```ts\n * const cropper = await loader.getHarness(WrImageCropperHarness);\n *\n * expect(await cropper.isEmpty()).toBe(false);\n * expect(await cropper.isReady()).toBe(false); // nothing measured yet\n *\n * await cropper.dispatchImageLoad();\n * expect(await cropper.getCropBox()).toEqual({ x: 80, y: 80, width: 240, height: 240 });\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrImageCropperHarness extends ComponentHarness {\n static hostSelector = 'wr-image-cropper';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrImageCropperHarnessFilters = {}): HarnessPredicate<WrImageCropperHarness> {\n return new HarnessPredicate(WrImageCropperHarness, options).addOption(\n 'empty',\n options.empty,\n async (harness, empty) => (await harness.isEmpty()) === empty\n );\n }\n\n private readonly image = this.locatorForOptional('.wr-image-cropper__image');\n private readonly cropWindow = this.locatorForOptional('.wr-image-cropper__window');\n\n /** Whether the cropper has no source at all, and is showing its placeholder. */\n async isEmpty(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-image-cropper__empty')()) !== null;\n }\n\n /** The placeholder's text, or `null` when there is an image. Comes from the catalog. */\n async getEmptyText(): Promise<string | null> {\n const empty = await this.locatorForOptional('.wr-image-cropper__empty')();\n return empty ? empty.text() : null;\n }\n\n /**\n * Whether the crop UI is up.\n *\n * Not the same question as {@link isEmpty}: a cropper with a perfectly good `src`\n * shows nothing but the image until it has been MEASURED, because the window's size\n * is a fraction of the rendered box. In a browser that happens on load; in a unit\n * test it happens when the spec stubs the box and calls\n * {@link dispatchImageLoad}.\n */\n async isReady(): Promise<boolean> {\n return (await this.cropWindow()) !== null;\n }\n\n /**\n * What the `<img>` is pointing at.\n *\n * A `File` or `Blob` source becomes an object URL, so this is `blob:…` for those\n * and the plain string for a string `src`. Comparing the two is not useful; asking\n * whether it CHANGED is.\n */\n async getImageSrc(): Promise<string | null> {\n const image = await this.image();\n return image ? image.getProperty<string>('src') : null;\n }\n\n /**\n * Fire the image's `load` event.\n *\n * The event, not a real load: jsdom fetches nothing, and the handler reads\n * `getBoundingClientRect()` and `naturalWidth` off the element. Stub those first —\n * see this class's docs — or the crop window will still not render and\n * {@link isReady} will stay `false`, which is exactly what a real cropper does with\n * a zero-sized image.\n */\n async dispatchImageLoad(): Promise<void> {\n const image = await this.image();\n if (!image) {\n throw new Error(\n 'WrImageCropperHarness.dispatchImageLoad(): there is no image — the cropper has no `src`, so it is ' +\n 'showing its empty state.'\n );\n }\n await image.dispatchEvent('load');\n }\n\n /**\n * Fire the image's `error` event — what a browser does with a source it cannot\n * decode, and the only way to get one in a unit test, where nothing is decoded.\n *\n * Afterwards {@link isFailed} is `true` and the component has emitted\n * `(loadError)`; a spec that wants the payload binds that output on its host.\n */\n async dispatchImageError(): Promise<void> {\n const image = await this.image();\n if (!image) {\n throw new Error(\n 'WrImageCropperHarness.dispatchImageError(): there is no image — the cropper has no `src`, so it is ' +\n 'showing its empty state and there is nothing to fail.'\n );\n }\n await image.dispatchEvent('error');\n }\n\n /**\n * Whether the browser failed to decode the current source.\n *\n * Read off the `wr-image-cropper--failed` host modifier — the part of the failure\n * the DOM shows. It is a third answer beside {@link isEmpty} and {@link isReady},\n * not a combination of them: a failed cropper has a `src` (so it is not empty) and\n * no crop window (so it is not ready), which is also exactly what a cropper still\n * LOADING looks like. Asking those two would call a broken image a slow one.\n *\n * The component renders no message for it, so there is no text to read here; the\n * host's own message is the host's to assert.\n */\n async isFailed(): Promise<boolean> {\n return (await this.host()).hasClass('wr-image-cropper--failed');\n }\n\n /**\n * Where the crop window sits over the displayed image, in CSS pixels.\n *\n * From the inline styles the component writes — a measured box would be 0×0 — and\n * in DISPLAY pixels, which is not what a consumer receives: `cropRect()` converts\n * to the image's natural pixels, and that conversion is the component's own contract\n * rather than something the DOM shows.\n */\n async getCropBox(): Promise<WrCropWindowBox> {\n const window_ = await this.requireReady('getCropBox');\n return {\n x: Number.parseFloat(await window_.getCssValue('left')),\n y: Number.parseFloat(await window_.getCssValue('top')),\n width: Number.parseFloat(await window_.getCssValue('width')),\n height: Number.parseFloat(await window_.getCssValue('height')),\n };\n }\n\n /** The eight resize handles, by their compass suffix. Throws before the crop UI is up. */\n async getHandles(): Promise<WrCropHandle[]> {\n await this.requireReady('getHandles');\n const handles = await this.locatorForAll('.wr-image-cropper__handle')();\n\n return Promise.all(\n handles.map(async handle => {\n const classes = (await handle.getAttribute('class')) ?? '';\n const found = classes.split(/\\s+/).find(name => name.startsWith('wr-image-cropper__handle--'));\n return (found?.slice('wr-image-cropper__handle--'.length) ?? '') as WrCropHandle;\n })\n );\n }\n\n /** Whether the dimming backdrop with the cut-out is drawn. */\n async hasBackdrop(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-image-cropper__backdrop')()) !== null;\n }\n\n /**\n * Press an arrow key on the crop window — one display pixel, or ten with\n * `shift`; `alt` resizes from the east / south edge instead of moving.\n *\n * Sent to the crop window, where the component listens, so it does not depend on\n * jsdom having managed to focus anything. `sendKeys` fires keyup as well as\n * keydown, which is what makes `(cropped)` emit — the component treats a run of\n * arrows as one gesture, the way a drag is one.\n */\n async pressArrow(arrow: WrCropArrow, modifiers: { alt?: boolean; shift?: boolean } = {}): Promise<void> {\n const window_ = await this.requireReady('pressArrow');\n const keys = ARROWS[arrow];\n await (modifiers.alt || modifiers.shift ? window_.sendKeys(modifiers, keys) : window_.sendKeys(keys));\n }\n\n /** The crop window's accessible name. Comes from the catalog. */\n async getCropWindowLabel(): Promise<string | null> {\n return (await this.requireReady('getCropWindowLabel')).getAttribute('aria-label');\n }\n\n /**\n * The last thing the live region announced — the crop rect in SOURCE pixels,\n * as `x, y, width × height`.\n *\n * Empty until a key has moved the crop: a pointer drag deliberately says nothing\n * (it would flood the region and the sighted user can see it), and the initial\n * crop is not news. Two questions, not one — {@link getCropBox} reads the window's\n * DISPLAY geometry, and a component that moved the box without announcing it\n * would still pass that one.\n */\n async getAnnouncement(): Promise<string> {\n await this.requireReady('getAnnouncement');\n const status = await this.locatorForOptional('[role=\"status\"]')();\n return status ? status.text() : '';\n }\n\n private async requireReady(method: string): Promise<TestElement> {\n const window_ = await this.cropWindow();\n if (window_) return window_;\n\n throw new Error(\n `WrImageCropperHarness.${method}(): the crop window is not rendered. The cropper sizes it from the ` +\n \"image's MEASURED box, and a unit test has none — give the <img> a `getBoundingClientRect` and a \" +\n '`naturalWidth`, then call dispatchImageLoad().'\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-image-cropper-testing.mjs","sources":["../../../projects/lib/image-cropper/testing/wr-image-cropper-harness.ts","../../../projects/lib/image-cropper/testing/ngwr-image-cropper-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, TestKey, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrCropHandle } from 'ngwr/image-cropper';\n\nimport type { WrImageCropperHarnessFilters } from './interfaces';\n\nconst ARROWS = {\n left: TestKey.LEFT_ARROW,\n right: TestKey.RIGHT_ARROW,\n up: TestKey.UP_ARROW,\n down: TestKey.DOWN_ARROW,\n} as const;\n\n/** The four directions the crop window's keyboard model understands. */\nexport type WrCropArrow = keyof typeof ARROWS;\n\n/** Where the crop window sits over the DISPLAYED image, in CSS pixels. */\nexport interface WrCropWindowBox {\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n}\n\n/**\n * Test harness for `<wr-image-cropper>`.\n *\n * **This is the smallest honest surface of any harness here, and the reason is worth\n * reading before you reach for a method that is not on it.** Almost everything the\n * cropper does is measured: it reads the `<img>`'s rendered box and its\n * `naturalWidth`, converts between display and source pixels, and moves the crop\n * window with pointer deltas. jsdom measures nothing — every rect is 0×0 and\n * `naturalWidth` is 0 — so an image never \"loads\", the crop UI never renders, and a\n * synthetic drag divides by zero. There is deliberately no drag method: it would\n * write `NaN` and report success.\n *\n * What a spec CAN do is give the image a box and a natural size itself, then call\n * {@link dispatchImageLoad} — the two numbers `onImageLoad` reads are the whole\n * stub, and from there {@link getCropBox} and {@link getHandles} are real answers,\n * because the component writes the crop window's geometry as inline pixels. The\n * KEYBOARD path needs the same stub to get the crop UI up, but nothing beyond it:\n * its step is a constant rather than a pointer delta, so {@link pressArrow} lands\n * on a number a spec can name.\n *\n * ```ts\n * // In your spec, before dispatching the load:\n * const img = fixture.nativeElement.querySelector('.wr-image-cropper__image');\n * img.getBoundingClientRect = () => ({ width: 400, height: 400, x: 0, y: 0 }) as DOMRect;\n * Object.defineProperty(img, 'naturalWidth', { value: 800 });\n * Object.defineProperty(img, 'naturalHeight', { value: 800 });\n * ```\n *\n * **There is nothing to click.** The cropper renders no buttons: the result\n * arrives on the `(cropped)` output, which emits a `Blob` after each drag end or\n * each run of arrow keys, and a consumer who wants a one-off read calls `toBlob()` /\n * `toDataUrl()` — or `cropRect()` for the geometry — on the component through a\n * `viewChild`. The same goes for `(loadError)` and `status()`, which is why\n * {@link isFailed} reads the failure off the host class instead. Outputs and instance\n * members are component API rather than DOM, so all of it is outside what a harness\n * can reach at all, which is worth knowing rather than discovering.\n *\n * @example\n * ```ts\n * const cropper = await loader.getHarness(WrImageCropperHarness);\n *\n * expect(await cropper.isEmpty()).toBe(false);\n * expect(await cropper.isReady()).toBe(false); // nothing measured yet\n *\n * await cropper.dispatchImageLoad();\n * expect(await cropper.getCropBox()).toEqual({ x: 80, y: 80, width: 240, height: 240 });\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrImageCropperHarness extends ComponentHarness {\n static hostSelector = 'wr-image-cropper';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrImageCropperHarnessFilters = {}): HarnessPredicate<WrImageCropperHarness> {\n return new HarnessPredicate(WrImageCropperHarness, options).addOption(\n 'empty',\n options.empty,\n async (harness, empty) => (await harness.isEmpty()) === empty\n );\n }\n\n private readonly image = this.locatorForOptional('.wr-image-cropper__image');\n private readonly cropWindow = this.locatorForOptional('.wr-image-cropper__window');\n\n /** Whether the cropper has no source at all, and is showing its placeholder. */\n async isEmpty(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-image-cropper__empty')()) !== null;\n }\n\n /** The placeholder's text, or `null` when there is an image. Comes from the catalog. */\n async getEmptyText(): Promise<string | null> {\n const empty = await this.locatorForOptional('.wr-image-cropper__empty')();\n return empty ? empty.text() : null;\n }\n\n /**\n * Whether the crop UI is up.\n *\n * Not the same question as {@link isEmpty}: a cropper with a perfectly good `src`\n * shows nothing but the image until it has been MEASURED, because the window's size\n * is a fraction of the rendered box. In a browser that happens on load; in a unit\n * test it happens when the spec stubs the box and calls\n * {@link dispatchImageLoad}.\n */\n async isReady(): Promise<boolean> {\n return (await this.cropWindow()) !== null;\n }\n\n /**\n * What the `<img>` is pointing at.\n *\n * A `File` or `Blob` source becomes an object URL, so this is `blob:…` for those\n * and the plain string for a string `src`. Comparing the two is not useful; asking\n * whether it CHANGED is.\n */\n async getImageSrc(): Promise<string | null> {\n const image = await this.image();\n return image ? image.getProperty<string>('src') : null;\n }\n\n /**\n * Fire the image's `load` event.\n *\n * The event, not a real load: jsdom fetches nothing, and the handler reads\n * `getBoundingClientRect()` and `naturalWidth` off the element. Stub those first —\n * see this class's docs — or the crop window will still not render and\n * {@link isReady} will stay `false`, which is exactly what a real cropper does with\n * a zero-sized image.\n */\n async dispatchImageLoad(): Promise<void> {\n const image = await this.image();\n if (!image) {\n throw new Error(\n 'WrImageCropperHarness.dispatchImageLoad(): there is no image — the cropper has no `src`, so it is ' +\n 'showing its empty state.'\n );\n }\n await image.dispatchEvent('load');\n }\n\n /**\n * Fire the image's `error` event — what a browser does with a source it cannot\n * decode, and the only way to get one in a unit test, where nothing is decoded.\n *\n * Afterwards {@link isFailed} is `true` and the component has emitted\n * `(loadError)`; a spec that wants the payload binds that output on its host.\n */\n async dispatchImageError(): Promise<void> {\n const image = await this.image();\n if (!image) {\n throw new Error(\n 'WrImageCropperHarness.dispatchImageError(): there is no image — the cropper has no `src`, so it is ' +\n 'showing its empty state and there is nothing to fail.'\n );\n }\n await image.dispatchEvent('error');\n }\n\n /**\n * Whether the browser failed to decode the current source.\n *\n * Read off the `wr-image-cropper--failed` host modifier — the part of the failure\n * the DOM shows. It is a third answer beside {@link isEmpty} and {@link isReady},\n * not a combination of them: a failed cropper has a `src` (so it is not empty) and\n * no crop window (so it is not ready), which is also exactly what a cropper still\n * LOADING looks like. Asking those two would call a broken image a slow one.\n *\n * The component renders no message for it, so there is no text to read here; the\n * host's own message is the host's to assert.\n */\n async isFailed(): Promise<boolean> {\n return (await this.host()).hasClass('wr-image-cropper--failed');\n }\n\n /**\n * Where the crop window sits over the displayed image, in CSS pixels.\n *\n * From the inline styles the component writes — a measured box would be 0×0 — and\n * in DISPLAY pixels, which is not what a consumer receives: `cropRect()` converts\n * to the image's natural pixels, and that conversion is the component's own contract\n * rather than something the DOM shows.\n */\n async getCropBox(): Promise<WrCropWindowBox> {\n const window_ = await this.requireReady('getCropBox');\n return {\n x: Number.parseFloat(await window_.getCssValue('left')),\n y: Number.parseFloat(await window_.getCssValue('top')),\n width: Number.parseFloat(await window_.getCssValue('width')),\n height: Number.parseFloat(await window_.getCssValue('height')),\n };\n }\n\n /** The eight resize handles, by their compass suffix. Throws before the crop UI is up. */\n async getHandles(): Promise<WrCropHandle[]> {\n await this.requireReady('getHandles');\n const handles = await this.locatorForAll('.wr-image-cropper__handle')();\n\n return Promise.all(\n handles.map(async handle => {\n const classes = (await handle.getAttribute('class')) ?? '';\n const found = classes.split(/\\s+/).find(name => name.startsWith('wr-image-cropper__handle--'));\n return (found?.slice('wr-image-cropper__handle--'.length) ?? '') as WrCropHandle;\n })\n );\n }\n\n /** Whether the dimming backdrop with the cut-out is drawn. */\n async hasBackdrop(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-image-cropper__backdrop')()) !== null;\n }\n\n /**\n * Press an arrow key on the crop window — one display pixel, or ten with\n * `shift`; `alt` resizes from the east / south edge instead of moving.\n *\n * Sent to the crop window, where the component listens, so it does not depend on\n * jsdom having managed to focus anything. `sendKeys` fires keyup as well as\n * keydown, which is what makes `(cropped)` emit — the component treats a run of\n * arrows as one gesture, the way a drag is one.\n */\n async pressArrow(arrow: WrCropArrow, modifiers: { alt?: boolean; shift?: boolean } = {}): Promise<void> {\n const window_ = await this.requireReady('pressArrow');\n const keys = ARROWS[arrow];\n await (modifiers.alt || modifiers.shift ? window_.sendKeys(modifiers, keys) : window_.sendKeys(keys));\n }\n\n /** The crop window's accessible name. Comes from the catalog. */\n async getCropWindowLabel(): Promise<string | null> {\n return (await this.requireReady('getCropWindowLabel')).getAttribute('aria-label');\n }\n\n /**\n * The last thing the live region announced — the crop rect in SOURCE pixels,\n * as `x, y, width × height`.\n *\n * Empty until a key has moved the crop: a pointer drag deliberately says nothing\n * (it would flood the region and the sighted user can see it), and the initial\n * crop is not news. Two questions, not one — {@link getCropBox} reads the window's\n * DISPLAY geometry, and a component that moved the box without announcing it\n * would still pass that one.\n */\n async getAnnouncement(): Promise<string> {\n await this.requireReady('getAnnouncement');\n const status = await this.locatorForOptional('[role=\"status\"]')();\n return status ? status.text() : '';\n }\n\n private async requireReady(method: string): Promise<TestElement> {\n const window_ = await this.cropWindow();\n if (window_) return window_;\n\n throw new Error(\n `WrImageCropperHarness.${method}(): the crop window is not rendered. The cropper sizes it from the ` +\n \"image's MEASURED box, and a unit test has none — give the <img> a `getBoundingClientRect` and a \" +\n '`naturalWidth`, then call dispatchImageLoad().'\n );\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -629,10 +629,10 @@ class WrImageCropper {
629
629
  // Swallow — invalid state (image not loaded, crop empty, etc.).
630
630
  }
631
631
  }
632
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrImageCropper, deps: [], target: i0.ɵɵFactoryTarget.Component });
633
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: WrImageCropper, isStandalone: true, selector: "wr-image-cropper", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, aspectRatio: { classPropertyName: "aspectRatio", publicName: "aspectRatio", isSignal: true, isRequired: false, transformFunction: null }, minWidth: { classPropertyName: "minWidth", publicName: "minWidth", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, outputType: { classPropertyName: "outputType", publicName: "outputType", isSignal: true, isRequired: false, transformFunction: null }, outputQuality: { classPropertyName: "outputQuality", publicName: "outputQuality", isSignal: true, isRequired: false, transformFunction: null }, maxOutputSize: { classPropertyName: "maxOutputSize", publicName: "maxOutputSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cropped: "cropped", loadError: "loadError" }, host: { properties: { "class.wr-image-cropper--failed": "status() === 'failed'" }, classAttribute: "wr-image-cropper" }, viewQueries: [{ propertyName: "imgEl", first: true, predicate: ["img"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (objectUrl(); as url) {\n <div class=\"wr-image-cropper__canvas\">\n <img\n #img\n class=\"wr-image-cropper__image\"\n [src]=\"url\"\n alt=\"\"\n draggable=\"false\"\n (load)=\"onImageLoad()\"\n (error)=\"onImageError()\"\n />\n\n @if (display().w > 0) {\n <!-- Semi-transparent backdrop with a cut-out for the crop window. -->\n <div\n class=\"wr-image-cropper__backdrop\"\n [style.clip-path]=\"`polygon(0 0, 100% 0, 100% 100%, 0 100%, 0 ${cropDisplay().y}px, ${cropDisplay().x}px ${cropDisplay().y}px, ${cropDisplay().x}px ${cropDisplay().y + cropDisplay().h}px, ${cropDisplay().x + cropDisplay().w}px ${cropDisplay().y + cropDisplay().h}px, ${cropDisplay().x + cropDisplay().w}px ${cropDisplay().y}px, 0 ${cropDisplay().y}px)`\"\n ></div>\n\n <!--\n Crop window \u2014 drag to move, with eight resize handles, and the ONE\n keyboard control the component has (see `onWindowKeydown`). The eight\n handles stay pointer-only and `aria-hidden`: eight extra tab stops per\n cropper would be hostile. The keys resize from the east and south edges\n only, so a west or north drag comes out as a resize plus a move rather\n than as one gesture.\n\n `role=\"group\"` rather than no role at all, because `aria-label` is\n prohibited on a roleless `<div>` and this needs a name; and rather than\n `role=\"application\"`, which would switch the reader out of its own\n browse mode for the whole subtree \u2014 far more than one rectangle is worth.\n -->\n <div\n class=\"wr-image-cropper__window\"\n role=\"group\"\n tabindex=\"0\"\n [attr.aria-label]=\"resolvedWindowLabel()\"\n [attr.aria-describedby]=\"helpId\"\n [style.left.px]=\"cropDisplay().x\"\n [style.top.px]=\"cropDisplay().y\"\n [style.width.px]=\"cropDisplay().w\"\n [style.height.px]=\"cropDisplay().h\"\n (pointerdown)=\"onPointerDown('move', $event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n (lostpointercapture)=\"onPointerUp($event)\"\n (keydown)=\"onWindowKeydown($event)\"\n (keyup)=\"onWindowKeyup()\"\n >\n @for (h of handles; track h) {\n <span\n aria-hidden=\"true\"\n [class]=\"`wr-image-cropper__handle wr-image-cropper__handle--${h}`\"\n (pointerdown)=\"onPointerDown(h, $event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n (lostpointercapture)=\"onPointerUp($event)\"\n ></span>\n }\n </div>\n\n <p class=\"wr-image-cropper__sr-only\" [id]=\"helpId\">{{ resolvedKeyHelp() }}</p>\n\n <!--\n The crop rect in SOURCE pixels, announced after a keyboard change only \u2014\n a pointer drag already gives sighted feedback and would flood the region,\n and the initial crop is not news. Deliberately bare numbers: a sentence\n here would be a translated string the catalog does not carry, and the\n reader has just been told what the four numbers mean by `helpId`.\n -->\n <p class=\"wr-image-cropper__sr-only\" role=\"status\">{{ announcement() }}</p>\n }\n </div>\n} @else {\n <div class=\"wr-image-cropper__empty\">{{ emptyText() }}</div>\n}\n", encapsulation: i0.ViewEncapsulation.None });
632
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrImageCropper, deps: [], target: i0.ɵɵFactoryTarget.Component });
633
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: WrImageCropper, isStandalone: true, selector: "wr-image-cropper", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, aspectRatio: { classPropertyName: "aspectRatio", publicName: "aspectRatio", isSignal: true, isRequired: false, transformFunction: null }, minWidth: { classPropertyName: "minWidth", publicName: "minWidth", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, outputType: { classPropertyName: "outputType", publicName: "outputType", isSignal: true, isRequired: false, transformFunction: null }, outputQuality: { classPropertyName: "outputQuality", publicName: "outputQuality", isSignal: true, isRequired: false, transformFunction: null }, maxOutputSize: { classPropertyName: "maxOutputSize", publicName: "maxOutputSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cropped: "cropped", loadError: "loadError" }, host: { properties: { "class.wr-image-cropper--failed": "status() === 'failed'" }, classAttribute: "wr-image-cropper" }, viewQueries: [{ propertyName: "imgEl", first: true, predicate: ["img"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (objectUrl(); as url) {\n <div class=\"wr-image-cropper__canvas\">\n <img\n #img\n class=\"wr-image-cropper__image\"\n [src]=\"url\"\n alt=\"\"\n draggable=\"false\"\n (load)=\"onImageLoad()\"\n (error)=\"onImageError()\"\n />\n\n @if (display().w > 0) {\n <!-- Semi-transparent backdrop with a cut-out for the crop window. -->\n <div\n class=\"wr-image-cropper__backdrop\"\n [style.clip-path]=\"`polygon(0 0, 100% 0, 100% 100%, 0 100%, 0 ${cropDisplay().y}px, ${cropDisplay().x}px ${cropDisplay().y}px, ${cropDisplay().x}px ${cropDisplay().y + cropDisplay().h}px, ${cropDisplay().x + cropDisplay().w}px ${cropDisplay().y + cropDisplay().h}px, ${cropDisplay().x + cropDisplay().w}px ${cropDisplay().y}px, 0 ${cropDisplay().y}px)`\"\n ></div>\n\n <!--\n Crop window \u2014 drag to move, with eight resize handles, and the ONE\n keyboard control the component has (see `onWindowKeydown`). The eight\n handles stay pointer-only and `aria-hidden`: eight extra tab stops per\n cropper would be hostile. The keys resize from the east and south edges\n only, so a west or north drag comes out as a resize plus a move rather\n than as one gesture.\n\n `role=\"group\"` rather than no role at all, because `aria-label` is\n prohibited on a roleless `<div>` and this needs a name; and rather than\n `role=\"application\"`, which would switch the reader out of its own\n browse mode for the whole subtree \u2014 far more than one rectangle is worth.\n -->\n <div\n class=\"wr-image-cropper__window\"\n role=\"group\"\n tabindex=\"0\"\n [attr.aria-label]=\"resolvedWindowLabel()\"\n [attr.aria-describedby]=\"helpId\"\n [style.left.px]=\"cropDisplay().x\"\n [style.top.px]=\"cropDisplay().y\"\n [style.width.px]=\"cropDisplay().w\"\n [style.height.px]=\"cropDisplay().h\"\n (pointerdown)=\"onPointerDown('move', $event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n (lostpointercapture)=\"onPointerUp($event)\"\n (keydown)=\"onWindowKeydown($event)\"\n (keyup)=\"onWindowKeyup()\"\n >\n @for (h of handles; track h) {\n <span\n aria-hidden=\"true\"\n [class]=\"`wr-image-cropper__handle wr-image-cropper__handle--${h}`\"\n (pointerdown)=\"onPointerDown(h, $event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n (lostpointercapture)=\"onPointerUp($event)\"\n ></span>\n }\n </div>\n\n <p class=\"wr-image-cropper__sr-only\" [id]=\"helpId\">{{ resolvedKeyHelp() }}</p>\n\n <!--\n The crop rect in SOURCE pixels, announced after a keyboard change only \u2014\n a pointer drag already gives sighted feedback and would flood the region,\n and the initial crop is not news. Deliberately bare numbers: a sentence\n here would be a translated string the catalog does not carry, and the\n reader has just been told what the four numbers mean by `helpId`.\n -->\n <p class=\"wr-image-cropper__sr-only\" role=\"status\">{{ announcement() }}</p>\n }\n </div>\n} @else {\n <div class=\"wr-image-cropper__empty\">{{ emptyText() }}</div>\n}\n", encapsulation: i0.ViewEncapsulation.None });
634
634
  }
635
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrImageCropper, decorators: [{
635
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrImageCropper, decorators: [{
636
636
  type: Component,
637
637
  args: [{ selector: 'wr-image-cropper', encapsulation: ViewEncapsulation.None, host: {
638
638
  class: 'wr-image-cropper',
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-image-cropper.mjs","sources":["../../../projects/lib/image-cropper/image-cropper.ts","../../../projects/lib/image-cropper/image-cropper.html","../../../projects/lib/image-cropper/ngwr-image-cropper.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 { coerceNumberProperty } from '@angular/cdk/coercion';\nimport {\n Component,\n DestroyRef,\n type ElementRef,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n input,\n output,\n signal,\n viewChild,\n} from '@angular/core';\n\nimport { useI18nText } from 'ngwr/i18n';\nimport { clamp, randomId } from 'ngwr/utils';\n\nimport type { WrCropHandle, WrCropRect, WrImageCropperStatus, WrImageLoadError, WrImageOutputType } from './interfaces';\n\n/**\n * `[maxOutputSize]`'s transform. Anything that is not a positive, finite number —\n * `null`, an empty attribute, `0`, a typo — means \"no cap\" rather than a 1px export:\n * a bad config should cost resolution nobody asked to lose, not the whole image.\n */\nfunction coerceMaxOutputSize(value: unknown): number | null {\n if (value === null || value === undefined || value === '') return null;\n const n = coerceNumberProperty(value, Number.NaN);\n return Number.isFinite(n) && n > 0 ? n : null;\n}\n\n/**\n * Stands in for the consumer input `useI18nText` expects. None of this\n * component's three strings has a `*Label` input behind it — the catalog is the\n * override channel — so they share one empty override.\n */\nconst NO_OVERRIDE = signal<string | null>(null).asReadonly();\n\n/** Keyboard step in display pixels, and what Shift promotes it to. */\nconst KEY_STEP = 1;\nconst KEY_STEP_COARSE = 10;\n\ninterface RectPx {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\ninterface SizePx {\n w: number;\n h: number;\n}\n\n/** Distances, in CSS pixels, past each side of a box. */\ninterface SidesPx {\n left: number;\n top: number;\n right: number;\n bottom: number;\n}\n\nconst NO_SIDES: SidesPx = { left: 0, top: 0, right: 0, bottom: 0 };\n\n/**\n * The rect a resize gesture produces: a pure function of the rect the gesture started\n * from and the TOTAL pointer offset, so the same pointer position always gives the same\n * rect however the pointer got there.\n *\n * The anchor never moves. It is the edge or corner opposite the handle — for `se` the\n * top-left corner, for `e` the west edge's line. The old version clamped `x`, `y`, `w`\n * and `h` one at a time after deriving the second axis, so once the derived axis ran\n * into the image edge the rect slid AWAY from its anchor and, past the far bound, off\n * the image entirely.\n *\n * With a ratio the size is the largest along that ratio which fits from the anchor —\n * the smaller of the width-limited and height-limited sizes — and no smaller than both\n * minimums allow. Where the image cannot fit the minimum from this anchor, the bound\n * wins: a crop past the edge of the image is worse than one under the minimum.\n *\n * An edge handle with a ratio also sizes the axis it does not drive, and that axis has\n * the image's WHOLE extent to grow into: the rect stays centred on its old midline where\n * it can and slides along that axis only as far as it must to stay on the image. Capping\n * the size at twice the distance from the midline to the nearer side instead would leave\n * a square crop resting against the top edge unable to widen at all.\n */\nfunction resizeFrom(\n r: RectPx,\n handle: WrCropHandle,\n dx: number,\n dy: number,\n bounds: SizePx,\n min: SizePx,\n ratio: number | null\n): RectPx {\n const east = handle.includes('e');\n const west = handle.includes('w');\n const south = handle.includes('s');\n const north = handle.includes('n');\n const right = r.x + r.w;\n const bottom = r.y + r.h;\n\n if (!ratio || ratio <= 0) {\n let { x, y, w, h } = r;\n if (east) w = clamp(r.w + dx, min.w, bounds.w - r.x);\n if (west) {\n w = clamp(r.w - dx, min.w, right);\n x = right - w;\n }\n if (south) h = clamp(r.h + dy, min.h, bounds.h - r.y);\n if (north) {\n h = clamp(r.h - dy, min.h, bottom);\n y = bottom - h;\n }\n return { x, y, w, h };\n }\n\n // Room from the anchor to the image edge on each axis the handle drives; the whole\n // image on an axis it does not.\n const roomW = east ? bounds.w - r.x : west ? right : bounds.w;\n const roomH = south ? bounds.h - r.y : north ? bottom : bounds.h;\n // Corners follow the horizontal delta, as they always have; `n` / `s` the vertical one.\n const wanted = east ? r.w + dx : west ? r.w - dx : (south ? r.h + dy : r.h - dy) * ratio;\n const w = clamp(wanted, Math.max(min.w, min.h * ratio), Math.min(roomW, roomH * ratio));\n const h = w / ratio;\n\n return {\n x: east ? r.x : west ? right - w : clamp(r.x + r.w / 2 - w / 2, 0, bounds.w - w),\n y: south ? r.y : north ? bottom - h : clamp(r.y + r.h / 2 - h / 2, 0, bounds.h - h),\n w,\n h,\n };\n}\n\n/**\n * Image crop UI. Pass `[src]` (URL, `File`, or `Blob`); the user drags\n * the crop window or any of its eight handles. Optionally lock the crop\n * to a fixed `[aspectRatio]`.\n *\n * The crop window is also the one tab stop: arrow keys move it, Alt with an\n * arrow key resizes it, and Shift makes either step ten pixels instead of one.\n *\n * `(cropped)` fires after each drag end — or each run of arrow keys — with a\n * freshly-rendered `Blob` of the cropped region. For one-off reads use\n * `toBlob()` / `toDataUrl()`.\n *\n * A source the browser cannot decode fires `(loadError)` once, sets `status()` to\n * `'failed'` and puts `wr-image-cropper--failed` on the host. The component renders\n * no message of its own for it — what went wrong and what to do next is the host's\n * to say.\n *\n * @example\n * ```html\n * <wr-image-cropper\n * [src]=\"file\"\n * [aspectRatio]=\"1\"\n * (cropped)=\"onBlob($event)\"\n * (loadError)=\"onUnreadable($event)\"\n * />\n * ```\n *\n * @see https://ngwr.dev/reference/components/image-cropper\n */\n@Component({\n selector: 'wr-image-cropper',\n templateUrl: './image-cropper.html',\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-image-cropper',\n '[class.wr-image-cropper--failed]': \"status() === 'failed'\",\n },\n})\nexport class WrImageCropper {\n /** Image source — URL string, `File`, or `Blob`. */\n readonly src = input<string | File | Blob | null>(null);\n\n /** Aspect ratio (width / height). `null` = free. @default null */\n readonly aspectRatio = input<number | null>(null);\n\n /** Minimum crop width in display pixels. @default 32 */\n readonly minWidth = input(32, { transform: (v: unknown): number => Math.max(8, coerceNumberProperty(v, 32)) });\n\n /** Minimum crop height in display pixels. @default 32 */\n readonly minHeight = input(32, { transform: (v: unknown): number => Math.max(8, coerceNumberProperty(v, 32)) });\n\n /** Default output type for `(cropped)`. @default 'image/png' */\n readonly outputType = input<WrImageOutputType>('image/png');\n\n /** JPEG / WebP quality for `(cropped)` in [0, 1]. @default 0.92 */\n readonly outputQuality = input(0.92);\n\n /**\n * Longest side of the exported image, in pixels — for `(cropped)`, `toBlob()` and\n * `toDataUrl()` alike. A crop larger than this is scaled down to fit, keeping its\n * aspect ratio; a smaller one is exported as it is, never upscaled. The crop itself\n * — `cropRect()`, in source pixels — is not affected. `null` (or anything that is\n * not a positive number) exports at the source image's own resolution.\n * @default null\n */\n readonly maxOutputSize = input<number | null>(null, { transform: coerceMaxOutputSize });\n\n /** Emits a Blob after each drag end. */\n readonly cropped = output<Blob>();\n\n /**\n * Fires once when the browser cannot decode the current `src` — a format it does\n * not support, a broken file, a URL that fails. Nothing is rendered for it; show\n * your own message. A new `src` clears the failure.\n */\n readonly loadError = output<WrImageLoadError>();\n\n protected readonly imgEl = viewChild.required<ElementRef<HTMLImageElement>>('img');\n\n /** Resolved object URL for `src` (so File / Blob render in `<img>`). */\n protected readonly emptyText = useI18nText(NO_OVERRIDE, 'imageCropper.empty', 'No image');\n\n /** Accessible name of the crop window — the component's only tab stop. */\n protected readonly resolvedWindowLabel = useI18nText(NO_OVERRIDE, 'imageCropper.window', 'Crop region');\n\n /** The key model, referenced by the window's `aria-describedby`. */\n protected readonly resolvedKeyHelp = useI18nText(\n NO_OVERRIDE,\n 'imageCropper.keyHelp',\n 'Arrow keys move the crop. Hold Alt with an arrow key to resize it, and Shift for larger steps. ' +\n 'The crop is announced as left, top, width and height in image pixels.'\n );\n\n /** Id linking the crop window to the help text. Random rather than counted:\n * the crop UI only exists after a real image load, so it never prerenders. */\n protected readonly helpId = randomId('wr-image-cropper-help');\n\n /** Live-region text — written by the keyboard path only (see `onWindowKeydown`). */\n protected readonly announcement = signal<string>('');\n\n protected readonly objectUrl = signal<string | null>(null);\n\n /** Previous object URL we created — kept off-signal so the resolve\n * effect doesn't depend on its own writes (which would loop). */\n private previousObjectUrl: string | null = null;\n\n /** Natural (source) pixel dimensions. */\n protected readonly natural = signal<{ w: number; h: number }>({ w: 0, h: 0 });\n\n /** Display (rendered) pixel dimensions. */\n protected readonly display = signal<{ w: number; h: number }>({ w: 0, h: 0 });\n\n /** Crop rect in display coordinates. */\n protected readonly cropDisplay = signal<RectPx>({ x: 0, y: 0, w: 0, h: 0 });\n\n /** Watches the rendered image box — see `watchResize`. */\n private resizeObserver: ResizeObserver | null = null;\n\n /** Currently active drag handle. */\n private active: WrCropHandle | null = null;\n private startPointer: { x: number; y: number } = { x: 0, y: 0 };\n private startRect: RectPx = { x: 0, y: 0, w: 0, h: 0 };\n\n /** The element the live drag set pointer capture on, so the end releases that one. */\n private captureTarget: HTMLElement | null = null;\n\n /** The pointer the live drag belongs to. Every other pointer's events are ignored. */\n private pointerId: number | null = null;\n\n /** How far past each side of the crop the live resize was pressed — see `onPointerMove`. */\n private overhang: SidesPx = NO_SIDES;\n\n private readonly destroyRef = inject(DestroyRef);\n\n private readonly statusState = signal<WrImageCropperStatus>('empty');\n\n /**\n * Where the source stands: `'empty'`, `'loading'`, `'loaded'` or `'failed'`. Reset\n * to `'loading'` (or `'empty'`) whenever `src` changes, so a failure never outlives\n * the source that caused it.\n */\n readonly status = this.statusState.asReadonly();\n\n /** Resolved crop rect in natural (source) pixel coordinates. */\n readonly cropRect = computed<WrCropRect>(() => {\n const display = this.display();\n const natural = this.natural();\n const c = this.cropDisplay();\n if (display.w === 0 || display.h === 0) return { x: 0, y: 0, width: 0, height: 0 };\n const sx = natural.w / display.w;\n const sy = natural.h / display.h;\n return {\n x: Math.round(c.x * sx),\n y: Math.round(c.y * sy),\n width: Math.round(c.w * sx),\n height: Math.round(c.h * sy),\n };\n });\n\n constructor() {\n // Resolve the source into a usable URL the <img> can render.\n // We only read `src()` here — `previousObjectUrl` is a plain field, so\n // writing to `objectUrl` doesn't re-trigger this effect (and hang the\n // tab on file upload).\n effect(() => {\n const src = this.src();\n if (this.previousObjectUrl) {\n URL.revokeObjectURL(this.previousObjectUrl);\n this.previousObjectUrl = null;\n }\n // The crop is measured against the image that WAS showing, and `cropRect` is a\n // public signal a consumer can read at any moment. Left in place it kept\n // reporting a rect scaled to the previous image for the whole gap between a new\n // `src` and its load event — long enough to crop the wrong thing.\n this.resetGeometry();\n if (!src) {\n this.objectUrl.set(null);\n this.statusState.set('empty');\n return;\n }\n // A new source starts over: a failure belongs to the source that caused it, and\n // left in place it would mark a perfectly good next image as broken until the\n // browser answered for that one too.\n this.statusState.set('loading');\n if (typeof src === 'string') {\n this.objectUrl.set(src);\n } else {\n const url = URL.createObjectURL(src);\n this.previousObjectUrl = url;\n this.objectUrl.set(url);\n }\n });\n\n this.destroyRef.onDestroy(() => {\n if (this.previousObjectUrl) URL.revokeObjectURL(this.previousObjectUrl);\n this.resizeObserver?.disconnect();\n });\n }\n\n // Image load\n\n /**\n * Back to \"nothing measured yet\", which is also what an absent `src` means.\n *\n * `display` is the field that gates everything downstream — the crop UI renders on\n * `display().w > 0` and `cropRect` returns zeros without it — so it is the only one\n * a test can observe. The other two are reset for coherence: the resize observer\n * below also writes `display`, and left alone they would resurrect a crop measured\n * against the previous image (which is why `onResize` refuses to run on a zeroed\n * `display` — see there).\n */\n private resetGeometry(): void {\n this.natural.set({ w: 0, h: 0 });\n this.display.set({ w: 0, h: 0 });\n this.cropDisplay.set({ x: 0, y: 0, w: 0, h: 0 });\n }\n\n protected onImageLoad(): void {\n const img = this.imgEl().nativeElement;\n const rect = img.getBoundingClientRect();\n const display = { w: rect.width, h: rect.height };\n // Some SVGs and odd images report zero natural dimensions — fall back\n // to the rendered size so the crop math stays well-defined.\n const natural = {\n w: img.naturalWidth || display.w,\n h: img.naturalHeight || display.h,\n };\n this.statusState.set('loaded');\n this.natural.set(natural);\n this.display.set(display);\n this.cropDisplay.set(this.initialCrop(display));\n this.watchResize(img);\n }\n\n /**\n * The `<img>` could not decode its source.\n *\n * Before this handler existed a failed decode left the component exactly as it\n * looks while loading — an image with no size, no crop window, nothing emitted —\n * so a host had no way to tell \"still decoding\" from \"never going to\", and a user\n * who picked a `.heic` the browser cannot read saw an empty box and no reason.\n *\n * Once per source: `status` is reset by a new `src` and by nothing else, so a\n * second `error` for the same source says nothing new and is not re-emitted.\n */\n protected onImageError(): void {\n const url = this.objectUrl();\n if (!url || this.statusState() === 'failed') return;\n this.statusState.set('failed');\n this.resetGeometry();\n\n const src = this.src();\n const isBlob = typeof src !== 'string' && src !== null;\n this.loadError.emit({\n url,\n name: typeof File !== 'undefined' && src instanceof File ? src.name : null,\n type: isBlob ? src.type : null,\n size: isBlob ? src.size : null,\n });\n }\n\n /**\n * Keep `display` on the box the image is actually rendered at.\n *\n * The stylesheet sizes the image responsively — `max-width: 100%`,\n * `max-height: 70dvh`, `height: auto` — so the single measurement taken in the\n * `(load)` handler stops being true as soon as the container or the viewport\n * changes. Everything the user sees and touches is in DISPLAY pixels: the crop\n * window's inline `left` / `top` / `width` / `height`, the backdrop cut-out, and\n * the bounds `applyMove` / `applyResize` clamp against. Measured once, a narrowed\n * window leaves the crop painted past the edge of the image and draggable off it.\n *\n * Re-observed per load rather than once, because a new `src` may mount a new\n * `<img>`; the old observation would then be on a detached element.\n */\n private watchResize(el: HTMLImageElement): void {\n if (typeof ResizeObserver === 'undefined') return;\n this.resizeObserver?.disconnect();\n // Re-read the same way `onImageLoad` does rather than take the entry's\n // `contentRect`, so the two measurements can't disagree about the box.\n const ro = new ResizeObserver(() => this.onResize(el));\n ro.observe(el);\n this.resizeObserver = ro;\n }\n\n /**\n * Rescale the crop by however much the image did, so the user keeps the region\n * they picked instead of having it re-centred: `cropRect()` — the source-pixel\n * value a consumer acts on — comes out the same across the resize.\n */\n private onResize(el: HTMLImageElement): void {\n const previous = this.display();\n // Nothing measured yet, or measured and then dropped by `resetGeometry` while a\n // new `src` loads. There is no crop to carry over, and the load will measure.\n if (previous.w === 0 || previous.h === 0) return;\n const rect = el.getBoundingClientRect();\n // A hidden image (a closed tab, `display: none`) reports zeros. Scaling by that\n // would collapse the crop and there would be nothing to scale back from, so hold\n // the last real box until it is shown again.\n if (rect.width === 0 || rect.height === 0) return;\n if (rect.width === previous.w && rect.height === previous.h) return;\n\n const sx = rect.width / previous.w;\n const sy = rect.height / previous.h;\n const c = this.cropDisplay();\n this.display.set({ w: rect.width, h: rect.height });\n this.cropDisplay.set({ x: c.x * sx, y: c.y * sy, w: c.w * sx, h: c.h * sy });\n }\n\n /** Compute a sensible initial crop — centered, respecting aspectRatio. */\n private initialCrop(display: { w: number; h: number }): RectPx {\n const ratio = this.aspectRatio();\n let w = display.w * 0.6;\n let h = display.h * 0.6;\n if (ratio && ratio > 0) {\n const candidate = w / ratio;\n if (candidate > h) w = h * ratio;\n else h = w / ratio;\n }\n return {\n x: (display.w - w) / 2,\n y: (display.h - h) / 2,\n w,\n h,\n };\n }\n\n // Drag handlers\n\n protected readonly handles: readonly WrCropHandle[] = ['n', 's', 'e', 'w', 'ne', 'nw', 'se', 'sw'];\n\n protected onPointerDown(handle: WrCropHandle, event: PointerEvent): void {\n // Any pointerdown used to start a drag, so the right button moved the crop window and\n // a second finger could take over a drag already in progress.\n if (event.button !== 0 || !event.isPrimary) return;\n event.preventDefault();\n event.stopPropagation();\n this.active = handle;\n this.pointerId = event.pointerId;\n this.startPointer = { x: event.clientX, y: event.clientY };\n this.startRect = { ...this.cropDisplay() };\n this.captureTarget = event.currentTarget as HTMLElement;\n this.captureTarget.setPointerCapture(event.pointerId);\n this.overhang = handle === 'move' ? NO_SIDES : this.pastCrop(event);\n }\n\n /**\n * A resize ENDS when the pointer leaves the image, exactly as `pointerup` would end it:\n * the move that carried it out is applied — clamped to the image like any other — and\n * then the drag is over. Nothing after that resumes it or changes the crop, pointer\n * back over the image or not, until a new `pointerdown`. A move drag is not affected:\n * its size is fixed, so it simply rests against the edge.\n *\n * The move that leaves is applied rather than dropped because pointer moves are\n * SAMPLES. Dropped, a quick drag toward a corner would stop wherever its last sample\n * inside happened to fall — a single move from a handle to past the image would change\n * nothing at all — and even a slow one would stop a fraction of a pixel short of the\n * edge. A drag that leaves across one edge still ends there: heading for a corner at a\n * slant that crosses the bottom first, the crop's right edge stays where it was then.\n *\n * \"The image\" is the `<img>`'s own box — the bounds `applyMove` / `applyResize` clamp\n * the crop to — with its edges counting as inside, since the crop may reach them. The\n * box is re-measured on every move and compared with `clientX` / `clientY`: both are\n * viewport coordinates, so a scroll mid-drag cannot put them out of step. The moves\n * outside reach this handler at all only because the drag holds pointer capture.\n *\n * One allowance, fixed at `pointerdown`: the box is widened on each side by however far\n * past that side of the CROP the press landed — at most as far as the pressed handle\n * reaches past it. Half of an edge handle, and three quarters of a corner handle, lies\n * outside the crop, so a press there already has the pointer ahead of the edge it drags.\n * Measured without the allowance, that pointer would leave the image while the crop was\n * still short of the image's edge by the same distance, with nothing able to close the\n * gap; and a handle resting on the image's edge, pressed on its outer part, would begin\n * its drag already outside. A press on a handle's inner part widens nothing.\n */\n protected onPointerMove(event: PointerEvent): void {\n if (!this.active || event.pointerId !== this.pointerId) return;\n const dx = event.clientX - this.startPointer.x;\n const dy = event.clientY - this.startPointer.y;\n if (this.active === 'move') {\n this.applyMove(dx, dy);\n return;\n }\n this.applyResize(this.active, dx, dy);\n if (!this.withinImage(event)) this.onPointerUp(event);\n }\n\n /**\n * Ends the drag — for `pointerup`, `pointercancel`, a capture lost to something else,\n * and a resize whose pointer left the image. Once per drag: the capture released here\n * fires `lostpointercapture` afterwards, and by then there is nothing left to end.\n *\n * Only for the pointer that started it. A second finger is refused a drag of its own at\n * `pointerdown`, but its moves and its lifting reach these handlers all the same, and\n * would otherwise drive or end the first finger's drag.\n */\n protected onPointerUp(event: PointerEvent): void {\n if (!this.active || event.pointerId !== this.pointerId) return;\n const target = this.captureTarget;\n this.active = null;\n this.pointerId = null;\n this.captureTarget = null;\n this.overhang = NO_SIDES;\n // Asked first: a capture that is already gone has nothing to release, and releasing\n // one for a pointer that is no longer active throws.\n if (target?.hasPointerCapture(event.pointerId)) target.releasePointerCapture(event.pointerId);\n void this.emitCropped();\n }\n\n /** How far past each side of the crop window the pointer is, in CSS pixels. */\n private pastCrop(event: PointerEvent): SidesPx {\n const box = this.imgEl().nativeElement.getBoundingClientRect();\n const c = this.startRect;\n return {\n left: Math.max(0, box.left + c.x - event.clientX),\n top: Math.max(0, box.top + c.y - event.clientY),\n right: Math.max(0, event.clientX - (box.left + c.x + c.w)),\n bottom: Math.max(0, event.clientY - (box.top + c.y + c.h)),\n };\n }\n\n /** Whether the pointer is on the image, edges and the press's `overhang` included. */\n private withinImage(event: PointerEvent): boolean {\n const box = this.imgEl().nativeElement.getBoundingClientRect();\n const o = this.overhang;\n return (\n event.clientX >= box.left - o.left &&\n event.clientX <= box.right + o.right &&\n event.clientY >= box.top - o.top &&\n event.clientY <= box.bottom + o.bottom\n );\n }\n\n // Keyboard\n\n /** Whether a key has moved the crop since the last `keyup`. */\n private keyboardDirty = false;\n\n /**\n * The component's only keyboard path. Arrow keys move the crop window; Alt\n * with an arrow key resizes it from the east / south edge; Shift makes either\n * one ten display pixels instead of one — the coarse modifier `wr-slider`,\n * `wr-knob` and `wr-splitter` all use, which is why Alt and not Shift carries\n * the mode switch here.\n *\n * Nothing mirrors under `dir=\"rtl\"`: the image is not flipped and the window\n * is placed with a physical `left`, so ArrowLeft moves the crop visually left\n * in both directions.\n *\n * Both branches go through `applyMove` / `applyResize`, so the `aspectRatio`\n * lock, the `minWidth` / `minHeight` floors and the canvas bounds hold for a\n * keystroke exactly as they do for a drag. Those two measure from `startRect`\n * rather than from the live rect, so it is re-seeded per keystroke — a 1px\n * delta against the rect from the first press would make a held arrow stall\n * after one pixel instead of accumulating.\n */\n protected onWindowKeydown(event: KeyboardEvent): void {\n const step = event.shiftKey ? KEY_STEP_COARSE : KEY_STEP;\n let dx = 0;\n let dy = 0;\n switch (event.key) {\n case 'ArrowLeft':\n dx = -step;\n break;\n case 'ArrowRight':\n dx = step;\n break;\n case 'ArrowUp':\n dy = -step;\n break;\n case 'ArrowDown':\n dy = step;\n break;\n default:\n return;\n }\n\n event.preventDefault();\n this.startRect = { ...this.cropDisplay() };\n if (event.altKey) this.applyResize(dx !== 0 ? 'e' : 's', dx, dy);\n else this.applyMove(dx, dy);\n\n this.keyboardDirty = true;\n const c = this.cropRect();\n // Bare numbers on purpose — `resolvedKeyHelp` has already told the reader\n // what the four are, and a sentence here would be a translated string.\n this.announcement.set(`${c.x}, ${c.y}, ${c.width} × ${c.height}`);\n }\n\n /**\n * Emit once per gesture, not once per keystroke. A held arrow repeats its\n * keydown many times a second and each `emitCropped` renders a canvas and\n * encodes a Blob; `keyup` fires once when the run ends, which is the keyboard's\n * equivalent of the `pointerup` the drag path emits on.\n */\n protected onWindowKeyup(): void {\n if (!this.keyboardDirty) return;\n this.keyboardDirty = false;\n void this.emitCropped();\n }\n\n private applyMove(dx: number, dy: number): void {\n const display = this.display();\n const r = this.startRect;\n const next = {\n x: clamp(r.x + dx, 0, display.w - r.w),\n y: clamp(r.y + dy, 0, display.h - r.h),\n w: r.w,\n h: r.h,\n };\n this.cropDisplay.set(next);\n }\n\n private applyResize(handle: WrCropHandle, dx: number, dy: number): void {\n const min = { w: this.minWidth(), h: this.minHeight() };\n this.cropDisplay.set(resizeFrom(this.startRect, handle, dx, dy, this.display(), min, this.aspectRatio()));\n }\n\n // Public API\n\n /** Render the current crop as a Blob. */\n async toBlob(type: WrImageOutputType = this.outputType(), quality: number = this.outputQuality()): Promise<Blob> {\n const canvas = this.toCanvas();\n return new Promise<Blob>((resolve, reject) => {\n canvas.toBlob(\n blob => {\n if (blob) resolve(blob);\n else reject(new Error('toBlob returned null'));\n },\n type,\n quality\n );\n });\n }\n\n /** Render the current crop as a data URL. */\n toDataUrl(type: WrImageOutputType = this.outputType(), quality: number = this.outputQuality()): string {\n return this.toCanvas().toDataURL(type, quality);\n }\n\n /** Re-emit the crop without waiting for a drag (e.g. after a programmatic change). */\n async refresh(): Promise<void> {\n await this.emitCropped();\n }\n\n // Internals\n\n /**\n * The exported image's pixel size: the crop in source pixels, scaled down so its\n * longer side fits `maxOutputSize`. `Math.min(1, …)` is the never-upscale rule — a\n * crop already inside the cap keeps its own size, which is also the whole of the\n * uncapped path.\n */\n private outputSize(c: WrCropRect): { width: number; height: number } {\n const cap = this.maxOutputSize();\n const longest = Math.max(c.width, c.height);\n const scale = cap === null || longest === 0 ? 1 : Math.min(1, cap / longest);\n return {\n width: Math.max(1, Math.round(c.width * scale)),\n height: Math.max(1, Math.round(c.height * scale)),\n };\n }\n\n private toCanvas(): HTMLCanvasElement {\n const img = this.imgEl().nativeElement;\n const c = this.cropRect();\n const canvas = document.createElement('canvas');\n const size = this.outputSize(c);\n canvas.width = size.width;\n canvas.height = size.height;\n // The SOURCE rect stays the whole crop and the destination is the canvas, so a\n // capped export is the same region drawn smaller rather than a smaller region.\n const ctx = canvas.getContext('2d');\n if (ctx) ctx.drawImage(img, c.x, c.y, c.width, c.height, 0, 0, canvas.width, canvas.height);\n return canvas;\n }\n\n private async emitCropped(): Promise<void> {\n try {\n const blob = await this.toBlob();\n this.cropped.emit(blob);\n } catch {\n // Swallow — invalid state (image not loaded, crop empty, etc.).\n }\n }\n}\n","@if (objectUrl(); as url) {\n <div class=\"wr-image-cropper__canvas\">\n <img\n #img\n class=\"wr-image-cropper__image\"\n [src]=\"url\"\n alt=\"\"\n draggable=\"false\"\n (load)=\"onImageLoad()\"\n (error)=\"onImageError()\"\n />\n\n @if (display().w > 0) {\n <!-- Semi-transparent backdrop with a cut-out for the crop window. -->\n <div\n class=\"wr-image-cropper__backdrop\"\n [style.clip-path]=\"`polygon(0 0, 100% 0, 100% 100%, 0 100%, 0 ${cropDisplay().y}px, ${cropDisplay().x}px ${cropDisplay().y}px, ${cropDisplay().x}px ${cropDisplay().y + cropDisplay().h}px, ${cropDisplay().x + cropDisplay().w}px ${cropDisplay().y + cropDisplay().h}px, ${cropDisplay().x + cropDisplay().w}px ${cropDisplay().y}px, 0 ${cropDisplay().y}px)`\"\n ></div>\n\n <!--\n Crop window — drag to move, with eight resize handles, and the ONE\n keyboard control the component has (see `onWindowKeydown`). The eight\n handles stay pointer-only and `aria-hidden`: eight extra tab stops per\n cropper would be hostile. The keys resize from the east and south edges\n only, so a west or north drag comes out as a resize plus a move rather\n than as one gesture.\n\n `role=\"group\"` rather than no role at all, because `aria-label` is\n prohibited on a roleless `<div>` and this needs a name; and rather than\n `role=\"application\"`, which would switch the reader out of its own\n browse mode for the whole subtree — far more than one rectangle is worth.\n -->\n <div\n class=\"wr-image-cropper__window\"\n role=\"group\"\n tabindex=\"0\"\n [attr.aria-label]=\"resolvedWindowLabel()\"\n [attr.aria-describedby]=\"helpId\"\n [style.left.px]=\"cropDisplay().x\"\n [style.top.px]=\"cropDisplay().y\"\n [style.width.px]=\"cropDisplay().w\"\n [style.height.px]=\"cropDisplay().h\"\n (pointerdown)=\"onPointerDown('move', $event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n (lostpointercapture)=\"onPointerUp($event)\"\n (keydown)=\"onWindowKeydown($event)\"\n (keyup)=\"onWindowKeyup()\"\n >\n @for (h of handles; track h) {\n <span\n aria-hidden=\"true\"\n [class]=\"`wr-image-cropper__handle wr-image-cropper__handle--${h}`\"\n (pointerdown)=\"onPointerDown(h, $event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n (lostpointercapture)=\"onPointerUp($event)\"\n ></span>\n }\n </div>\n\n <p class=\"wr-image-cropper__sr-only\" [id]=\"helpId\">{{ resolvedKeyHelp() }}</p>\n\n <!--\n The crop rect in SOURCE pixels, announced after a keyboard change only —\n a pointer drag already gives sighted feedback and would flood the region,\n and the initial crop is not news. Deliberately bare numbers: a sentence\n here would be a translated string the catalog does not carry, and the\n reader has just been told what the four numbers mean by `helpId`.\n -->\n <p class=\"wr-image-cropper__sr-only\" role=\"status\">{{ announcement() }}</p>\n }\n </div>\n} @else {\n <div class=\"wr-image-cropper__empty\">{{ emptyText() }}</div>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ngwr-image-cropper.mjs","sources":["../../../projects/lib/image-cropper/image-cropper.ts","../../../projects/lib/image-cropper/image-cropper.html","../../../projects/lib/image-cropper/ngwr-image-cropper.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 { coerceNumberProperty } from '@angular/cdk/coercion';\nimport {\n Component,\n DestroyRef,\n type ElementRef,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n input,\n output,\n signal,\n viewChild,\n} from '@angular/core';\n\nimport { useI18nText } from 'ngwr/i18n';\nimport { clamp, randomId } from 'ngwr/utils';\n\nimport type { WrCropHandle, WrCropRect, WrImageCropperStatus, WrImageLoadError, WrImageOutputType } from './interfaces';\n\n/**\n * `[maxOutputSize]`'s transform. Anything that is not a positive, finite number —\n * `null`, an empty attribute, `0`, a typo — means \"no cap\" rather than a 1px export:\n * a bad config should cost resolution nobody asked to lose, not the whole image.\n */\nfunction coerceMaxOutputSize(value: unknown): number | null {\n if (value === null || value === undefined || value === '') return null;\n const n = coerceNumberProperty(value, Number.NaN);\n return Number.isFinite(n) && n > 0 ? n : null;\n}\n\n/**\n * Stands in for the consumer input `useI18nText` expects. None of this\n * component's three strings has a `*Label` input behind it — the catalog is the\n * override channel — so they share one empty override.\n */\nconst NO_OVERRIDE = signal<string | null>(null).asReadonly();\n\n/** Keyboard step in display pixels, and what Shift promotes it to. */\nconst KEY_STEP = 1;\nconst KEY_STEP_COARSE = 10;\n\ninterface RectPx {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\ninterface SizePx {\n w: number;\n h: number;\n}\n\n/** Distances, in CSS pixels, past each side of a box. */\ninterface SidesPx {\n left: number;\n top: number;\n right: number;\n bottom: number;\n}\n\nconst NO_SIDES: SidesPx = { left: 0, top: 0, right: 0, bottom: 0 };\n\n/**\n * The rect a resize gesture produces: a pure function of the rect the gesture started\n * from and the TOTAL pointer offset, so the same pointer position always gives the same\n * rect however the pointer got there.\n *\n * The anchor never moves. It is the edge or corner opposite the handle — for `se` the\n * top-left corner, for `e` the west edge's line. The old version clamped `x`, `y`, `w`\n * and `h` one at a time after deriving the second axis, so once the derived axis ran\n * into the image edge the rect slid AWAY from its anchor and, past the far bound, off\n * the image entirely.\n *\n * With a ratio the size is the largest along that ratio which fits from the anchor —\n * the smaller of the width-limited and height-limited sizes — and no smaller than both\n * minimums allow. Where the image cannot fit the minimum from this anchor, the bound\n * wins: a crop past the edge of the image is worse than one under the minimum.\n *\n * An edge handle with a ratio also sizes the axis it does not drive, and that axis has\n * the image's WHOLE extent to grow into: the rect stays centred on its old midline where\n * it can and slides along that axis only as far as it must to stay on the image. Capping\n * the size at twice the distance from the midline to the nearer side instead would leave\n * a square crop resting against the top edge unable to widen at all.\n */\nfunction resizeFrom(\n r: RectPx,\n handle: WrCropHandle,\n dx: number,\n dy: number,\n bounds: SizePx,\n min: SizePx,\n ratio: number | null\n): RectPx {\n const east = handle.includes('e');\n const west = handle.includes('w');\n const south = handle.includes('s');\n const north = handle.includes('n');\n const right = r.x + r.w;\n const bottom = r.y + r.h;\n\n if (!ratio || ratio <= 0) {\n let { x, y, w, h } = r;\n if (east) w = clamp(r.w + dx, min.w, bounds.w - r.x);\n if (west) {\n w = clamp(r.w - dx, min.w, right);\n x = right - w;\n }\n if (south) h = clamp(r.h + dy, min.h, bounds.h - r.y);\n if (north) {\n h = clamp(r.h - dy, min.h, bottom);\n y = bottom - h;\n }\n return { x, y, w, h };\n }\n\n // Room from the anchor to the image edge on each axis the handle drives; the whole\n // image on an axis it does not.\n const roomW = east ? bounds.w - r.x : west ? right : bounds.w;\n const roomH = south ? bounds.h - r.y : north ? bottom : bounds.h;\n // Corners follow the horizontal delta, as they always have; `n` / `s` the vertical one.\n const wanted = east ? r.w + dx : west ? r.w - dx : (south ? r.h + dy : r.h - dy) * ratio;\n const w = clamp(wanted, Math.max(min.w, min.h * ratio), Math.min(roomW, roomH * ratio));\n const h = w / ratio;\n\n return {\n x: east ? r.x : west ? right - w : clamp(r.x + r.w / 2 - w / 2, 0, bounds.w - w),\n y: south ? r.y : north ? bottom - h : clamp(r.y + r.h / 2 - h / 2, 0, bounds.h - h),\n w,\n h,\n };\n}\n\n/**\n * Image crop UI. Pass `[src]` (URL, `File`, or `Blob`); the user drags\n * the crop window or any of its eight handles. Optionally lock the crop\n * to a fixed `[aspectRatio]`.\n *\n * The crop window is also the one tab stop: arrow keys move it, Alt with an\n * arrow key resizes it, and Shift makes either step ten pixels instead of one.\n *\n * `(cropped)` fires after each drag end — or each run of arrow keys — with a\n * freshly-rendered `Blob` of the cropped region. For one-off reads use\n * `toBlob()` / `toDataUrl()`.\n *\n * A source the browser cannot decode fires `(loadError)` once, sets `status()` to\n * `'failed'` and puts `wr-image-cropper--failed` on the host. The component renders\n * no message of its own for it — what went wrong and what to do next is the host's\n * to say.\n *\n * @example\n * ```html\n * <wr-image-cropper\n * [src]=\"file\"\n * [aspectRatio]=\"1\"\n * (cropped)=\"onBlob($event)\"\n * (loadError)=\"onUnreadable($event)\"\n * />\n * ```\n *\n * @see https://ngwr.dev/reference/components/image-cropper\n */\n@Component({\n selector: 'wr-image-cropper',\n templateUrl: './image-cropper.html',\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-image-cropper',\n '[class.wr-image-cropper--failed]': \"status() === 'failed'\",\n },\n})\nexport class WrImageCropper {\n /** Image source — URL string, `File`, or `Blob`. */\n readonly src = input<string | File | Blob | null>(null);\n\n /** Aspect ratio (width / height). `null` = free. @default null */\n readonly aspectRatio = input<number | null>(null);\n\n /** Minimum crop width in display pixels. @default 32 */\n readonly minWidth = input(32, { transform: (v: unknown): number => Math.max(8, coerceNumberProperty(v, 32)) });\n\n /** Minimum crop height in display pixels. @default 32 */\n readonly minHeight = input(32, { transform: (v: unknown): number => Math.max(8, coerceNumberProperty(v, 32)) });\n\n /** Default output type for `(cropped)`. @default 'image/png' */\n readonly outputType = input<WrImageOutputType>('image/png');\n\n /** JPEG / WebP quality for `(cropped)` in [0, 1]. @default 0.92 */\n readonly outputQuality = input(0.92);\n\n /**\n * Longest side of the exported image, in pixels — for `(cropped)`, `toBlob()` and\n * `toDataUrl()` alike. A crop larger than this is scaled down to fit, keeping its\n * aspect ratio; a smaller one is exported as it is, never upscaled. The crop itself\n * — `cropRect()`, in source pixels — is not affected. `null` (or anything that is\n * not a positive number) exports at the source image's own resolution.\n * @default null\n */\n readonly maxOutputSize = input<number | null>(null, { transform: coerceMaxOutputSize });\n\n /** Emits a Blob after each drag end. */\n readonly cropped = output<Blob>();\n\n /**\n * Fires once when the browser cannot decode the current `src` — a format it does\n * not support, a broken file, a URL that fails. Nothing is rendered for it; show\n * your own message. A new `src` clears the failure.\n */\n readonly loadError = output<WrImageLoadError>();\n\n protected readonly imgEl = viewChild.required<ElementRef<HTMLImageElement>>('img');\n\n /** Resolved object URL for `src` (so File / Blob render in `<img>`). */\n protected readonly emptyText = useI18nText(NO_OVERRIDE, 'imageCropper.empty', 'No image');\n\n /** Accessible name of the crop window — the component's only tab stop. */\n protected readonly resolvedWindowLabel = useI18nText(NO_OVERRIDE, 'imageCropper.window', 'Crop region');\n\n /** The key model, referenced by the window's `aria-describedby`. */\n protected readonly resolvedKeyHelp = useI18nText(\n NO_OVERRIDE,\n 'imageCropper.keyHelp',\n 'Arrow keys move the crop. Hold Alt with an arrow key to resize it, and Shift for larger steps. ' +\n 'The crop is announced as left, top, width and height in image pixels.'\n );\n\n /** Id linking the crop window to the help text. Random rather than counted:\n * the crop UI only exists after a real image load, so it never prerenders. */\n protected readonly helpId = randomId('wr-image-cropper-help');\n\n /** Live-region text — written by the keyboard path only (see `onWindowKeydown`). */\n protected readonly announcement = signal<string>('');\n\n protected readonly objectUrl = signal<string | null>(null);\n\n /** Previous object URL we created — kept off-signal so the resolve\n * effect doesn't depend on its own writes (which would loop). */\n private previousObjectUrl: string | null = null;\n\n /** Natural (source) pixel dimensions. */\n protected readonly natural = signal<{ w: number; h: number }>({ w: 0, h: 0 });\n\n /** Display (rendered) pixel dimensions. */\n protected readonly display = signal<{ w: number; h: number }>({ w: 0, h: 0 });\n\n /** Crop rect in display coordinates. */\n protected readonly cropDisplay = signal<RectPx>({ x: 0, y: 0, w: 0, h: 0 });\n\n /** Watches the rendered image box — see `watchResize`. */\n private resizeObserver: ResizeObserver | null = null;\n\n /** Currently active drag handle. */\n private active: WrCropHandle | null = null;\n private startPointer: { x: number; y: number } = { x: 0, y: 0 };\n private startRect: RectPx = { x: 0, y: 0, w: 0, h: 0 };\n\n /** The element the live drag set pointer capture on, so the end releases that one. */\n private captureTarget: HTMLElement | null = null;\n\n /** The pointer the live drag belongs to. Every other pointer's events are ignored. */\n private pointerId: number | null = null;\n\n /** How far past each side of the crop the live resize was pressed — see `onPointerMove`. */\n private overhang: SidesPx = NO_SIDES;\n\n private readonly destroyRef = inject(DestroyRef);\n\n private readonly statusState = signal<WrImageCropperStatus>('empty');\n\n /**\n * Where the source stands: `'empty'`, `'loading'`, `'loaded'` or `'failed'`. Reset\n * to `'loading'` (or `'empty'`) whenever `src` changes, so a failure never outlives\n * the source that caused it.\n */\n readonly status = this.statusState.asReadonly();\n\n /** Resolved crop rect in natural (source) pixel coordinates. */\n readonly cropRect = computed<WrCropRect>(() => {\n const display = this.display();\n const natural = this.natural();\n const c = this.cropDisplay();\n if (display.w === 0 || display.h === 0) return { x: 0, y: 0, width: 0, height: 0 };\n const sx = natural.w / display.w;\n const sy = natural.h / display.h;\n return {\n x: Math.round(c.x * sx),\n y: Math.round(c.y * sy),\n width: Math.round(c.w * sx),\n height: Math.round(c.h * sy),\n };\n });\n\n constructor() {\n // Resolve the source into a usable URL the <img> can render.\n // We only read `src()` here — `previousObjectUrl` is a plain field, so\n // writing to `objectUrl` doesn't re-trigger this effect (and hang the\n // tab on file upload).\n effect(() => {\n const src = this.src();\n if (this.previousObjectUrl) {\n URL.revokeObjectURL(this.previousObjectUrl);\n this.previousObjectUrl = null;\n }\n // The crop is measured against the image that WAS showing, and `cropRect` is a\n // public signal a consumer can read at any moment. Left in place it kept\n // reporting a rect scaled to the previous image for the whole gap between a new\n // `src` and its load event — long enough to crop the wrong thing.\n this.resetGeometry();\n if (!src) {\n this.objectUrl.set(null);\n this.statusState.set('empty');\n return;\n }\n // A new source starts over: a failure belongs to the source that caused it, and\n // left in place it would mark a perfectly good next image as broken until the\n // browser answered for that one too.\n this.statusState.set('loading');\n if (typeof src === 'string') {\n this.objectUrl.set(src);\n } else {\n const url = URL.createObjectURL(src);\n this.previousObjectUrl = url;\n this.objectUrl.set(url);\n }\n });\n\n this.destroyRef.onDestroy(() => {\n if (this.previousObjectUrl) URL.revokeObjectURL(this.previousObjectUrl);\n this.resizeObserver?.disconnect();\n });\n }\n\n // Image load\n\n /**\n * Back to \"nothing measured yet\", which is also what an absent `src` means.\n *\n * `display` is the field that gates everything downstream — the crop UI renders on\n * `display().w > 0` and `cropRect` returns zeros without it — so it is the only one\n * a test can observe. The other two are reset for coherence: the resize observer\n * below also writes `display`, and left alone they would resurrect a crop measured\n * against the previous image (which is why `onResize` refuses to run on a zeroed\n * `display` — see there).\n */\n private resetGeometry(): void {\n this.natural.set({ w: 0, h: 0 });\n this.display.set({ w: 0, h: 0 });\n this.cropDisplay.set({ x: 0, y: 0, w: 0, h: 0 });\n }\n\n protected onImageLoad(): void {\n const img = this.imgEl().nativeElement;\n const rect = img.getBoundingClientRect();\n const display = { w: rect.width, h: rect.height };\n // Some SVGs and odd images report zero natural dimensions — fall back\n // to the rendered size so the crop math stays well-defined.\n const natural = {\n w: img.naturalWidth || display.w,\n h: img.naturalHeight || display.h,\n };\n this.statusState.set('loaded');\n this.natural.set(natural);\n this.display.set(display);\n this.cropDisplay.set(this.initialCrop(display));\n this.watchResize(img);\n }\n\n /**\n * The `<img>` could not decode its source.\n *\n * Before this handler existed a failed decode left the component exactly as it\n * looks while loading — an image with no size, no crop window, nothing emitted —\n * so a host had no way to tell \"still decoding\" from \"never going to\", and a user\n * who picked a `.heic` the browser cannot read saw an empty box and no reason.\n *\n * Once per source: `status` is reset by a new `src` and by nothing else, so a\n * second `error` for the same source says nothing new and is not re-emitted.\n */\n protected onImageError(): void {\n const url = this.objectUrl();\n if (!url || this.statusState() === 'failed') return;\n this.statusState.set('failed');\n this.resetGeometry();\n\n const src = this.src();\n const isBlob = typeof src !== 'string' && src !== null;\n this.loadError.emit({\n url,\n name: typeof File !== 'undefined' && src instanceof File ? src.name : null,\n type: isBlob ? src.type : null,\n size: isBlob ? src.size : null,\n });\n }\n\n /**\n * Keep `display` on the box the image is actually rendered at.\n *\n * The stylesheet sizes the image responsively — `max-width: 100%`,\n * `max-height: 70dvh`, `height: auto` — so the single measurement taken in the\n * `(load)` handler stops being true as soon as the container or the viewport\n * changes. Everything the user sees and touches is in DISPLAY pixels: the crop\n * window's inline `left` / `top` / `width` / `height`, the backdrop cut-out, and\n * the bounds `applyMove` / `applyResize` clamp against. Measured once, a narrowed\n * window leaves the crop painted past the edge of the image and draggable off it.\n *\n * Re-observed per load rather than once, because a new `src` may mount a new\n * `<img>`; the old observation would then be on a detached element.\n */\n private watchResize(el: HTMLImageElement): void {\n if (typeof ResizeObserver === 'undefined') return;\n this.resizeObserver?.disconnect();\n // Re-read the same way `onImageLoad` does rather than take the entry's\n // `contentRect`, so the two measurements can't disagree about the box.\n const ro = new ResizeObserver(() => this.onResize(el));\n ro.observe(el);\n this.resizeObserver = ro;\n }\n\n /**\n * Rescale the crop by however much the image did, so the user keeps the region\n * they picked instead of having it re-centred: `cropRect()` — the source-pixel\n * value a consumer acts on — comes out the same across the resize.\n */\n private onResize(el: HTMLImageElement): void {\n const previous = this.display();\n // Nothing measured yet, or measured and then dropped by `resetGeometry` while a\n // new `src` loads. There is no crop to carry over, and the load will measure.\n if (previous.w === 0 || previous.h === 0) return;\n const rect = el.getBoundingClientRect();\n // A hidden image (a closed tab, `display: none`) reports zeros. Scaling by that\n // would collapse the crop and there would be nothing to scale back from, so hold\n // the last real box until it is shown again.\n if (rect.width === 0 || rect.height === 0) return;\n if (rect.width === previous.w && rect.height === previous.h) return;\n\n const sx = rect.width / previous.w;\n const sy = rect.height / previous.h;\n const c = this.cropDisplay();\n this.display.set({ w: rect.width, h: rect.height });\n this.cropDisplay.set({ x: c.x * sx, y: c.y * sy, w: c.w * sx, h: c.h * sy });\n }\n\n /** Compute a sensible initial crop — centered, respecting aspectRatio. */\n private initialCrop(display: { w: number; h: number }): RectPx {\n const ratio = this.aspectRatio();\n let w = display.w * 0.6;\n let h = display.h * 0.6;\n if (ratio && ratio > 0) {\n const candidate = w / ratio;\n if (candidate > h) w = h * ratio;\n else h = w / ratio;\n }\n return {\n x: (display.w - w) / 2,\n y: (display.h - h) / 2,\n w,\n h,\n };\n }\n\n // Drag handlers\n\n protected readonly handles: readonly WrCropHandle[] = ['n', 's', 'e', 'w', 'ne', 'nw', 'se', 'sw'];\n\n protected onPointerDown(handle: WrCropHandle, event: PointerEvent): void {\n // Any pointerdown used to start a drag, so the right button moved the crop window and\n // a second finger could take over a drag already in progress.\n if (event.button !== 0 || !event.isPrimary) return;\n event.preventDefault();\n event.stopPropagation();\n this.active = handle;\n this.pointerId = event.pointerId;\n this.startPointer = { x: event.clientX, y: event.clientY };\n this.startRect = { ...this.cropDisplay() };\n this.captureTarget = event.currentTarget as HTMLElement;\n this.captureTarget.setPointerCapture(event.pointerId);\n this.overhang = handle === 'move' ? NO_SIDES : this.pastCrop(event);\n }\n\n /**\n * A resize ENDS when the pointer leaves the image, exactly as `pointerup` would end it:\n * the move that carried it out is applied — clamped to the image like any other — and\n * then the drag is over. Nothing after that resumes it or changes the crop, pointer\n * back over the image or not, until a new `pointerdown`. A move drag is not affected:\n * its size is fixed, so it simply rests against the edge.\n *\n * The move that leaves is applied rather than dropped because pointer moves are\n * SAMPLES. Dropped, a quick drag toward a corner would stop wherever its last sample\n * inside happened to fall — a single move from a handle to past the image would change\n * nothing at all — and even a slow one would stop a fraction of a pixel short of the\n * edge. A drag that leaves across one edge still ends there: heading for a corner at a\n * slant that crosses the bottom first, the crop's right edge stays where it was then.\n *\n * \"The image\" is the `<img>`'s own box — the bounds `applyMove` / `applyResize` clamp\n * the crop to — with its edges counting as inside, since the crop may reach them. The\n * box is re-measured on every move and compared with `clientX` / `clientY`: both are\n * viewport coordinates, so a scroll mid-drag cannot put them out of step. The moves\n * outside reach this handler at all only because the drag holds pointer capture.\n *\n * One allowance, fixed at `pointerdown`: the box is widened on each side by however far\n * past that side of the CROP the press landed — at most as far as the pressed handle\n * reaches past it. Half of an edge handle, and three quarters of a corner handle, lies\n * outside the crop, so a press there already has the pointer ahead of the edge it drags.\n * Measured without the allowance, that pointer would leave the image while the crop was\n * still short of the image's edge by the same distance, with nothing able to close the\n * gap; and a handle resting on the image's edge, pressed on its outer part, would begin\n * its drag already outside. A press on a handle's inner part widens nothing.\n */\n protected onPointerMove(event: PointerEvent): void {\n if (!this.active || event.pointerId !== this.pointerId) return;\n const dx = event.clientX - this.startPointer.x;\n const dy = event.clientY - this.startPointer.y;\n if (this.active === 'move') {\n this.applyMove(dx, dy);\n return;\n }\n this.applyResize(this.active, dx, dy);\n if (!this.withinImage(event)) this.onPointerUp(event);\n }\n\n /**\n * Ends the drag — for `pointerup`, `pointercancel`, a capture lost to something else,\n * and a resize whose pointer left the image. Once per drag: the capture released here\n * fires `lostpointercapture` afterwards, and by then there is nothing left to end.\n *\n * Only for the pointer that started it. A second finger is refused a drag of its own at\n * `pointerdown`, but its moves and its lifting reach these handlers all the same, and\n * would otherwise drive or end the first finger's drag.\n */\n protected onPointerUp(event: PointerEvent): void {\n if (!this.active || event.pointerId !== this.pointerId) return;\n const target = this.captureTarget;\n this.active = null;\n this.pointerId = null;\n this.captureTarget = null;\n this.overhang = NO_SIDES;\n // Asked first: a capture that is already gone has nothing to release, and releasing\n // one for a pointer that is no longer active throws.\n if (target?.hasPointerCapture(event.pointerId)) target.releasePointerCapture(event.pointerId);\n void this.emitCropped();\n }\n\n /** How far past each side of the crop window the pointer is, in CSS pixels. */\n private pastCrop(event: PointerEvent): SidesPx {\n const box = this.imgEl().nativeElement.getBoundingClientRect();\n const c = this.startRect;\n return {\n left: Math.max(0, box.left + c.x - event.clientX),\n top: Math.max(0, box.top + c.y - event.clientY),\n right: Math.max(0, event.clientX - (box.left + c.x + c.w)),\n bottom: Math.max(0, event.clientY - (box.top + c.y + c.h)),\n };\n }\n\n /** Whether the pointer is on the image, edges and the press's `overhang` included. */\n private withinImage(event: PointerEvent): boolean {\n const box = this.imgEl().nativeElement.getBoundingClientRect();\n const o = this.overhang;\n return (\n event.clientX >= box.left - o.left &&\n event.clientX <= box.right + o.right &&\n event.clientY >= box.top - o.top &&\n event.clientY <= box.bottom + o.bottom\n );\n }\n\n // Keyboard\n\n /** Whether a key has moved the crop since the last `keyup`. */\n private keyboardDirty = false;\n\n /**\n * The component's only keyboard path. Arrow keys move the crop window; Alt\n * with an arrow key resizes it from the east / south edge; Shift makes either\n * one ten display pixels instead of one — the coarse modifier `wr-slider`,\n * `wr-knob` and `wr-splitter` all use, which is why Alt and not Shift carries\n * the mode switch here.\n *\n * Nothing mirrors under `dir=\"rtl\"`: the image is not flipped and the window\n * is placed with a physical `left`, so ArrowLeft moves the crop visually left\n * in both directions.\n *\n * Both branches go through `applyMove` / `applyResize`, so the `aspectRatio`\n * lock, the `minWidth` / `minHeight` floors and the canvas bounds hold for a\n * keystroke exactly as they do for a drag. Those two measure from `startRect`\n * rather than from the live rect, so it is re-seeded per keystroke — a 1px\n * delta against the rect from the first press would make a held arrow stall\n * after one pixel instead of accumulating.\n */\n protected onWindowKeydown(event: KeyboardEvent): void {\n const step = event.shiftKey ? KEY_STEP_COARSE : KEY_STEP;\n let dx = 0;\n let dy = 0;\n switch (event.key) {\n case 'ArrowLeft':\n dx = -step;\n break;\n case 'ArrowRight':\n dx = step;\n break;\n case 'ArrowUp':\n dy = -step;\n break;\n case 'ArrowDown':\n dy = step;\n break;\n default:\n return;\n }\n\n event.preventDefault();\n this.startRect = { ...this.cropDisplay() };\n if (event.altKey) this.applyResize(dx !== 0 ? 'e' : 's', dx, dy);\n else this.applyMove(dx, dy);\n\n this.keyboardDirty = true;\n const c = this.cropRect();\n // Bare numbers on purpose — `resolvedKeyHelp` has already told the reader\n // what the four are, and a sentence here would be a translated string.\n this.announcement.set(`${c.x}, ${c.y}, ${c.width} × ${c.height}`);\n }\n\n /**\n * Emit once per gesture, not once per keystroke. A held arrow repeats its\n * keydown many times a second and each `emitCropped` renders a canvas and\n * encodes a Blob; `keyup` fires once when the run ends, which is the keyboard's\n * equivalent of the `pointerup` the drag path emits on.\n */\n protected onWindowKeyup(): void {\n if (!this.keyboardDirty) return;\n this.keyboardDirty = false;\n void this.emitCropped();\n }\n\n private applyMove(dx: number, dy: number): void {\n const display = this.display();\n const r = this.startRect;\n const next = {\n x: clamp(r.x + dx, 0, display.w - r.w),\n y: clamp(r.y + dy, 0, display.h - r.h),\n w: r.w,\n h: r.h,\n };\n this.cropDisplay.set(next);\n }\n\n private applyResize(handle: WrCropHandle, dx: number, dy: number): void {\n const min = { w: this.minWidth(), h: this.minHeight() };\n this.cropDisplay.set(resizeFrom(this.startRect, handle, dx, dy, this.display(), min, this.aspectRatio()));\n }\n\n // Public API\n\n /** Render the current crop as a Blob. */\n async toBlob(type: WrImageOutputType = this.outputType(), quality: number = this.outputQuality()): Promise<Blob> {\n const canvas = this.toCanvas();\n return new Promise<Blob>((resolve, reject) => {\n canvas.toBlob(\n blob => {\n if (blob) resolve(blob);\n else reject(new Error('toBlob returned null'));\n },\n type,\n quality\n );\n });\n }\n\n /** Render the current crop as a data URL. */\n toDataUrl(type: WrImageOutputType = this.outputType(), quality: number = this.outputQuality()): string {\n return this.toCanvas().toDataURL(type, quality);\n }\n\n /** Re-emit the crop without waiting for a drag (e.g. after a programmatic change). */\n async refresh(): Promise<void> {\n await this.emitCropped();\n }\n\n // Internals\n\n /**\n * The exported image's pixel size: the crop in source pixels, scaled down so its\n * longer side fits `maxOutputSize`. `Math.min(1, …)` is the never-upscale rule — a\n * crop already inside the cap keeps its own size, which is also the whole of the\n * uncapped path.\n */\n private outputSize(c: WrCropRect): { width: number; height: number } {\n const cap = this.maxOutputSize();\n const longest = Math.max(c.width, c.height);\n const scale = cap === null || longest === 0 ? 1 : Math.min(1, cap / longest);\n return {\n width: Math.max(1, Math.round(c.width * scale)),\n height: Math.max(1, Math.round(c.height * scale)),\n };\n }\n\n private toCanvas(): HTMLCanvasElement {\n const img = this.imgEl().nativeElement;\n const c = this.cropRect();\n const canvas = document.createElement('canvas');\n const size = this.outputSize(c);\n canvas.width = size.width;\n canvas.height = size.height;\n // The SOURCE rect stays the whole crop and the destination is the canvas, so a\n // capped export is the same region drawn smaller rather than a smaller region.\n const ctx = canvas.getContext('2d');\n if (ctx) ctx.drawImage(img, c.x, c.y, c.width, c.height, 0, 0, canvas.width, canvas.height);\n return canvas;\n }\n\n private async emitCropped(): Promise<void> {\n try {\n const blob = await this.toBlob();\n this.cropped.emit(blob);\n } catch {\n // Swallow — invalid state (image not loaded, crop empty, etc.).\n }\n }\n}\n","@if (objectUrl(); as url) {\n <div class=\"wr-image-cropper__canvas\">\n <img\n #img\n class=\"wr-image-cropper__image\"\n [src]=\"url\"\n alt=\"\"\n draggable=\"false\"\n (load)=\"onImageLoad()\"\n (error)=\"onImageError()\"\n />\n\n @if (display().w > 0) {\n <!-- Semi-transparent backdrop with a cut-out for the crop window. -->\n <div\n class=\"wr-image-cropper__backdrop\"\n [style.clip-path]=\"`polygon(0 0, 100% 0, 100% 100%, 0 100%, 0 ${cropDisplay().y}px, ${cropDisplay().x}px ${cropDisplay().y}px, ${cropDisplay().x}px ${cropDisplay().y + cropDisplay().h}px, ${cropDisplay().x + cropDisplay().w}px ${cropDisplay().y + cropDisplay().h}px, ${cropDisplay().x + cropDisplay().w}px ${cropDisplay().y}px, 0 ${cropDisplay().y}px)`\"\n ></div>\n\n <!--\n Crop window — drag to move, with eight resize handles, and the ONE\n keyboard control the component has (see `onWindowKeydown`). The eight\n handles stay pointer-only and `aria-hidden`: eight extra tab stops per\n cropper would be hostile. The keys resize from the east and south edges\n only, so a west or north drag comes out as a resize plus a move rather\n than as one gesture.\n\n `role=\"group\"` rather than no role at all, because `aria-label` is\n prohibited on a roleless `<div>` and this needs a name; and rather than\n `role=\"application\"`, which would switch the reader out of its own\n browse mode for the whole subtree — far more than one rectangle is worth.\n -->\n <div\n class=\"wr-image-cropper__window\"\n role=\"group\"\n tabindex=\"0\"\n [attr.aria-label]=\"resolvedWindowLabel()\"\n [attr.aria-describedby]=\"helpId\"\n [style.left.px]=\"cropDisplay().x\"\n [style.top.px]=\"cropDisplay().y\"\n [style.width.px]=\"cropDisplay().w\"\n [style.height.px]=\"cropDisplay().h\"\n (pointerdown)=\"onPointerDown('move', $event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n (lostpointercapture)=\"onPointerUp($event)\"\n (keydown)=\"onWindowKeydown($event)\"\n (keyup)=\"onWindowKeyup()\"\n >\n @for (h of handles; track h) {\n <span\n aria-hidden=\"true\"\n [class]=\"`wr-image-cropper__handle wr-image-cropper__handle--${h}`\"\n (pointerdown)=\"onPointerDown(h, $event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n (lostpointercapture)=\"onPointerUp($event)\"\n ></span>\n }\n </div>\n\n <p class=\"wr-image-cropper__sr-only\" [id]=\"helpId\">{{ resolvedKeyHelp() }}</p>\n\n <!--\n The crop rect in SOURCE pixels, announced after a keyboard change only —\n a pointer drag already gives sighted feedback and would flood the region,\n and the initial crop is not news. Deliberately bare numbers: a sentence\n here would be a translated string the catalog does not carry, and the\n reader has just been told what the four numbers mean by `helpId`.\n -->\n <p class=\"wr-image-cropper__sr-only\" role=\"status\">{{ announcement() }}</p>\n }\n </div>\n} @else {\n <div class=\"wr-image-cropper__empty\">{{ emptyText() }}</div>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}