ngwr 11.2.0 → 12.1.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 (395) hide show
  1. package/README.md +85 -26
  2. package/burger/styles/_index.scss +1 -1
  3. package/button/styles/_index.scss +2 -2
  4. package/calendar/styles/_index.scss +32 -12
  5. package/calendar-heatmap/styles/_index.scss +20 -0
  6. package/carousel/styles/_index.scss +23 -1
  7. package/cascader/styles/_index.scss +15 -3
  8. package/checkbox/styles/_index.scss +2 -2
  9. package/collapse/styles/_index.scss +2 -2
  10. package/color-picker/styles/_index.scss +3 -3
  11. package/compare/styles/_index.scss +18 -2
  12. package/context-menu/styles/_index.scss +21 -2
  13. package/date-picker/styles/_index.scss +5 -5
  14. package/descriptions/styles/_index.scss +1 -1
  15. package/drag-drop/styles/_index.scss +28 -1
  16. package/drawer/styles/_index.scss +37 -0
  17. package/dropdown/styles/_index.scss +2 -2
  18. package/event-calendar/styles/_index.scss +67 -10
  19. package/fesm2022/ngwr-action-sheet.mjs +3 -3
  20. package/fesm2022/ngwr-affix.mjs +3 -3
  21. package/fesm2022/ngwr-alert.mjs +3 -3
  22. package/fesm2022/ngwr-anchor.mjs +3 -3
  23. package/fesm2022/ngwr-aurora.mjs +3 -3
  24. package/fesm2022/ngwr-avatar.mjs +3 -3
  25. package/fesm2022/ngwr-back-top.mjs +3 -3
  26. package/fesm2022/ngwr-badge.mjs +6 -6
  27. package/fesm2022/ngwr-bar-chart-testing.mjs +20 -2
  28. package/fesm2022/ngwr-bar-chart-testing.mjs.map +1 -1
  29. package/fesm2022/ngwr-bar-chart.mjs +3 -3
  30. package/fesm2022/ngwr-blur-text.mjs +3 -3
  31. package/fesm2022/ngwr-blur-text.mjs.map +1 -1
  32. package/fesm2022/ngwr-border-glow.mjs +4 -4
  33. package/fesm2022/ngwr-border-glow.mjs.map +1 -1
  34. package/fesm2022/ngwr-breadcrumbs.mjs +29 -17
  35. package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
  36. package/fesm2022/ngwr-burger.mjs +3 -3
  37. package/fesm2022/ngwr-button.mjs +6 -6
  38. package/fesm2022/ngwr-calendar-heatmap.mjs +17 -5
  39. package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
  40. package/fesm2022/ngwr-calendar-testing.mjs +1 -1
  41. package/fesm2022/ngwr-calendar-testing.mjs.map +1 -1
  42. package/fesm2022/ngwr-calendar.mjs +187 -9
  43. package/fesm2022/ngwr-calendar.mjs.map +1 -1
  44. package/fesm2022/ngwr-card.mjs +10 -10
  45. package/fesm2022/ngwr-card.mjs.map +1 -1
  46. package/fesm2022/ngwr-carousel.mjs +124 -24
  47. package/fesm2022/ngwr-carousel.mjs.map +1 -1
  48. package/fesm2022/ngwr-cascader.mjs +48 -5
  49. package/fesm2022/ngwr-cascader.mjs.map +1 -1
  50. package/fesm2022/ngwr-checkbox.mjs +6 -6
  51. package/fesm2022/ngwr-circular-text.mjs +4 -4
  52. package/fesm2022/ngwr-circular-text.mjs.map +1 -1
  53. package/fesm2022/ngwr-click-spark.mjs +27 -7
  54. package/fesm2022/ngwr-click-spark.mjs.map +1 -1
  55. package/fesm2022/ngwr-clipboard.mjs +3 -3
  56. package/fesm2022/ngwr-collapse.mjs +9 -9
  57. package/fesm2022/ngwr-color-picker-testing.mjs +26 -6
  58. package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -1
  59. package/fesm2022/ngwr-color-picker.mjs +98 -8
  60. package/fesm2022/ngwr-color-picker.mjs.map +1 -1
  61. package/fesm2022/ngwr-command-palette.mjs +14 -5
  62. package/fesm2022/ngwr-command-palette.mjs.map +1 -1
  63. package/fesm2022/ngwr-compare.mjs +3 -3
  64. package/fesm2022/ngwr-confetti.mjs +31 -6
  65. package/fesm2022/ngwr-confetti.mjs.map +1 -1
  66. package/fesm2022/ngwr-config.mjs +9 -0
  67. package/fesm2022/ngwr-config.mjs.map +1 -1
  68. package/fesm2022/ngwr-context-menu-testing.mjs +30 -11
  69. package/fesm2022/ngwr-context-menu-testing.mjs.map +1 -1
  70. package/fesm2022/ngwr-context-menu.mjs +333 -43
  71. package/fesm2022/ngwr-context-menu.mjs.map +1 -1
  72. package/fesm2022/ngwr-cookie.mjs +3 -3
  73. package/fesm2022/ngwr-counter.mjs +25 -9
  74. package/fesm2022/ngwr-counter.mjs.map +1 -1
  75. package/fesm2022/{ngwr-date-adapter-fns.mjs → ngwr-date-adapters-fns.mjs} +37 -10
  76. package/fesm2022/ngwr-date-adapters-fns.mjs.map +1 -0
  77. package/fesm2022/{ngwr-date-adapter-luxon.mjs → ngwr-date-adapters-luxon.mjs} +5 -5
  78. package/fesm2022/ngwr-date-adapters-luxon.mjs.map +1 -0
  79. package/fesm2022/ngwr-date-picker-testing.mjs +11 -6
  80. package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
  81. package/fesm2022/ngwr-date-picker.mjs +119 -37
  82. package/fesm2022/ngwr-date-picker.mjs.map +1 -1
  83. package/fesm2022/{ngwr-date-adapter.mjs → ngwr-date.mjs} +36 -10
  84. package/fesm2022/ngwr-date.mjs.map +1 -0
  85. package/fesm2022/ngwr-decrypt-text.mjs +3 -3
  86. package/fesm2022/ngwr-density.mjs +6 -6
  87. package/fesm2022/ngwr-descriptions.mjs +6 -6
  88. package/fesm2022/ngwr-dialog.mjs +73 -18
  89. package/fesm2022/ngwr-dialog.mjs.map +1 -1
  90. package/fesm2022/ngwr-directives.mjs +12 -12
  91. package/fesm2022/ngwr-divider.mjs +52 -5
  92. package/fesm2022/ngwr-divider.mjs.map +1 -1
  93. package/fesm2022/ngwr-donut-chart-testing.mjs +8 -4
  94. package/fesm2022/ngwr-donut-chart-testing.mjs.map +1 -1
  95. package/fesm2022/ngwr-donut-chart.mjs +7 -7
  96. package/fesm2022/ngwr-donut-chart.mjs.map +1 -1
  97. package/fesm2022/ngwr-drag-drop.mjs +160 -11
  98. package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
  99. package/fesm2022/ngwr-drawer.mjs +121 -58
  100. package/fesm2022/ngwr-drawer.mjs.map +1 -1
  101. package/fesm2022/ngwr-dropdown.mjs +26 -10
  102. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  103. package/fesm2022/ngwr-empty.mjs +3 -3
  104. package/fesm2022/ngwr-event-calendar.mjs +28 -15
  105. package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
  106. package/fesm2022/ngwr-falling-text-testing.mjs +5 -3
  107. package/fesm2022/ngwr-falling-text-testing.mjs.map +1 -1
  108. package/fesm2022/ngwr-falling-text.mjs +75 -46
  109. package/fesm2022/ngwr-falling-text.mjs.map +1 -1
  110. package/fesm2022/ngwr-file-upload.mjs +3 -3
  111. package/fesm2022/ngwr-form.mjs +32 -15
  112. package/fesm2022/ngwr-form.mjs.map +1 -1
  113. package/fesm2022/ngwr-fuzzy-text.mjs +21 -11
  114. package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
  115. package/fesm2022/ngwr-gauge.mjs +3 -3
  116. package/fesm2022/ngwr-glitch-text-testing.mjs +51 -17
  117. package/fesm2022/ngwr-glitch-text-testing.mjs.map +1 -1
  118. package/fesm2022/ngwr-glitch-text.mjs +7 -7
  119. package/fesm2022/ngwr-glitch-text.mjs.map +1 -1
  120. package/fesm2022/ngwr-gradient-text-testing.mjs +46 -11
  121. package/fesm2022/ngwr-gradient-text-testing.mjs.map +1 -1
  122. package/fesm2022/ngwr-gradient-text.mjs +4 -4
  123. package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
  124. package/fesm2022/ngwr-hotkey.mjs +24 -9
  125. package/fesm2022/ngwr-hotkey.mjs.map +1 -1
  126. package/fesm2022/ngwr-i18n-en.mjs +73 -1
  127. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  128. package/fesm2022/ngwr-i18n-ru.mjs +55 -1
  129. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  130. package/fesm2022/ngwr-i18n.mjs +26 -15
  131. package/fesm2022/ngwr-i18n.mjs.map +1 -1
  132. package/fesm2022/ngwr-icon.mjs +6 -6
  133. package/fesm2022/ngwr-image-cropper-testing.mjs +53 -9
  134. package/fesm2022/ngwr-image-cropper-testing.mjs.map +1 -1
  135. package/fesm2022/ngwr-image-cropper.mjs +150 -12
  136. package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
  137. package/fesm2022/ngwr-input-number-testing.mjs +15 -10
  138. package/fesm2022/ngwr-input-number-testing.mjs.map +1 -1
  139. package/fesm2022/ngwr-input-number.mjs +17 -10
  140. package/fesm2022/ngwr-input-number.mjs.map +1 -1
  141. package/fesm2022/ngwr-input-otp-testing.mjs +12 -6
  142. package/fesm2022/ngwr-input-otp-testing.mjs.map +1 -1
  143. package/fesm2022/ngwr-input-otp.mjs +24 -5
  144. package/fesm2022/ngwr-input-otp.mjs.map +1 -1
  145. package/fesm2022/ngwr-input.mjs +15 -15
  146. package/fesm2022/ngwr-keyboard.mjs +3 -3
  147. package/fesm2022/ngwr-knob.mjs +3 -3
  148. package/fesm2022/ngwr-layout.mjs +15 -15
  149. package/fesm2022/ngwr-lightbox.mjs +4 -4
  150. package/fesm2022/ngwr-lightbox.mjs.map +1 -1
  151. package/fesm2022/ngwr-line-chart.mjs +73 -26
  152. package/fesm2022/ngwr-line-chart.mjs.map +1 -1
  153. package/fesm2022/ngwr-list.mjs +7 -7
  154. package/fesm2022/ngwr-list.mjs.map +1 -1
  155. package/fesm2022/ngwr-loading-bar.mjs +7 -7
  156. package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
  157. package/fesm2022/ngwr-markdown.mjs +66 -8
  158. package/fesm2022/ngwr-markdown.mjs.map +1 -1
  159. package/fesm2022/ngwr-marquee.mjs +4 -4
  160. package/fesm2022/ngwr-marquee.mjs.map +1 -1
  161. package/fesm2022/ngwr-media.mjs +3 -3
  162. package/fesm2022/ngwr-mention.mjs +108 -13
  163. package/fesm2022/ngwr-mention.mjs.map +1 -1
  164. package/fesm2022/ngwr-meta.mjs +6 -6
  165. package/fesm2022/ngwr-meter-group-testing.mjs +26 -6
  166. package/fesm2022/ngwr-meter-group-testing.mjs.map +1 -1
  167. package/fesm2022/ngwr-meter-group.mjs +3 -3
  168. package/fesm2022/ngwr-overlay.mjs +6 -6
  169. package/fesm2022/ngwr-page-header.mjs +3 -3
  170. package/fesm2022/ngwr-pagination-testing.mjs +6 -6
  171. package/fesm2022/ngwr-pagination-testing.mjs.map +1 -1
  172. package/fesm2022/ngwr-pagination.mjs +83 -16
  173. package/fesm2022/ngwr-pagination.mjs.map +1 -1
  174. package/fesm2022/ngwr-pipes.mjs +39 -26
  175. package/fesm2022/ngwr-pipes.mjs.map +1 -1
  176. package/fesm2022/ngwr-platform.mjs +9 -9
  177. package/fesm2022/ngwr-popconfirm.mjs +6 -6
  178. package/fesm2022/ngwr-popover-testing.mjs +7 -2
  179. package/fesm2022/ngwr-popover-testing.mjs.map +1 -1
  180. package/fesm2022/ngwr-popover.mjs +155 -33
  181. package/fesm2022/ngwr-popover.mjs.map +1 -1
  182. package/fesm2022/ngwr-progress.mjs +3 -3
  183. package/fesm2022/ngwr-pull-to-refresh.mjs +3 -3
  184. package/fesm2022/ngwr-qr.mjs +42 -5
  185. package/fesm2022/ngwr-qr.mjs.map +1 -1
  186. package/fesm2022/ngwr-radio.mjs +6 -6
  187. package/fesm2022/ngwr-rating.mjs +3 -3
  188. package/fesm2022/ngwr-result.mjs +21 -30
  189. package/fesm2022/ngwr-result.mjs.map +1 -1
  190. package/fesm2022/ngwr-rotating-text.mjs +3 -3
  191. package/fesm2022/ngwr-scroll.mjs +3 -3
  192. package/fesm2022/ngwr-segmented-testing.mjs +85 -1
  193. package/fesm2022/ngwr-segmented-testing.mjs.map +1 -1
  194. package/fesm2022/ngwr-segmented.mjs +123 -10
  195. package/fesm2022/ngwr-segmented.mjs.map +1 -1
  196. package/fesm2022/ngwr-select.mjs +159 -34
  197. package/fesm2022/ngwr-select.mjs.map +1 -1
  198. package/fesm2022/ngwr-shiny-text.mjs +11 -9
  199. package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
  200. package/fesm2022/ngwr-sidebar.mjs +31 -16
  201. package/fesm2022/ngwr-sidebar.mjs.map +1 -1
  202. package/fesm2022/ngwr-skeleton.mjs +3 -3
  203. package/fesm2022/ngwr-slider.mjs +100 -9
  204. package/fesm2022/ngwr-slider.mjs.map +1 -1
  205. package/fesm2022/ngwr-sparkline.mjs +3 -3
  206. package/fesm2022/ngwr-speed-dial-testing.mjs +15 -13
  207. package/fesm2022/ngwr-speed-dial-testing.mjs.map +1 -1
  208. package/fesm2022/ngwr-speed-dial.mjs +27 -12
  209. package/fesm2022/ngwr-speed-dial.mjs.map +1 -1
  210. package/fesm2022/ngwr-spinner.mjs +3 -3
  211. package/fesm2022/ngwr-splash-cursor.mjs +3 -3
  212. package/fesm2022/ngwr-split-text.mjs +17 -5
  213. package/fesm2022/ngwr-split-text.mjs.map +1 -1
  214. package/fesm2022/ngwr-splitter.mjs +3 -3
  215. package/fesm2022/ngwr-spotlight-card.mjs +7 -7
  216. package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
  217. package/fesm2022/ngwr-squircle.mjs +6 -6
  218. package/fesm2022/ngwr-star-border.mjs +4 -4
  219. package/fesm2022/ngwr-star-border.mjs.map +1 -1
  220. package/fesm2022/ngwr-statistic.mjs +9 -9
  221. package/fesm2022/ngwr-stepper-testing.mjs +8 -8
  222. package/fesm2022/ngwr-stepper-testing.mjs.map +1 -1
  223. package/fesm2022/ngwr-stepper.mjs +33 -8
  224. package/fesm2022/ngwr-stepper.mjs.map +1 -1
  225. package/fesm2022/ngwr-storage.mjs +17 -5
  226. package/fesm2022/ngwr-storage.mjs.map +1 -1
  227. package/fesm2022/ngwr-switch.mjs +3 -3
  228. package/fesm2022/ngwr-table.mjs +99 -39
  229. package/fesm2022/ngwr-table.mjs.map +1 -1
  230. package/fesm2022/ngwr-tabs-testing.mjs +11 -10
  231. package/fesm2022/ngwr-tabs-testing.mjs.map +1 -1
  232. package/fesm2022/ngwr-tabs.mjs +36 -10
  233. package/fesm2022/ngwr-tabs.mjs.map +1 -1
  234. package/fesm2022/ngwr-textarea.mjs +3 -3
  235. package/fesm2022/ngwr-theme.mjs +181 -4
  236. package/fesm2022/ngwr-theme.mjs.map +1 -1
  237. package/fesm2022/ngwr-tilt-card.mjs +7 -7
  238. package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
  239. package/fesm2022/ngwr-timeline.mjs +6 -6
  240. package/fesm2022/ngwr-toast.mjs +9 -9
  241. package/fesm2022/ngwr-toolbar.mjs +3 -3
  242. package/fesm2022/ngwr-tour.mjs +34 -10
  243. package/fesm2022/ngwr-tour.mjs.map +1 -1
  244. package/fesm2022/ngwr-transfer.mjs +3 -3
  245. package/fesm2022/ngwr-tree.mjs +28 -10
  246. package/fesm2022/ngwr-tree.mjs.map +1 -1
  247. package/fesm2022/ngwr-typewriter-testing.mjs +15 -1
  248. package/fesm2022/ngwr-typewriter-testing.mjs.map +1 -1
  249. package/fesm2022/ngwr-typewriter.mjs +13 -5
  250. package/fesm2022/ngwr-typewriter.mjs.map +1 -1
  251. package/fesm2022/ngwr-typography.mjs +3 -3
  252. package/fesm2022/ngwr-utils.mjs +5 -2
  253. package/fesm2022/ngwr-utils.mjs.map +1 -1
  254. package/fesm2022/ngwr-version.mjs +1 -1
  255. package/fesm2022/ngwr-version.mjs.map +1 -1
  256. package/fesm2022/ngwr-virtual-scroll.mjs +4 -4
  257. package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -1
  258. package/fesm2022/ngwr-waves.mjs +4 -4
  259. package/fesm2022/ngwr-waves.mjs.map +1 -1
  260. package/fesm2022/ngwr-window.mjs +94 -24
  261. package/fesm2022/ngwr-window.mjs.map +1 -1
  262. package/file-upload/styles/_index.scss +2 -2
  263. package/glitch-text/styles/_index.scss +27 -12
  264. package/i18n/en.json +56 -6
  265. package/i18n/ru.json +56 -6
  266. package/image-cropper/styles/_index.scss +19 -0
  267. package/input/styles/_index.scss +2 -2
  268. package/input-number/styles/_index.scss +2 -2
  269. package/input-otp/styles/_index.scss +9 -2
  270. package/knob/styles/_index.scss +1 -1
  271. package/lightbox/styles/_index.scss +16 -2
  272. package/line-chart/styles/_index.scss +28 -14
  273. package/list/styles/_index.scss +1 -1
  274. package/llms-full.txt +19 -24
  275. package/llms.txt +46 -8
  276. package/markdown/styles/_index.scss +3 -3
  277. package/mcp/api.js +6 -4
  278. package/mcp/catalog.js +40 -3
  279. package/mcp/providers.js +16 -3
  280. package/mcp/tools.js +21 -1
  281. package/meter-group/styles/_index.scss +1 -1
  282. package/overlay/styles/_index.scss +33 -1
  283. package/package.json +17 -12
  284. package/pagination/styles/_index.scss +23 -1
  285. package/popover/styles/_index.scss +16 -7
  286. package/progress/styles/_index.scss +1 -1
  287. package/radio/styles/_index.scss +2 -2
  288. package/rating/styles/_index.scss +1 -1
  289. package/schematics/collection.json +1 -1
  290. package/schematics/migrations/v12/index.js +65 -0
  291. package/schematics/migrations/v7/index.js +12 -1
  292. package/schematics/migrations/v8/index.js +10 -1
  293. package/schematics/migrations.json +5 -0
  294. package/schematics/ng-add/index.js +8 -3
  295. package/schematics/provider/index.js +80 -13
  296. package/schematics/use/index.js +9 -129
  297. package/schematics/use/rule.js +206 -0
  298. package/schematics/use/symbol-map.json +4 -4
  299. package/segmented/styles/_index.scss +14 -3
  300. package/select/styles/_index.scss +14 -2
  301. package/sidebar/styles/_index.scss +12 -10
  302. package/skills/ngwr/SKILL.md +7 -5
  303. package/skills/ngwr/references/catalog.md +9 -9
  304. package/skills/ngwr/references/setup.md +10 -5
  305. package/slider/styles/_index.scss +1 -1
  306. package/speed-dial/styles/_index.scss +1 -1
  307. package/stepper/styles/_index.scss +12 -3
  308. package/switch/styles/_index.scss +2 -2
  309. package/table/styles/_index.scss +61 -12
  310. package/tabs/styles/_index.scss +1 -1
  311. package/textarea/styles/_index.scss +1 -1
  312. package/theme/styles/_colors.scss +142 -56
  313. package/theme/styles/_dark.scss +46 -3
  314. package/theme/styles/_focus.scss +28 -5
  315. package/theme/styles/_mixins.scss +1 -1
  316. package/theme/styles/_variables.scss +67 -0
  317. package/transfer/styles/_index.scss +26 -3
  318. package/tree/styles/_index.scss +29 -6
  319. package/types/ngwr-bar-chart-testing.d.ts +9 -1
  320. package/types/ngwr-blur-text.d.ts +8 -4
  321. package/types/ngwr-breadcrumbs.d.ts +17 -10
  322. package/types/ngwr-calendar-testing.d.ts +1 -1
  323. package/types/ngwr-calendar.d.ts +66 -0
  324. package/types/ngwr-carousel.d.ts +85 -6
  325. package/types/ngwr-cascader.d.ts +17 -0
  326. package/types/ngwr-click-spark.d.ts +4 -0
  327. package/types/ngwr-color-picker-testing.d.ts +8 -0
  328. package/types/ngwr-color-picker.d.ts +21 -1
  329. package/types/ngwr-confetti.d.ts +8 -0
  330. package/types/ngwr-config.d.ts +10 -1
  331. package/types/ngwr-context-menu-testing.d.ts +24 -11
  332. package/types/ngwr-context-menu.d.ts +56 -0
  333. package/types/ngwr-counter.d.ts +1 -0
  334. package/types/{ngwr-date-adapter-fns.d.ts → ngwr-date-adapters-fns.d.ts} +33 -6
  335. package/types/{ngwr-date-adapter-luxon.d.ts → ngwr-date-adapters-luxon.d.ts} +1 -1
  336. package/types/ngwr-date-picker-testing.d.ts +11 -6
  337. package/types/ngwr-date-picker.d.ts +8 -4
  338. package/types/{ngwr-date-adapter.d.ts → ngwr-date.d.ts} +24 -5
  339. package/types/ngwr-dialog.d.ts +38 -1
  340. package/types/ngwr-divider.d.ts +25 -0
  341. package/types/ngwr-donut-chart-testing.d.ts +7 -3
  342. package/types/ngwr-donut-chart.d.ts +3 -3
  343. package/types/ngwr-drag-drop.d.ts +56 -2
  344. package/types/ngwr-drawer.d.ts +56 -5
  345. package/types/ngwr-event-calendar.d.ts +8 -2
  346. package/types/ngwr-falling-text-testing.d.ts +5 -3
  347. package/types/ngwr-falling-text.d.ts +1 -1
  348. package/types/ngwr-form.d.ts +12 -2
  349. package/types/ngwr-glitch-text-testing.d.ts +37 -8
  350. package/types/ngwr-gradient-text-testing.d.ts +16 -3
  351. package/types/ngwr-i18n.d.ts +13 -4
  352. package/types/ngwr-image-cropper-testing.d.ts +49 -10
  353. package/types/ngwr-image-cropper.d.ts +70 -5
  354. package/types/ngwr-input-number-testing.d.ts +15 -10
  355. package/types/ngwr-input-number.d.ts +11 -4
  356. package/types/ngwr-input-otp-testing.d.ts +12 -6
  357. package/types/ngwr-input-otp.d.ts +15 -1
  358. package/types/ngwr-line-chart.d.ts +20 -3
  359. package/types/ngwr-markdown.d.ts +11 -0
  360. package/types/ngwr-mention.d.ts +32 -0
  361. package/types/ngwr-meter-group-testing.d.ts +12 -2
  362. package/types/ngwr-overlay.d.ts +1 -0
  363. package/types/ngwr-pagination-testing.d.ts +4 -4
  364. package/types/ngwr-pagination.d.ts +11 -0
  365. package/types/ngwr-pipes.d.ts +1 -1
  366. package/types/ngwr-popover-testing.d.ts +7 -2
  367. package/types/ngwr-popover.d.ts +1 -0
  368. package/types/ngwr-result.d.ts +6 -0
  369. package/types/ngwr-segmented-testing.d.ts +53 -0
  370. package/types/ngwr-segmented.d.ts +107 -5
  371. package/types/ngwr-select.d.ts +68 -6
  372. package/types/ngwr-shiny-text.d.ts +4 -2
  373. package/types/ngwr-sidebar.d.ts +14 -5
  374. package/types/ngwr-slider.d.ts +69 -1
  375. package/types/ngwr-speed-dial-testing.d.ts +15 -11
  376. package/types/ngwr-speed-dial.d.ts +22 -3
  377. package/types/ngwr-split-text.d.ts +9 -0
  378. package/types/ngwr-stepper-testing.d.ts +5 -5
  379. package/types/ngwr-stepper.d.ts +8 -0
  380. package/types/ngwr-storage.d.ts +1 -0
  381. package/types/ngwr-table.d.ts +104 -11
  382. package/types/ngwr-tabs-testing.d.ts +11 -10
  383. package/types/ngwr-tabs.d.ts +2 -0
  384. package/types/ngwr-theme.d.ts +82 -1
  385. package/types/ngwr-tour.d.ts +22 -4
  386. package/types/ngwr-tree.d.ts +5 -4
  387. package/types/ngwr-typewriter-testing.d.ts +10 -0
  388. package/types/ngwr-utils.d.ts +5 -2
  389. package/types/ngwr-version.d.ts +1 -1
  390. package/types/ngwr-window.d.ts +39 -11
  391. package/typography/styles/_index.scss +1 -1
  392. package/window/styles/_index.scss +72 -26
  393. package/fesm2022/ngwr-date-adapter-fns.mjs.map +0 -1
  394. package/fesm2022/ngwr-date-adapter-luxon.mjs.map +0 -1
  395. package/fesm2022/ngwr-date-adapter.mjs.map +0 -1
@@ -114,10 +114,10 @@ class WrToastItem {
114
114
  this.swipeX.set(0);
115
115
  }
116
116
  }
117
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrToastItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
118
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: WrToastItem, isStandalone: true, selector: "wr-toast", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, dismissible: { classPropertyName: "dismissible", publicName: "dismissible", isSignal: true, isRequired: false, transformFunction: null }, showProgress: { classPropertyName: "showProgress", publicName: "showProgress", isSignal: true, isRequired: false, transformFunction: null }, showCopy: { classPropertyName: "showCopy", publicName: "showCopy", isSignal: true, isRequired: false, transformFunction: null }, duration: { classPropertyName: "duration", publicName: "duration", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { dismissed: "dismissed", pauseRequested: "pauseRequested", resumeRequested: "resumeRequested" }, host: { listeners: { "mouseenter": "pauseRequested.emit()", "mouseleave": "resumeRequested.emit()", "touchstart": "onSwipeStart($event)", "touchmove": "onSwipeMove($event)", "touchend": "onSwipeEnd()", "touchcancel": "onSwipeEnd()" }, properties: { "class": "classes()", "style.--wr-toast-duration": "durationMs()", "style.transform": "swipeTransform()", "style.opacity": "swipeOpacity()", "style.transition": "swiping() ? \"none\" : null", "attr.role": "liveRole()", "attr.aria-live": "liveLevel()" } }, ngImport: i0, template: "<div class=\"wr-toast__body\">\n @if (title(); as text) {\n <p class=\"wr-toast__title\">{{ text }}</p>\n }\n <p class=\"wr-toast__message\">{{ message() }}</p>\n</div>\n\n<div class=\"wr-toast__actions\">\n @if (showCopy()) {\n <button\n type=\"button\"\n class=\"wr-toast__action wr-toast__action--copy\"\n [attr.aria-label]=\"justCopied() ? labels().copied : labels().copy\"\n [title]=\"justCopied() ? labels().copied : labels().copy\"\n (click)=\"onCopy()\"\n >\n @if (justCopied()) {\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n } @else {\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\" />\n <path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\" />\n </svg>\n }\n </button>\n }\n\n @if (dismissible()) {\n <button\n type=\"button\"\n class=\"wr-toast__action wr-toast__action--close\"\n [attr.aria-label]=\"labels().close\"\n [title]=\"labels().close\"\n (click)=\"dismissed.emit()\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </button>\n }\n</div>\n\n@if (progressVisible()) {\n <span class=\"wr-toast__progress\" aria-hidden=\"true\"></span>\n}\n", encapsulation: i0.ViewEncapsulation.None });
117
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrToastItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
118
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: WrToastItem, isStandalone: true, selector: "wr-toast", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, dismissible: { classPropertyName: "dismissible", publicName: "dismissible", isSignal: true, isRequired: false, transformFunction: null }, showProgress: { classPropertyName: "showProgress", publicName: "showProgress", isSignal: true, isRequired: false, transformFunction: null }, showCopy: { classPropertyName: "showCopy", publicName: "showCopy", isSignal: true, isRequired: false, transformFunction: null }, duration: { classPropertyName: "duration", publicName: "duration", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { dismissed: "dismissed", pauseRequested: "pauseRequested", resumeRequested: "resumeRequested" }, host: { listeners: { "mouseenter": "pauseRequested.emit()", "mouseleave": "resumeRequested.emit()", "touchstart": "onSwipeStart($event)", "touchmove": "onSwipeMove($event)", "touchend": "onSwipeEnd()", "touchcancel": "onSwipeEnd()" }, properties: { "class": "classes()", "style.--wr-toast-duration": "durationMs()", "style.transform": "swipeTransform()", "style.opacity": "swipeOpacity()", "style.transition": "swiping() ? \"none\" : null", "attr.role": "liveRole()", "attr.aria-live": "liveLevel()" } }, ngImport: i0, template: "<div class=\"wr-toast__body\">\n @if (title(); as text) {\n <p class=\"wr-toast__title\">{{ text }}</p>\n }\n <p class=\"wr-toast__message\">{{ message() }}</p>\n</div>\n\n<div class=\"wr-toast__actions\">\n @if (showCopy()) {\n <button\n type=\"button\"\n class=\"wr-toast__action wr-toast__action--copy\"\n [attr.aria-label]=\"justCopied() ? labels().copied : labels().copy\"\n [title]=\"justCopied() ? labels().copied : labels().copy\"\n (click)=\"onCopy()\"\n >\n @if (justCopied()) {\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n } @else {\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\" />\n <path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\" />\n </svg>\n }\n </button>\n }\n\n @if (dismissible()) {\n <button\n type=\"button\"\n class=\"wr-toast__action wr-toast__action--close\"\n [attr.aria-label]=\"labels().close\"\n [title]=\"labels().close\"\n (click)=\"dismissed.emit()\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </button>\n }\n</div>\n\n@if (progressVisible()) {\n <span class=\"wr-toast__progress\" aria-hidden=\"true\"></span>\n}\n", encapsulation: i0.ViewEncapsulation.None });
119
119
  }
120
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrToastItem, decorators: [{
120
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrToastItem, decorators: [{
121
121
  type: Component,
122
122
  args: [{ selector: 'wr-toast', encapsulation: ViewEncapsulation.None, host: {
123
123
  '[class]': 'classes()',
@@ -283,10 +283,10 @@ class WrToastHost {
283
283
  showCopyFor(t) {
284
284
  return t.showCopy ?? this.config().showCopy;
285
285
  }
286
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrToastHost, deps: [], target: i0.ɵɵFactoryTarget.Component });
287
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: WrToastHost, isStandalone: true, selector: "wr-toast-host", inputs: { position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dismissed: "dismissed", pauseRequested: "pauseRequested", resumeRequested: "resumeRequested", dismissAllRequested: "dismissAllRequested" }, host: { attributes: { "role": "region" }, listeners: { "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()", "focusin": "onFocusIn()", "focusout": "onFocusOut()" }, properties: { "class": "classes()", "attr.aria-label": "regionLabel()" } }, ngImport: i0, template: "@if (closeAllVisible()) {\n <button type=\"button\" class=\"wr-toast-host__close-all\" (click)=\"dismissAllRequested.emit()\">\n {{ closeAllLabel() }} ({{ totalCount() }})\n </button>\n}\n\n@for (toast of toasts(); track toast.id) {\n <wr-toast\n [style.--wr-toast-stack-index]=\"stackIndex($index)\"\n [type]=\"toast.type ?? 'info'\"\n [title]=\"toast.title ?? null\"\n [message]=\"toast.message\"\n [dismissible]=\"toast.dismissible ?? true\"\n [showProgress]=\"showProgressFor(toast)\"\n [showCopy]=\"showCopyFor(toast)\"\n [duration]=\"toast.resolvedDuration\"\n [labels]=\"labels()\"\n (dismissed)=\"dismissed.emit(toast.id)\"\n (pauseRequested)=\"pauseRequested.emit(toast.id)\"\n (resumeRequested)=\"resumeRequested.emit(toast.id)\"\n />\n}\n", dependencies: [{ kind: "component", type: WrToastItem, selector: "wr-toast", inputs: ["type", "title", "message", "dismissible", "showProgress", "showCopy", "duration", "labels"], outputs: ["dismissed", "pauseRequested", "resumeRequested"] }], encapsulation: i0.ViewEncapsulation.None });
286
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrToastHost, deps: [], target: i0.ɵɵFactoryTarget.Component });
287
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: WrToastHost, isStandalone: true, selector: "wr-toast-host", inputs: { position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dismissed: "dismissed", pauseRequested: "pauseRequested", resumeRequested: "resumeRequested", dismissAllRequested: "dismissAllRequested" }, host: { attributes: { "role": "region" }, listeners: { "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()", "focusin": "onFocusIn()", "focusout": "onFocusOut()" }, properties: { "class": "classes()", "attr.aria-label": "regionLabel()" } }, ngImport: i0, template: "@if (closeAllVisible()) {\n <button type=\"button\" class=\"wr-toast-host__close-all\" (click)=\"dismissAllRequested.emit()\">\n {{ closeAllLabel() }} ({{ totalCount() }})\n </button>\n}\n\n@for (toast of toasts(); track toast.id) {\n <wr-toast\n [style.--wr-toast-stack-index]=\"stackIndex($index)\"\n [type]=\"toast.type ?? 'info'\"\n [title]=\"toast.title ?? null\"\n [message]=\"toast.message\"\n [dismissible]=\"toast.dismissible ?? true\"\n [showProgress]=\"showProgressFor(toast)\"\n [showCopy]=\"showCopyFor(toast)\"\n [duration]=\"toast.resolvedDuration\"\n [labels]=\"labels()\"\n (dismissed)=\"dismissed.emit(toast.id)\"\n (pauseRequested)=\"pauseRequested.emit(toast.id)\"\n (resumeRequested)=\"resumeRequested.emit(toast.id)\"\n />\n}\n", dependencies: [{ kind: "component", type: WrToastItem, selector: "wr-toast", inputs: ["type", "title", "message", "dismissible", "showProgress", "showCopy", "duration", "labels"], outputs: ["dismissed", "pauseRequested", "resumeRequested"] }], encapsulation: i0.ViewEncapsulation.None });
288
288
  }
289
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrToastHost, decorators: [{
289
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrToastHost, decorators: [{
290
290
  type: Component,
291
291
  args: [{ selector: 'wr-toast-host', encapsulation: ViewEncapsulation.None, host: {
292
292
  '[class]': 'classes()',
@@ -533,10 +533,10 @@ class WrToast {
533
533
  this.hostRef = null;
534
534
  }
535
535
  }
536
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrToast, deps: [], target: i0.ɵɵFactoryTarget.Service });
537
- static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.1", ngImport: i0, type: WrToast });
536
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrToast, deps: [], target: i0.ɵɵFactoryTarget.Service });
537
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.4", ngImport: i0, type: WrToast });
538
538
  }
539
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrToast, decorators: [{
539
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrToast, decorators: [{
540
540
  type: Service
541
541
  }] });
542
542
 
@@ -37,10 +37,10 @@ class WrToolbar {
37
37
  * adapts inside a narrow column or split pane). @default false
38
38
  */
39
39
  responsive = input(false, { ...(ngDevMode ? { debugName: "responsive" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
40
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrToolbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
41
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.1", type: WrToolbar, isStandalone: true, selector: "wr-toolbar", inputs: { responsive: { classPropertyName: "responsive", publicName: "responsive", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "toolbar" }, properties: { "class.wr-toolbar--responsive": "responsive()" }, classAttribute: "wr-toolbar" }, ngImport: i0, template: "<div class=\"wr-toolbar__zone wr-toolbar__zone--start\">\n <ng-content select=\"[wrToolbarStart]\" />\n</div>\n<div class=\"wr-toolbar__zone wr-toolbar__zone--center\">\n <ng-content select=\"[wrToolbarCenter]\" />\n</div>\n<div class=\"wr-toolbar__zone wr-toolbar__zone--end\">\n <ng-content select=\"[wrToolbarEnd]\" />\n</div>\n", encapsulation: i0.ViewEncapsulation.None });
40
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrToolbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
41
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.4", type: WrToolbar, isStandalone: true, selector: "wr-toolbar", inputs: { responsive: { classPropertyName: "responsive", publicName: "responsive", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "toolbar" }, properties: { "class.wr-toolbar--responsive": "responsive()" }, classAttribute: "wr-toolbar" }, ngImport: i0, template: "<div class=\"wr-toolbar__zone wr-toolbar__zone--start\">\n <ng-content select=\"[wrToolbarStart]\" />\n</div>\n<div class=\"wr-toolbar__zone wr-toolbar__zone--center\">\n <ng-content select=\"[wrToolbarCenter]\" />\n</div>\n<div class=\"wr-toolbar__zone wr-toolbar__zone--end\">\n <ng-content select=\"[wrToolbarEnd]\" />\n</div>\n", encapsulation: i0.ViewEncapsulation.None });
42
42
  }
43
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrToolbar, decorators: [{
43
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrToolbar, decorators: [{
44
44
  type: Component,
45
45
  args: [{ selector: 'wr-toolbar', encapsulation: ViewEncapsulation.None, host: { class: 'wr-toolbar', role: 'toolbar', '[class.wr-toolbar--responsive]': 'responsive()' }, template: "<div class=\"wr-toolbar__zone wr-toolbar__zone--start\">\n <ng-content select=\"[wrToolbarStart]\" />\n</div>\n<div class=\"wr-toolbar__zone wr-toolbar__zone--center\">\n <ng-content select=\"[wrToolbarCenter]\" />\n</div>\n<div class=\"wr-toolbar__zone wr-toolbar__zone--end\">\n <ng-content select=\"[wrToolbarEnd]\" />\n</div>\n" }]
46
46
  }], propDecorators: { responsive: [{ type: i0.Input, args: [{ isSignal: true, alias: "responsive", required: false }] }] } });
@@ -26,8 +26,7 @@ const WR_TOUR_STEP = new InjectionToken('WR_TOUR_STEP');
26
26
  class WrTourPopup {
27
27
  tour = inject(WR_TOUR_STEP);
28
28
  // `readI18nText`, not `useI18nText`: these have no consumer-facing input to
29
- // forward, and the popup is rebuilt for every step, so resolving once at
30
- // construction is enough.
29
+ // forward. Both helpers are reactive.
31
30
  nextLabel = readI18nText('tour.next', 'Next');
32
31
  backLabel = readI18nText('tour.back', 'Back');
33
32
  doneLabel = readI18nText('tour.done', 'Done');
@@ -39,7 +38,10 @@ class WrTourPopup {
39
38
  // ended the tour when it was pressed.
40
39
  isLast = computed(() => this.tour.isLast(), /* @ts-ignore */
41
40
  ...(ngDevMode ? [{ debugName: "isLast" }] : /* istanbul ignore next */ []));
42
- isFirst = computed(() => this.tour.index() <= 0, /* @ts-ignore */
41
+ // Same story at the other end, and the same fix: `index() <= 0` rendered a Back
42
+ // button on the first card of a tour whose leading step had been skipped, and
43
+ // `prev()` walks off the start and returns — so the button did nothing at all.
44
+ isFirst = computed(() => this.tour.isFirst(), /* @ts-ignore */
43
45
  ...(ngDevMode ? [{ debugName: "isFirst" }] : /* istanbul ignore next */ []));
44
46
  progressText = computed(() => this.progress({ current: this.tour.index() + 1, total: this.tour.total() }), /* @ts-ignore */
45
47
  ...(ngDevMode ? [{ debugName: "progressText" }] : /* istanbul ignore next */ []));
@@ -48,12 +50,12 @@ class WrTourPopup {
48
50
  return title ? `${title} — ${this.progressText()}` : this.progressText();
49
51
  }, /* @ts-ignore */
50
52
  ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
51
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrTourPopup, deps: [], target: i0.ɵɵFactoryTarget.Component });
52
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: WrTourPopup, isStandalone: true, selector: "wr-tour-popup", host: { attributes: { "role": "dialog", "aria-modal": "true" }, properties: { "attr.aria-label": "ariaLabel()" }, classAttribute: "wr-tour-popup" }, ngImport: i0, template: "@let step = tour.step();\n\n<p class=\"wr-tour-popup__progress\">{{ progressText() }}</p>\n\n@if (step?.title; as title) {\n <h2 class=\"wr-tour-popup__title\">{{ title }}</h2>\n}\n\n<p class=\"wr-tour-popup__content\">{{ step?.content }}</p>\n\n<div class=\"wr-tour-popup__actions\">\n <!-- The three buttons are told apart by a class rather than by their position in\n the row: the back button is conditional, so an index means one thing on the\n first step and another everywhere else \u2014 and the labels are translated. -->\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"medium\"\n outlined\n class=\"wr-tour-popup__action wr-tour-popup__action--skip\"\n (click)=\"tour.stop()\"\n >\n {{ skipLabel }}\n </button>\n\n <span class=\"wr-tour-popup__spacer\"></span>\n\n @if (!isFirst()) {\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n outlined\n class=\"wr-tour-popup__action wr-tour-popup__action--back\"\n (click)=\"tour.prev()\"\n >\n {{ backLabel }}\n </button>\n }\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"primary\"\n class=\"wr-tour-popup__action wr-tour-popup__action--next\"\n (click)=\"tour.next()\"\n >\n {{ isLast() ? doneLabel : nextLabel }}\n </button>\n</div>\n", dependencies: [{ kind: "component", type: WrButton, selector: "wr-btn, button[wr-btn], a[wr-btn]", inputs: ["color", "size", "shape", "icon", "iconPosition", "disabled", "outlined", "block", "loading", "isDisabledWhenLoading"] }], encapsulation: i0.ViewEncapsulation.None });
53
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrTourPopup, deps: [], target: i0.ɵɵFactoryTarget.Component });
54
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: WrTourPopup, isStandalone: true, selector: "wr-tour-popup", host: { attributes: { "role": "dialog", "aria-modal": "true" }, properties: { "attr.aria-label": "ariaLabel()" }, classAttribute: "wr-tour-popup" }, ngImport: i0, template: "@let step = tour.step();\n\n<p class=\"wr-tour-popup__progress\">{{ progressText() }}</p>\n\n@if (step?.title; as title) {\n <h2 class=\"wr-tour-popup__title\">{{ title }}</h2>\n}\n\n<p class=\"wr-tour-popup__content\">{{ step?.content }}</p>\n\n<div class=\"wr-tour-popup__actions\">\n <!-- The three buttons are told apart by a class rather than by their position in\n the row: the back button is conditional, so an index means one thing on the\n first step and another everywhere else \u2014 and the labels are translated. -->\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"medium\"\n outlined\n class=\"wr-tour-popup__action wr-tour-popup__action--skip\"\n (click)=\"tour.stop()\"\n >\n {{ skipLabel() }}\n </button>\n\n <span class=\"wr-tour-popup__spacer\"></span>\n\n @if (!isFirst()) {\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n outlined\n class=\"wr-tour-popup__action wr-tour-popup__action--back\"\n (click)=\"tour.prev()\"\n >\n {{ backLabel() }}\n </button>\n }\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"primary\"\n class=\"wr-tour-popup__action wr-tour-popup__action--next\"\n (click)=\"tour.next()\"\n >\n {{ isLast() ? doneLabel() : nextLabel() }}\n </button>\n</div>\n", dependencies: [{ kind: "component", type: WrButton, selector: "wr-btn, button[wr-btn], a[wr-btn]", inputs: ["color", "size", "shape", "icon", "iconPosition", "disabled", "outlined", "block", "loading", "isDisabledWhenLoading"] }], encapsulation: i0.ViewEncapsulation.None });
53
55
  }
54
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrTourPopup, decorators: [{
56
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrTourPopup, decorators: [{
55
57
  type: Component,
56
- args: [{ selector: 'wr-tour-popup', encapsulation: ViewEncapsulation.None, host: { class: 'wr-tour-popup', role: 'dialog', 'aria-modal': 'true', '[attr.aria-label]': 'ariaLabel()' }, imports: [WrButton], template: "@let step = tour.step();\n\n<p class=\"wr-tour-popup__progress\">{{ progressText() }}</p>\n\n@if (step?.title; as title) {\n <h2 class=\"wr-tour-popup__title\">{{ title }}</h2>\n}\n\n<p class=\"wr-tour-popup__content\">{{ step?.content }}</p>\n\n<div class=\"wr-tour-popup__actions\">\n <!-- The three buttons are told apart by a class rather than by their position in\n the row: the back button is conditional, so an index means one thing on the\n first step and another everywhere else \u2014 and the labels are translated. -->\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"medium\"\n outlined\n class=\"wr-tour-popup__action wr-tour-popup__action--skip\"\n (click)=\"tour.stop()\"\n >\n {{ skipLabel }}\n </button>\n\n <span class=\"wr-tour-popup__spacer\"></span>\n\n @if (!isFirst()) {\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n outlined\n class=\"wr-tour-popup__action wr-tour-popup__action--back\"\n (click)=\"tour.prev()\"\n >\n {{ backLabel }}\n </button>\n }\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"primary\"\n class=\"wr-tour-popup__action wr-tour-popup__action--next\"\n (click)=\"tour.next()\"\n >\n {{ isLast() ? doneLabel : nextLabel }}\n </button>\n</div>\n" }]
58
+ args: [{ selector: 'wr-tour-popup', encapsulation: ViewEncapsulation.None, host: { class: 'wr-tour-popup', role: 'dialog', 'aria-modal': 'true', '[attr.aria-label]': 'ariaLabel()' }, imports: [WrButton], template: "@let step = tour.step();\n\n<p class=\"wr-tour-popup__progress\">{{ progressText() }}</p>\n\n@if (step?.title; as title) {\n <h2 class=\"wr-tour-popup__title\">{{ title }}</h2>\n}\n\n<p class=\"wr-tour-popup__content\">{{ step?.content }}</p>\n\n<div class=\"wr-tour-popup__actions\">\n <!-- The three buttons are told apart by a class rather than by their position in\n the row: the back button is conditional, so an index means one thing on the\n first step and another everywhere else \u2014 and the labels are translated. -->\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"medium\"\n outlined\n class=\"wr-tour-popup__action wr-tour-popup__action--skip\"\n (click)=\"tour.stop()\"\n >\n {{ skipLabel() }}\n </button>\n\n <span class=\"wr-tour-popup__spacer\"></span>\n\n @if (!isFirst()) {\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n outlined\n class=\"wr-tour-popup__action wr-tour-popup__action--back\"\n (click)=\"tour.prev()\"\n >\n {{ backLabel() }}\n </button>\n }\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"primary\"\n class=\"wr-tour-popup__action wr-tour-popup__action--next\"\n (click)=\"tour.next()\"\n >\n {{ isLast() ? doneLabel() : nextLabel() }}\n </button>\n</div>\n" }]
57
59
  }] });
58
60
 
59
61
  /**
@@ -132,6 +134,9 @@ class WrTour {
132
134
  // move, in `goTo`, because reachability is a DOM question and not a signal.
133
135
  noStepAfter = signal(false, /* @ts-ignore */
134
136
  ...(ngDevMode ? [{ debugName: "noStepAfter" }] : /* istanbul ignore next */ []));
137
+ // The same question looking the other way. Settled in `goTo` for the same reason.
138
+ noStepBefore = signal(true, /* @ts-ignore */
139
+ ...(ngDevMode ? [{ debugName: "noStepBefore" }] : /* istanbul ignore next */ []));
135
140
  /**
136
141
  * Whether this is the last step the user will see. Not `index() === total() - 1`:
137
142
  * a trailing step whose target is missing is skipped, so comparing against the
@@ -140,6 +145,15 @@ class WrTour {
140
145
  */
141
146
  isLast = computed(() => this.noStepAfter(), /* @ts-ignore */
142
147
  ...(ngDevMode ? [{ debugName: "isLast" }] : /* istanbul ignore next */ []));
148
+ /**
149
+ * Whether this is the first step the user will see. Not `index() === 0`, for the
150
+ * mirror of the reason above: a LEADING step whose target is missing is skipped,
151
+ * so the tour opens on a later index and the raw one put a Back button on the
152
+ * first card — one that did nothing at all, because `prev()` walks off the start
153
+ * and returns without moving.
154
+ */
155
+ isFirst = computed(() => this.noStepBefore(), /* @ts-ignore */
156
+ ...(ngDevMode ? [{ debugName: "isFirst" }] : /* istanbul ignore next */ []));
143
157
  constructor() {
144
158
  // Everything this service owns lives OUTSIDE its own injector: a CDK overlay, a
145
159
  // div appended to `document.body`, a document keydown listener and two window
@@ -197,6 +211,7 @@ class WrTour {
197
211
  this.teardownStep();
198
212
  this.cursor.set(i);
199
213
  this.noStepAfter.set(!this.anyResolvableAfter(i));
214
+ this.noStepBefore.set(!this.anyResolvableBefore(i));
200
215
  this.openStep(steps[i]);
201
216
  }
202
217
  /** Whether the tour has a step past `index` that can actually be shown. */
@@ -208,6 +223,15 @@ class WrTour {
208
223
  }
209
224
  return false;
210
225
  }
226
+ /** Whether the tour has a step before `index` that can actually be shown. */
227
+ anyResolvableBefore(index) {
228
+ const steps = this.steps();
229
+ for (let i = index - 1; i >= 0; i -= 1) {
230
+ if (this.resolve(steps[i]) !== null)
231
+ return true;
232
+ }
233
+ return false;
234
+ }
211
235
  resolve(step) {
212
236
  if (typeof step.target !== 'string')
213
237
  return step.target.isConnected ? step.target : null;
@@ -301,10 +325,10 @@ class WrTour {
301
325
  isRtl() {
302
326
  return this.dir?.value === 'rtl';
303
327
  }
304
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrTour, deps: [], target: i0.ɵɵFactoryTarget.Service });
305
- static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.1", ngImport: i0, type: WrTour });
328
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrTour, deps: [], target: i0.ɵɵFactoryTarget.Service });
329
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.4", ngImport: i0, type: WrTour });
306
330
  }
307
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrTour, decorators: [{
331
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrTour, decorators: [{
308
332
  type: Service
309
333
  }], ctorParameters: () => [] });
310
334
 
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-tour.mjs","sources":["../../../projects/lib/tour/tour-popup.ts","../../../projects/lib/tour/tour-popup.html","../../../projects/lib/tour/tour.ts","../../../projects/lib/tour/ngwr-tour.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 { Component, InjectionToken, ViewEncapsulation, computed, inject } from '@angular/core';\n\nimport { WrButton } from 'ngwr/button';\nimport { readI18nText, useI18nFormatter } from 'ngwr/i18n';\n\nimport type { WrTourStep } from './interfaces';\n\n/** What the popup needs from the running tour, without importing the service (cycle). */\nexport interface WrTourController {\n readonly index: () => number;\n readonly total: () => number;\n readonly step: () => WrTourStep | null;\n /**\n * Whether this is the last step the user will actually see — the service looks\n * ahead for a reachable one, because a trailing step whose target is missing gets\n * skipped and the raw count would call the final card \"Next\".\n */\n readonly isLast: () => boolean;\n next(): void;\n prev(): void;\n stop(): void;\n}\n\n/** Provided by `WrTour` when it attaches the popup portal. */\nexport const WR_TOUR_STEP = new InjectionToken<WrTourController>('WR_TOUR_STEP');\n\n/**\n * The card a tour shows next to the spotlit element. Rendered by `WrTour` into\n * a CDK overlay — not something you place yourself.\n *\n * @see https://ngwr.dev/reference/services/tour\n */\n@Component({\n selector: 'wr-tour-popup',\n templateUrl: './tour-popup.html',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-tour-popup', role: 'dialog', 'aria-modal': 'true', '[attr.aria-label]': 'ariaLabel()' },\n imports: [WrButton],\n})\nexport class WrTourPopup {\n protected readonly tour = inject(WR_TOUR_STEP);\n\n // `readI18nText`, not `useI18nText`: these have no consumer-facing input to\n // forward, and the popup is rebuilt for every step, so resolving once at\n // construction is enough.\n protected readonly nextLabel = readI18nText('tour.next', 'Next');\n protected readonly backLabel = readI18nText('tour.back', 'Back');\n protected readonly doneLabel = readI18nText('tour.done', 'Done');\n protected readonly skipLabel = readI18nText('tour.skip', 'Skip tour');\n\n /** `Step {{current}} of {{total}}` — also the dialog's accessible name. */\n protected readonly progress = useI18nFormatter('tour.progress', 'Step {{current}} of {{total}}');\n\n // From the service, not `index() >= total() - 1`: a trailing step whose target is\n // missing gets skipped, so the raw count called the final card \"Next\" and then\n // ended the tour when it was pressed.\n protected readonly isLast = computed(() => this.tour.isLast());\n protected readonly isFirst = computed(() => this.tour.index() <= 0);\n\n protected readonly progressText = computed(() =>\n this.progress({ current: this.tour.index() + 1, total: this.tour.total() })\n );\n\n protected readonly ariaLabel = computed(() => {\n const title = this.tour.step()?.title;\n return title ? `${title} — ${this.progressText()}` : this.progressText();\n });\n}\n","@let step = tour.step();\n\n<p class=\"wr-tour-popup__progress\">{{ progressText() }}</p>\n\n@if (step?.title; as title) {\n <h2 class=\"wr-tour-popup__title\">{{ title }}</h2>\n}\n\n<p class=\"wr-tour-popup__content\">{{ step?.content }}</p>\n\n<div class=\"wr-tour-popup__actions\">\n <!-- The three buttons are told apart by a class rather than by their position in\n the row: the back button is conditional, so an index means one thing on the\n first step and another everywhere else — and the labels are translated. -->\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"medium\"\n outlined\n class=\"wr-tour-popup__action wr-tour-popup__action--skip\"\n (click)=\"tour.stop()\"\n >\n {{ skipLabel }}\n </button>\n\n <span class=\"wr-tour-popup__spacer\"></span>\n\n @if (!isFirst()) {\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n outlined\n class=\"wr-tour-popup__action wr-tour-popup__action--back\"\n (click)=\"tour.prev()\"\n >\n {{ backLabel }}\n </button>\n }\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"primary\"\n class=\"wr-tour-popup__action wr-tour-popup__action--next\"\n (click)=\"tour.next()\"\n >\n {{ isLast() ? doneLabel : nextLabel }}\n </button>\n</div>\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 ConfigurableFocusTrap, ConfigurableFocusTrapFactory } from '@angular/cdk/a11y';\nimport { Directionality } from '@angular/cdk/bidi';\nimport { type OverlayRef, ScrollStrategyOptions } from '@angular/cdk/overlay';\nimport { ComponentPortal } from '@angular/cdk/portal';\nimport { isPlatformBrowser } from '@angular/common';\nimport {\n DOCUMENT,\n DestroyRef,\n EnvironmentInjector,\n Injector,\n PLATFORM_ID,\n Service,\n computed,\n inject,\n signal,\n} from '@angular/core';\n\nimport { WR_OVERLAY, wrMirrorOffsets } from 'ngwr/overlay';\n\nimport type { WrTourPlacement, WrTourStep } from './interfaces';\nimport { WR_TOUR_STEP, WrTourPopup } from './tour-popup';\n\n/** Fallback order per preferred side — the opposite side first, then the sides. */\nconst FALLBACKS: Record<WrTourPlacement, readonly WrTourPlacement[]> = {\n bottom: ['bottom', 'top', 'right', 'left'],\n top: ['top', 'bottom', 'right', 'left'],\n left: ['left', 'right', 'bottom', 'top'],\n right: ['right', 'left', 'bottom', 'top'],\n};\n\nconst CONNECTED: Record<\n WrTourPlacement,\n {\n originX: 'start' | 'center' | 'end';\n originY: 'top' | 'center' | 'bottom';\n overlayX: 'start' | 'center' | 'end';\n overlayY: 'top' | 'center' | 'bottom';\n }\n> = {\n bottom: { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top' },\n top: { originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom' },\n left: { originX: 'start', originY: 'center', overlayX: 'end', overlayY: 'center' },\n right: { originX: 'end', originY: 'center', overlayX: 'start', overlayY: 'center' },\n};\n\n/**\n * Guided product tour: a spotlight over one element at a time plus a popup\n * explaining it.\n *\n * Steps are data, not markup — call `start()` with an array and the service owns\n * the overlay, the cut-out, focus and the keyboard. A step whose target is not\n * on the page is skipped rather than shown floating, so a tour survives a\n * feature being hidden behind a flag or a permission.\n *\n * @example\n * ```ts\n * private readonly tour = inject(WrTour);\n *\n * showMeAround(): void {\n * this.tour.start([\n * { target: '[data-tour=\"search\"]', title: 'Search', content: 'Find anything by name.' },\n * { target: '[data-tour=\"filters\"]', content: 'Narrow the list down.', placement: 'right' },\n * ]);\n * }\n * ```\n *\n * @see https://ngwr.dev/reference/services/tour\n */\n@Service()\nexport class WrTour {\n private readonly overlay = inject(WR_OVERLAY);\n private readonly dir = inject(Directionality, { optional: true });\n private readonly scrollStrategies = inject(ScrollStrategyOptions);\n private readonly injector = inject(EnvironmentInjector);\n private readonly focusTrapFactory = inject(ConfigurableFocusTrapFactory);\n private readonly document = inject(DOCUMENT);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n private readonly steps = signal<readonly WrTourStep[]>([]);\n private readonly cursor = signal(-1);\n\n private overlayRef: OverlayRef | null = null;\n private focusTrap: ConfigurableFocusTrap | null = null;\n private spotlight: HTMLElement | null = null;\n private detachKeydown: (() => void) | null = null;\n private detachReposition: (() => void) | null = null;\n private restoreFocus: HTMLElement | null = null;\n\n /** Whether a tour is running. */\n readonly active = computed(() => this.cursor() >= 0);\n\n /** Zero-based index of the current step, `-1` when idle. */\n readonly index = computed(() => this.cursor());\n\n /** How many steps this tour has. */\n readonly total = computed(() => this.steps().length);\n\n /** The step being shown, or `null` when idle. */\n readonly step = computed<WrTourStep | null>(() => this.steps()[this.cursor()] ?? null);\n\n // Whether any step AFTER the current one can actually be shown. Settled once per\n // move, in `goTo`, because reachability is a DOM question and not a signal.\n private readonly noStepAfter = signal(false);\n\n /**\n * Whether this is the last step the user will see. Not `index() === total() - 1`:\n * a trailing step whose target is missing is skipped, so comparing against the\n * raw count left the primary button reading \"Next\" on the final card and then\n * ending the tour when it was pressed.\n */\n readonly isLast = computed(() => this.noStepAfter());\n\n constructor() {\n // Everything this service owns lives OUTSIDE its own injector: a CDK overlay, a\n // div appended to `document.body`, a document keydown listener and two window\n // listeners. Nothing removes them when the injector goes, so a tour running at\n // teardown left its cut-out on the page.\n inject(DestroyRef).onDestroy(() => this.stop());\n }\n\n /**\n * Begin a tour. Restarts if one is already running. A no-op under SSR — a\n * tour is a client-side affordance and there is nothing to spotlight on the\n * server.\n */\n start(steps: readonly WrTourStep[]): void {\n if (!this.isBrowser || steps.length === 0) return;\n this.stop();\n this.steps.set(steps);\n this.restoreFocus = this.document.activeElement as HTMLElement | null;\n this.goTo(0, 1);\n }\n\n /** Advance; finishes the tour past the last step. */\n next(): void {\n this.goTo(this.cursor() + 1, 1);\n }\n\n /** Go back; stays put on the first step. */\n prev(): void {\n this.goTo(this.cursor() - 1, -1);\n }\n\n /** End the tour and return focus where it was. */\n stop(): void {\n this.teardownStep();\n this.steps.set([]);\n this.cursor.set(-1);\n this.restoreFocus?.focus?.();\n this.restoreFocus = null;\n }\n\n /**\n * Move to `index`, skipping steps whose target is not in the DOM. `direction`\n * says which way to keep skipping, so a missing step never traps the tour\n * between two dead ends.\n */\n private goTo(index: number, direction: 1 | -1): void {\n const steps = this.steps();\n let i = index;\n while (i >= 0 && i < steps.length && this.resolve(steps[i]) === null) i += direction;\n\n if (i < 0) {\n // Ran off the start while going back — stay where we were.\n return;\n }\n if (i >= steps.length) {\n this.stop();\n return;\n }\n\n this.teardownStep();\n this.cursor.set(i);\n this.noStepAfter.set(!this.anyResolvableAfter(i));\n this.openStep(steps[i]);\n }\n\n /** Whether the tour has a step past `index` that can actually be shown. */\n private anyResolvableAfter(index: number): boolean {\n const steps = this.steps();\n for (let i = index + 1; i < steps.length; i += 1) {\n if (this.resolve(steps[i]) !== null) return true;\n }\n return false;\n }\n\n private resolve(step: WrTourStep): HTMLElement | null {\n if (typeof step.target !== 'string') return step.target.isConnected ? step.target : null;\n return this.document.querySelector<HTMLElement>(step.target);\n }\n\n private openStep(step: WrTourStep): void {\n const target = this.resolve(step);\n if (!target) return;\n\n target.scrollIntoView({ block: 'center', inline: 'nearest', behavior: 'smooth' });\n\n // The cut-out is a bare element sized to the target with an enormous spread\n // shadow: everything outside it dims, the target itself stays untouched.\n // Cheaper and far more robust than a clip-path polygon, and it needs no\n // recompute when the page reflows beyond moving the box.\n const spotlight = this.document.createElement('div');\n spotlight.className = 'wr-tour__spotlight';\n spotlight.setAttribute('aria-hidden', 'true');\n this.document.body.appendChild(spotlight);\n this.spotlight = spotlight;\n this.placeSpotlight(target);\n\n const placement = step.placement ?? 'bottom';\n const positionStrategy = this.overlay\n .position()\n .flexibleConnectedTo(target)\n .withPositions(\n wrMirrorOffsets(\n FALLBACKS[placement].map(side => ({\n ...CONNECTED[side],\n offsetY: side === 'bottom' ? 12 : side === 'top' ? -12 : 0,\n offsetX: side === 'right' ? 12 : side === 'left' ? -12 : 0,\n })),\n this.isRtl()\n )\n )\n .withPush(true);\n\n this.overlayRef = this.overlay.create({\n positionStrategy,\n scrollStrategy: this.scrollStrategies.reposition(),\n panelClass: 'wr-tour-overlay',\n });\n\n const portal = new ComponentPortal(\n WrTourPopup,\n null,\n Injector.create({ parent: this.injector, providers: [{ provide: WR_TOUR_STEP, useValue: this }] })\n );\n const ref = this.overlayRef.attach(portal);\n\n // Kept on the instance, not in a local: a trap registers with CDK's\n // `FocusTrapManager` and installs a capture-phase document focus listener, so an\n // undestroyed one leaks per STEP and every tour left a stack of them behind.\n this.focusTrap = this.focusTrapFactory.create(ref.location.nativeElement as HTMLElement);\n void this.focusTrap.focusInitialElementWhenReady();\n\n const onKey = (event: KeyboardEvent): void => {\n // The listener is on the document, so a step shown over a dialog would see the\n // Escape that dialog just handled and close the tour too.\n if (event.key === 'Escape' && !event.defaultPrevented) {\n event.preventDefault();\n this.stop();\n }\n };\n this.document.addEventListener('keydown', onKey);\n this.detachKeydown = () => this.document.removeEventListener('keydown', onKey);\n\n const reposition = (): void => this.placeSpotlight(target);\n window.addEventListener('scroll', reposition, true);\n window.addEventListener('resize', reposition);\n this.detachReposition = () => {\n window.removeEventListener('scroll', reposition, true);\n window.removeEventListener('resize', reposition);\n };\n }\n\n private placeSpotlight(target: HTMLElement): void {\n const el = this.spotlight;\n if (!el) return;\n const r = target.getBoundingClientRect();\n const pad = 6;\n el.style.top = `${r.top - pad}px`;\n el.style.left = `${r.left - pad}px`;\n el.style.width = `${r.width + pad * 2}px`;\n el.style.height = `${r.height + pad * 2}px`;\n }\n\n private teardownStep(): void {\n this.focusTrap?.destroy();\n this.focusTrap = null;\n this.detachKeydown?.();\n this.detachKeydown = null;\n this.detachReposition?.();\n this.detachReposition = null;\n this.overlayRef?.dispose();\n this.overlayRef = null;\n this.spotlight?.remove();\n this.spotlight = null;\n }\n\n /**\n * Whether the app reads right-to-left.\n *\n * Optional inject: `Directionality` is root-provided, so this is never null in\n * an app — but a bare `TestBed` that provides nothing should still get a\n * component that works, and defaulting to LTR is the honest fallback.\n */\n private isRtl(): boolean {\n return this.dir?.value === 'rtl';\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-tour.mjs","sources":["../../../projects/lib/tour/tour-popup.ts","../../../projects/lib/tour/tour-popup.html","../../../projects/lib/tour/tour.ts","../../../projects/lib/tour/ngwr-tour.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 { Component, InjectionToken, ViewEncapsulation, computed, inject } from '@angular/core';\n\nimport { WrButton } from 'ngwr/button';\nimport { readI18nText, useI18nFormatter } from 'ngwr/i18n';\n\nimport type { WrTourStep } from './interfaces';\n\n/** What the popup needs from the running tour, without importing the service (cycle). */\nexport interface WrTourController {\n readonly index: () => number;\n readonly total: () => number;\n readonly step: () => WrTourStep | null;\n /**\n * Whether this is the last step the user will actually see — the service looks\n * ahead for a reachable one, because a trailing step whose target is missing gets\n * skipped and the raw count would call the final card \"Next\".\n */\n readonly isLast: () => boolean;\n /**\n * Whether this is the first step the user will actually see — the service looks\n * BEHIND for a reachable one, for the mirror reason: a leading step whose target\n * is missing gets skipped, so the tour opens on a later index and the raw one\n * would put a Back button on the very first card.\n */\n readonly isFirst: () => boolean;\n next(): void;\n prev(): void;\n stop(): void;\n}\n\n/** Provided by `WrTour` when it attaches the popup portal. */\nexport const WR_TOUR_STEP = new InjectionToken<WrTourController>('WR_TOUR_STEP');\n\n/**\n * The card a tour shows next to the spotlit element. Rendered by `WrTour` into\n * a CDK overlay — not something you place yourself.\n *\n * @see https://ngwr.dev/reference/services/tour\n */\n@Component({\n selector: 'wr-tour-popup',\n templateUrl: './tour-popup.html',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-tour-popup', role: 'dialog', 'aria-modal': 'true', '[attr.aria-label]': 'ariaLabel()' },\n imports: [WrButton],\n})\nexport class WrTourPopup {\n protected readonly tour = inject(WR_TOUR_STEP);\n\n // `readI18nText`, not `useI18nText`: these have no consumer-facing input to\n // forward. Both helpers are reactive.\n protected readonly nextLabel = readI18nText('tour.next', 'Next');\n protected readonly backLabel = readI18nText('tour.back', 'Back');\n protected readonly doneLabel = readI18nText('tour.done', 'Done');\n protected readonly skipLabel = readI18nText('tour.skip', 'Skip tour');\n\n /** `Step {{current}} of {{total}}` — also the dialog's accessible name. */\n protected readonly progress = useI18nFormatter('tour.progress', 'Step {{current}} of {{total}}');\n\n // From the service, not `index() >= total() - 1`: a trailing step whose target is\n // missing gets skipped, so the raw count called the final card \"Next\" and then\n // ended the tour when it was pressed.\n protected readonly isLast = computed(() => this.tour.isLast());\n // Same story at the other end, and the same fix: `index() <= 0` rendered a Back\n // button on the first card of a tour whose leading step had been skipped, and\n // `prev()` walks off the start and returns — so the button did nothing at all.\n protected readonly isFirst = computed(() => this.tour.isFirst());\n\n protected readonly progressText = computed(() =>\n this.progress({ current: this.tour.index() + 1, total: this.tour.total() })\n );\n\n protected readonly ariaLabel = computed(() => {\n const title = this.tour.step()?.title;\n return title ? `${title} — ${this.progressText()}` : this.progressText();\n });\n}\n","@let step = tour.step();\n\n<p class=\"wr-tour-popup__progress\">{{ progressText() }}</p>\n\n@if (step?.title; as title) {\n <h2 class=\"wr-tour-popup__title\">{{ title }}</h2>\n}\n\n<p class=\"wr-tour-popup__content\">{{ step?.content }}</p>\n\n<div class=\"wr-tour-popup__actions\">\n <!-- The three buttons are told apart by a class rather than by their position in\n the row: the back button is conditional, so an index means one thing on the\n first step and another everywhere else — and the labels are translated. -->\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"medium\"\n outlined\n class=\"wr-tour-popup__action wr-tour-popup__action--skip\"\n (click)=\"tour.stop()\"\n >\n {{ skipLabel() }}\n </button>\n\n <span class=\"wr-tour-popup__spacer\"></span>\n\n @if (!isFirst()) {\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n outlined\n class=\"wr-tour-popup__action wr-tour-popup__action--back\"\n (click)=\"tour.prev()\"\n >\n {{ backLabel() }}\n </button>\n }\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"primary\"\n class=\"wr-tour-popup__action wr-tour-popup__action--next\"\n (click)=\"tour.next()\"\n >\n {{ isLast() ? doneLabel() : nextLabel() }}\n </button>\n</div>\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 ConfigurableFocusTrap, ConfigurableFocusTrapFactory } from '@angular/cdk/a11y';\nimport { Directionality } from '@angular/cdk/bidi';\nimport { type OverlayRef, ScrollStrategyOptions } from '@angular/cdk/overlay';\nimport { ComponentPortal } from '@angular/cdk/portal';\nimport { isPlatformBrowser } from '@angular/common';\nimport {\n DOCUMENT,\n DestroyRef,\n EnvironmentInjector,\n Injector,\n PLATFORM_ID,\n Service,\n computed,\n inject,\n signal,\n} from '@angular/core';\n\nimport { WR_OVERLAY, wrMirrorOffsets } from 'ngwr/overlay';\n\nimport type { WrTourPlacement, WrTourStep } from './interfaces';\nimport { WR_TOUR_STEP, WrTourPopup } from './tour-popup';\n\n/** Fallback order per preferred side — the opposite side first, then the sides. */\nconst FALLBACKS: Record<WrTourPlacement, readonly WrTourPlacement[]> = {\n bottom: ['bottom', 'top', 'right', 'left'],\n top: ['top', 'bottom', 'right', 'left'],\n left: ['left', 'right', 'bottom', 'top'],\n right: ['right', 'left', 'bottom', 'top'],\n};\n\nconst CONNECTED: Record<\n WrTourPlacement,\n {\n originX: 'start' | 'center' | 'end';\n originY: 'top' | 'center' | 'bottom';\n overlayX: 'start' | 'center' | 'end';\n overlayY: 'top' | 'center' | 'bottom';\n }\n> = {\n bottom: { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top' },\n top: { originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom' },\n left: { originX: 'start', originY: 'center', overlayX: 'end', overlayY: 'center' },\n right: { originX: 'end', originY: 'center', overlayX: 'start', overlayY: 'center' },\n};\n\n/**\n * Guided product tour: a spotlight over one element at a time plus a popup\n * explaining it.\n *\n * Steps are data, not markup — call `start()` with an array and the service owns\n * the overlay, the cut-out, focus and the keyboard. A step whose target is not\n * on the page is skipped rather than shown floating, so a tour survives a\n * feature being hidden behind a flag or a permission.\n *\n * @example\n * ```ts\n * private readonly tour = inject(WrTour);\n *\n * showMeAround(): void {\n * this.tour.start([\n * { target: '[data-tour=\"search\"]', title: 'Search', content: 'Find anything by name.' },\n * { target: '[data-tour=\"filters\"]', content: 'Narrow the list down.', placement: 'right' },\n * ]);\n * }\n * ```\n *\n * @see https://ngwr.dev/reference/services/tour\n */\n@Service()\nexport class WrTour {\n private readonly overlay = inject(WR_OVERLAY);\n private readonly dir = inject(Directionality, { optional: true });\n private readonly scrollStrategies = inject(ScrollStrategyOptions);\n private readonly injector = inject(EnvironmentInjector);\n private readonly focusTrapFactory = inject(ConfigurableFocusTrapFactory);\n private readonly document = inject(DOCUMENT);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n private readonly steps = signal<readonly WrTourStep[]>([]);\n private readonly cursor = signal(-1);\n\n private overlayRef: OverlayRef | null = null;\n private focusTrap: ConfigurableFocusTrap | null = null;\n private spotlight: HTMLElement | null = null;\n private detachKeydown: (() => void) | null = null;\n private detachReposition: (() => void) | null = null;\n private restoreFocus: HTMLElement | null = null;\n\n /** Whether a tour is running. */\n readonly active = computed(() => this.cursor() >= 0);\n\n /** Zero-based index of the current step, `-1` when idle. */\n readonly index = computed(() => this.cursor());\n\n /** How many steps this tour has. */\n readonly total = computed(() => this.steps().length);\n\n /** The step being shown, or `null` when idle. */\n readonly step = computed<WrTourStep | null>(() => this.steps()[this.cursor()] ?? null);\n\n // Whether any step AFTER the current one can actually be shown. Settled once per\n // move, in `goTo`, because reachability is a DOM question and not a signal.\n private readonly noStepAfter = signal(false);\n\n // The same question looking the other way. Settled in `goTo` for the same reason.\n private readonly noStepBefore = signal(true);\n\n /**\n * Whether this is the last step the user will see. Not `index() === total() - 1`:\n * a trailing step whose target is missing is skipped, so comparing against the\n * raw count left the primary button reading \"Next\" on the final card and then\n * ending the tour when it was pressed.\n */\n readonly isLast = computed(() => this.noStepAfter());\n\n /**\n * Whether this is the first step the user will see. Not `index() === 0`, for the\n * mirror of the reason above: a LEADING step whose target is missing is skipped,\n * so the tour opens on a later index and the raw one put a Back button on the\n * first card — one that did nothing at all, because `prev()` walks off the start\n * and returns without moving.\n */\n readonly isFirst = computed(() => this.noStepBefore());\n\n constructor() {\n // Everything this service owns lives OUTSIDE its own injector: a CDK overlay, a\n // div appended to `document.body`, a document keydown listener and two window\n // listeners. Nothing removes them when the injector goes, so a tour running at\n // teardown left its cut-out on the page.\n inject(DestroyRef).onDestroy(() => this.stop());\n }\n\n /**\n * Begin a tour. Restarts if one is already running. A no-op under SSR — a\n * tour is a client-side affordance and there is nothing to spotlight on the\n * server.\n */\n start(steps: readonly WrTourStep[]): void {\n if (!this.isBrowser || steps.length === 0) return;\n this.stop();\n this.steps.set(steps);\n this.restoreFocus = this.document.activeElement as HTMLElement | null;\n this.goTo(0, 1);\n }\n\n /** Advance; finishes the tour past the last step. */\n next(): void {\n this.goTo(this.cursor() + 1, 1);\n }\n\n /** Go back; stays put on the first step. */\n prev(): void {\n this.goTo(this.cursor() - 1, -1);\n }\n\n /** End the tour and return focus where it was. */\n stop(): void {\n this.teardownStep();\n this.steps.set([]);\n this.cursor.set(-1);\n this.restoreFocus?.focus?.();\n this.restoreFocus = null;\n }\n\n /**\n * Move to `index`, skipping steps whose target is not in the DOM. `direction`\n * says which way to keep skipping, so a missing step never traps the tour\n * between two dead ends.\n */\n private goTo(index: number, direction: 1 | -1): void {\n const steps = this.steps();\n let i = index;\n while (i >= 0 && i < steps.length && this.resolve(steps[i]) === null) i += direction;\n\n if (i < 0) {\n // Ran off the start while going back — stay where we were.\n return;\n }\n if (i >= steps.length) {\n this.stop();\n return;\n }\n\n this.teardownStep();\n this.cursor.set(i);\n this.noStepAfter.set(!this.anyResolvableAfter(i));\n this.noStepBefore.set(!this.anyResolvableBefore(i));\n this.openStep(steps[i]);\n }\n\n /** Whether the tour has a step past `index` that can actually be shown. */\n private anyResolvableAfter(index: number): boolean {\n const steps = this.steps();\n for (let i = index + 1; i < steps.length; i += 1) {\n if (this.resolve(steps[i]) !== null) return true;\n }\n return false;\n }\n\n /** Whether the tour has a step before `index` that can actually be shown. */\n private anyResolvableBefore(index: number): boolean {\n const steps = this.steps();\n for (let i = index - 1; i >= 0; i -= 1) {\n if (this.resolve(steps[i]) !== null) return true;\n }\n return false;\n }\n\n private resolve(step: WrTourStep): HTMLElement | null {\n if (typeof step.target !== 'string') return step.target.isConnected ? step.target : null;\n return this.document.querySelector<HTMLElement>(step.target);\n }\n\n private openStep(step: WrTourStep): void {\n const target = this.resolve(step);\n if (!target) return;\n\n target.scrollIntoView({ block: 'center', inline: 'nearest', behavior: 'smooth' });\n\n // The cut-out is a bare element sized to the target with an enormous spread\n // shadow: everything outside it dims, the target itself stays untouched.\n // Cheaper and far more robust than a clip-path polygon, and it needs no\n // recompute when the page reflows beyond moving the box.\n const spotlight = this.document.createElement('div');\n spotlight.className = 'wr-tour__spotlight';\n spotlight.setAttribute('aria-hidden', 'true');\n this.document.body.appendChild(spotlight);\n this.spotlight = spotlight;\n this.placeSpotlight(target);\n\n const placement = step.placement ?? 'bottom';\n const positionStrategy = this.overlay\n .position()\n .flexibleConnectedTo(target)\n .withPositions(\n wrMirrorOffsets(\n FALLBACKS[placement].map(side => ({\n ...CONNECTED[side],\n offsetY: side === 'bottom' ? 12 : side === 'top' ? -12 : 0,\n offsetX: side === 'right' ? 12 : side === 'left' ? -12 : 0,\n })),\n this.isRtl()\n )\n )\n .withPush(true);\n\n this.overlayRef = this.overlay.create({\n positionStrategy,\n scrollStrategy: this.scrollStrategies.reposition(),\n panelClass: 'wr-tour-overlay',\n });\n\n const portal = new ComponentPortal(\n WrTourPopup,\n null,\n Injector.create({ parent: this.injector, providers: [{ provide: WR_TOUR_STEP, useValue: this }] })\n );\n const ref = this.overlayRef.attach(portal);\n\n // Kept on the instance, not in a local: a trap registers with CDK's\n // `FocusTrapManager` and installs a capture-phase document focus listener, so an\n // undestroyed one leaks per STEP and every tour left a stack of them behind.\n this.focusTrap = this.focusTrapFactory.create(ref.location.nativeElement as HTMLElement);\n void this.focusTrap.focusInitialElementWhenReady();\n\n const onKey = (event: KeyboardEvent): void => {\n // The listener is on the document, so a step shown over a dialog would see the\n // Escape that dialog just handled and close the tour too.\n if (event.key === 'Escape' && !event.defaultPrevented) {\n event.preventDefault();\n this.stop();\n }\n };\n this.document.addEventListener('keydown', onKey);\n this.detachKeydown = () => this.document.removeEventListener('keydown', onKey);\n\n const reposition = (): void => this.placeSpotlight(target);\n window.addEventListener('scroll', reposition, true);\n window.addEventListener('resize', reposition);\n this.detachReposition = () => {\n window.removeEventListener('scroll', reposition, true);\n window.removeEventListener('resize', reposition);\n };\n }\n\n private placeSpotlight(target: HTMLElement): void {\n const el = this.spotlight;\n if (!el) return;\n const r = target.getBoundingClientRect();\n const pad = 6;\n el.style.top = `${r.top - pad}px`;\n el.style.left = `${r.left - pad}px`;\n el.style.width = `${r.width + pad * 2}px`;\n el.style.height = `${r.height + pad * 2}px`;\n }\n\n private teardownStep(): void {\n this.focusTrap?.destroy();\n this.focusTrap = null;\n this.detachKeydown?.();\n this.detachKeydown = null;\n this.detachReposition?.();\n this.detachReposition = null;\n this.overlayRef?.dispose();\n this.overlayRef = null;\n this.spotlight?.remove();\n this.spotlight = null;\n }\n\n /**\n * Whether the app reads right-to-left.\n *\n * Optional inject: `Directionality` is root-provided, so this is never null in\n * an app — but a bare `TestBed` that provides nothing should still get a\n * component that works, and defaulting to LTR is the honest fallback.\n */\n private isRtl(): boolean {\n return this.dir?.value === 'rtl';\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -199,10 +199,10 @@ class WrTransfer {
199
199
  someChecked: shown.length > 0 && shown.length < enabled.length,
200
200
  };
201
201
  }
202
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrTransfer, deps: [], target: i0.ɵɵFactoryTarget.Component });
203
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: WrTransfer, isStandalone: true, selector: "wr-transfer", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, sourceTitle: { classPropertyName: "sourceTitle", publicName: "sourceTitle", isSignal: true, isRequired: false, transformFunction: null }, targetTitle: { classPropertyName: "targetTitle", publicName: "targetTitle", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, selectAllLabel: { classPropertyName: "selectAllLabel", publicName: "selectAllLabel", isSignal: true, isRequired: false, transformFunction: null }, toTargetLabel: { classPropertyName: "toTargetLabel", publicName: "toTargetLabel", isSignal: true, isRequired: false, transformFunction: null }, toSourceLabel: { classPropertyName: "toSourceLabel", publicName: "toSourceLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch" }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: "<!--\n Two panes and the buttons between them. Each pane is a PLAIN `<ul>` of\n checkboxes, deliberately: dressing it as a `role=\"listbox\"` of `role=\"option\"`\n rows reads better in the abstract but is invalid \u2014 an option may not contain an\n interactive control, and the axe gate says so. The checkbox is the thing the\n user operates, so it is the thing the semantics should describe; the pane\n heading names the group.\n-->\n@for (pane of panes; track pane) {\n @let state = pane === 'source' ? source() : target();\n @let title = pane === 'source' ? resolvedSourceTitle() : resolvedTargetTitle();\n\n <div class=\"wr-transfer__pane\" [class.wr-transfer__pane--target]=\"pane === 'target'\">\n <div class=\"wr-transfer__head\">\n <wr-checkbox\n size=\"sm\"\n [checked]=\"state.allChecked\"\n [indeterminate]=\"state.someChecked\"\n [disabled]=\"disabled() || state.enabledCount === 0\"\n [ariaLabel]=\"`${title} \u2014 ${resolvedSelectAll()}`\"\n (checkedChange)=\"toggleAll(pane, $event)\"\n />\n <span class=\"wr-transfer__title\">{{ title }}</span>\n <span class=\"wr-transfer__count\">{{\n countLabel({ checked: state.checkedCount, total: state.rows.length })\n }}</span>\n </div>\n\n @if (searchable()) {\n <div class=\"wr-transfer__search\">\n <input\n wrInput\n wrSize=\"sm\"\n type=\"search\"\n [placeholder]=\"resolvedSearch()\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"`${title} \u2014 ${resolvedSearch()}`\"\n (input)=\"onSearch(pane, $event)\"\n />\n </div>\n }\n\n <ul class=\"wr-transfer__list\" [attr.aria-label]=\"title\">\n @for (item of state.rows; track item.value) {\n <li class=\"wr-transfer__item\" [class.wr-transfer__item--disabled]=\"item.disabled\">\n <wr-checkbox\n size=\"sm\"\n [checked]=\"isChecked(pane, item)\"\n [disabled]=\"disabled() || !!item.disabled\"\n (checkedChange)=\"toggle(pane, item, $event)\"\n (touch)=\"touch.emit()\"\n >\n {{ item.label }}\n </wr-checkbox>\n </li>\n } @empty {\n <li class=\"wr-transfer__empty\">{{ resolvedEmpty() }}</li>\n }\n </ul>\n </div>\n\n <!-- The buttons live between the panes, so they are emitted after the first. -->\n @if (pane === 'source') {\n <div class=\"wr-transfer__actions\">\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"primary\"\n outlined\n class=\"wr-transfer__move wr-transfer__move--to-target\"\n [disabled]=\"!canMoveRight()\"\n [attr.aria-label]=\"resolvedToTarget()\"\n (click)=\"moveRight()\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </button>\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"primary\"\n outlined\n class=\"wr-transfer__move wr-transfer__move--to-source\"\n [disabled]=\"!canMoveLeft()\"\n [attr.aria-label]=\"resolvedToSource()\"\n (click)=\"moveLeft()\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n </button>\n </div>\n }\n}\n", dependencies: [{ kind: "component", type: WrButton, selector: "wr-btn, button[wr-btn], a[wr-btn]", inputs: ["color", "size", "shape", "icon", "iconPosition", "disabled", "outlined", "block", "loading", "isDisabledWhenLoading"] }, { kind: "component", type: WrCheckbox, selector: "wr-checkbox", inputs: ["id", "checkboxValue", "ariaLabel", "checked", "disabled", "size", "icon", "indeterminate"], outputs: ["checkedChange", "touch"] }, { kind: "directive", type: WrInput, selector: "input[wrInput], textarea[wrInput]", inputs: ["wrSize", "rounded"] }], encapsulation: i0.ViewEncapsulation.None });
202
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrTransfer, deps: [], target: i0.ɵɵFactoryTarget.Component });
203
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: WrTransfer, isStandalone: true, selector: "wr-transfer", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, sourceTitle: { classPropertyName: "sourceTitle", publicName: "sourceTitle", isSignal: true, isRequired: false, transformFunction: null }, targetTitle: { classPropertyName: "targetTitle", publicName: "targetTitle", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, selectAllLabel: { classPropertyName: "selectAllLabel", publicName: "selectAllLabel", isSignal: true, isRequired: false, transformFunction: null }, toTargetLabel: { classPropertyName: "toTargetLabel", publicName: "toTargetLabel", isSignal: true, isRequired: false, transformFunction: null }, toSourceLabel: { classPropertyName: "toSourceLabel", publicName: "toSourceLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch" }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: "<!--\n Two panes and the buttons between them. Each pane is a PLAIN `<ul>` of\n checkboxes, deliberately: dressing it as a `role=\"listbox\"` of `role=\"option\"`\n rows reads better in the abstract but is invalid \u2014 an option may not contain an\n interactive control, and the axe gate says so. The checkbox is the thing the\n user operates, so it is the thing the semantics should describe; the pane\n heading names the group.\n-->\n@for (pane of panes; track pane) {\n @let state = pane === 'source' ? source() : target();\n @let title = pane === 'source' ? resolvedSourceTitle() : resolvedTargetTitle();\n\n <div class=\"wr-transfer__pane\" [class.wr-transfer__pane--target]=\"pane === 'target'\">\n <div class=\"wr-transfer__head\">\n <wr-checkbox\n size=\"sm\"\n [checked]=\"state.allChecked\"\n [indeterminate]=\"state.someChecked\"\n [disabled]=\"disabled() || state.enabledCount === 0\"\n [ariaLabel]=\"`${title} \u2014 ${resolvedSelectAll()}`\"\n (checkedChange)=\"toggleAll(pane, $event)\"\n />\n <span class=\"wr-transfer__title\">{{ title }}</span>\n <span class=\"wr-transfer__count\">{{\n countLabel({ checked: state.checkedCount, total: state.rows.length })\n }}</span>\n </div>\n\n @if (searchable()) {\n <div class=\"wr-transfer__search\">\n <input\n wrInput\n wrSize=\"sm\"\n type=\"search\"\n [placeholder]=\"resolvedSearch()\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"`${title} \u2014 ${resolvedSearch()}`\"\n (input)=\"onSearch(pane, $event)\"\n />\n </div>\n }\n\n <ul class=\"wr-transfer__list\" [attr.aria-label]=\"title\">\n @for (item of state.rows; track item.value) {\n <li class=\"wr-transfer__item\" [class.wr-transfer__item--disabled]=\"item.disabled\">\n <wr-checkbox\n size=\"sm\"\n [checked]=\"isChecked(pane, item)\"\n [disabled]=\"disabled() || !!item.disabled\"\n (checkedChange)=\"toggle(pane, item, $event)\"\n (touch)=\"touch.emit()\"\n >\n {{ item.label }}\n </wr-checkbox>\n </li>\n } @empty {\n <li class=\"wr-transfer__empty\">{{ resolvedEmpty() }}</li>\n }\n </ul>\n </div>\n\n <!-- The buttons live between the panes, so they are emitted after the first. -->\n @if (pane === 'source') {\n <div class=\"wr-transfer__actions\">\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"primary\"\n outlined\n class=\"wr-transfer__move wr-transfer__move--to-target\"\n [disabled]=\"!canMoveRight()\"\n [attr.aria-label]=\"resolvedToTarget()\"\n (click)=\"moveRight()\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </button>\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"primary\"\n outlined\n class=\"wr-transfer__move wr-transfer__move--to-source\"\n [disabled]=\"!canMoveLeft()\"\n [attr.aria-label]=\"resolvedToSource()\"\n (click)=\"moveLeft()\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n </button>\n </div>\n }\n}\n", dependencies: [{ kind: "component", type: WrButton, selector: "wr-btn, button[wr-btn], a[wr-btn]", inputs: ["color", "size", "shape", "icon", "iconPosition", "disabled", "outlined", "block", "loading", "isDisabledWhenLoading"] }, { kind: "component", type: WrCheckbox, selector: "wr-checkbox", inputs: ["id", "checkboxValue", "ariaLabel", "checked", "disabled", "size", "icon", "indeterminate"], outputs: ["checkedChange", "touch"] }, { kind: "directive", type: WrInput, selector: "input[wrInput], textarea[wrInput]", inputs: ["wrSize", "rounded"] }], encapsulation: i0.ViewEncapsulation.None });
204
204
  }
205
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrTransfer, decorators: [{
205
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrTransfer, decorators: [{
206
206
  type: Component,
207
207
  args: [{ selector: 'wr-transfer', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, imports: [WrButton, WrCheckbox, WrInput], template: "<!--\n Two panes and the buttons between them. Each pane is a PLAIN `<ul>` of\n checkboxes, deliberately: dressing it as a `role=\"listbox\"` of `role=\"option\"`\n rows reads better in the abstract but is invalid \u2014 an option may not contain an\n interactive control, and the axe gate says so. The checkbox is the thing the\n user operates, so it is the thing the semantics should describe; the pane\n heading names the group.\n-->\n@for (pane of panes; track pane) {\n @let state = pane === 'source' ? source() : target();\n @let title = pane === 'source' ? resolvedSourceTitle() : resolvedTargetTitle();\n\n <div class=\"wr-transfer__pane\" [class.wr-transfer__pane--target]=\"pane === 'target'\">\n <div class=\"wr-transfer__head\">\n <wr-checkbox\n size=\"sm\"\n [checked]=\"state.allChecked\"\n [indeterminate]=\"state.someChecked\"\n [disabled]=\"disabled() || state.enabledCount === 0\"\n [ariaLabel]=\"`${title} \u2014 ${resolvedSelectAll()}`\"\n (checkedChange)=\"toggleAll(pane, $event)\"\n />\n <span class=\"wr-transfer__title\">{{ title }}</span>\n <span class=\"wr-transfer__count\">{{\n countLabel({ checked: state.checkedCount, total: state.rows.length })\n }}</span>\n </div>\n\n @if (searchable()) {\n <div class=\"wr-transfer__search\">\n <input\n wrInput\n wrSize=\"sm\"\n type=\"search\"\n [placeholder]=\"resolvedSearch()\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"`${title} \u2014 ${resolvedSearch()}`\"\n (input)=\"onSearch(pane, $event)\"\n />\n </div>\n }\n\n <ul class=\"wr-transfer__list\" [attr.aria-label]=\"title\">\n @for (item of state.rows; track item.value) {\n <li class=\"wr-transfer__item\" [class.wr-transfer__item--disabled]=\"item.disabled\">\n <wr-checkbox\n size=\"sm\"\n [checked]=\"isChecked(pane, item)\"\n [disabled]=\"disabled() || !!item.disabled\"\n (checkedChange)=\"toggle(pane, item, $event)\"\n (touch)=\"touch.emit()\"\n >\n {{ item.label }}\n </wr-checkbox>\n </li>\n } @empty {\n <li class=\"wr-transfer__empty\">{{ resolvedEmpty() }}</li>\n }\n </ul>\n </div>\n\n <!-- The buttons live between the panes, so they are emitted after the first. -->\n @if (pane === 'source') {\n <div class=\"wr-transfer__actions\">\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"primary\"\n outlined\n class=\"wr-transfer__move wr-transfer__move--to-target\"\n [disabled]=\"!canMoveRight()\"\n [attr.aria-label]=\"resolvedToTarget()\"\n (click)=\"moveRight()\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </button>\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"primary\"\n outlined\n class=\"wr-transfer__move wr-transfer__move--to-source\"\n [disabled]=\"!canMoveLeft()\"\n [attr.aria-label]=\"resolvedToSource()\"\n (click)=\"moveLeft()\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n </button>\n </div>\n }\n}\n" }]
208
208
  }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touch: [{ type: i0.Output, args: ["touch"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], sourceTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "sourceTitle", required: false }] }], targetTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "targetTitle", required: false }] }], searchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchPlaceholder", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], selectAllLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectAllLabel", required: false }] }], toTargetLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "toTargetLabel", required: false }] }], toSourceLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "toSourceLabel", required: false }] }] } });
@@ -4,7 +4,7 @@ import { ScrollStrategyOptions } from '@angular/cdk/overlay';
4
4
  import { TemplatePortal } from '@angular/cdk/portal';
5
5
  import { isPlatformBrowser, NgTemplateOutlet } from '@angular/common';
6
6
  import * as i0 from '@angular/core';
7
- import { input, model, computed, output, inject, ElementRef, ViewContainerRef, DestroyRef, PLATFORM_ID, signal, viewChild, TemplateRef, effect, untracked, ViewEncapsulation, Component } from '@angular/core';
7
+ import { input, model, computed, output, inject, ElementRef, ViewContainerRef, DestroyRef, PLATFORM_ID, Injector, signal, viewChild, TemplateRef, effect, untracked, afterNextRender, ViewEncapsulation, Component } from '@angular/core';
8
8
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
9
9
  import { useI18nText, readI18nText, useI18nFormatter } from 'ngwr/i18n';
10
10
  import { WR_OVERLAY, WrOutsideClick } from 'ngwr/overlay';
@@ -159,6 +159,7 @@ class WrTree {
159
159
  scrollStrategies = inject(ScrollStrategyOptions);
160
160
  destroyRef = inject(DestroyRef);
161
161
  platformId = inject(PLATFORM_ID);
162
+ injector = inject(Injector);
162
163
  /**
163
164
  * Ambient reading direction. Optional so a bare `TestBed` — or a consumer who
164
165
  * never set a direction — needs no provider; `Directionality` is root-provided
@@ -185,7 +186,7 @@ class WrTree {
185
186
  return Math.min(this.focusedIndex(), last);
186
187
  }, /* @ts-ignore */
187
188
  ...(ngDevMode ? [{ debugName: "cursorIndex" }] : /* istanbul ignore next */ []));
188
- /** Resolved ARIA labels — resolved once at construction, so a runtime locale switch needs a re-create. */
189
+ /** Resolved ARIA labels — signals, so they follow both a late catalog and a locale switch. */
189
190
  clearLabel = readI18nText('tree.clearSelection', 'Clear selection');
190
191
  expandLabel = readI18nText('tree.expand', 'Expand');
191
192
  collapseLabel = readI18nText('tree.collapse', 'Collapse');
@@ -718,10 +719,27 @@ class WrTree {
718
719
  panelClass: 'wr-tree-overlay',
719
720
  });
720
721
  this.overlayRef.attach(new TemplatePortal(tpl, this.vcr));
721
- // Virtual mode drives keyboard nav via aria-activedescendant on the list
722
- // container, so it must hold focus once the panel is attached.
723
- if (this.virtualized())
724
- queueMicrotask(() => this.listElement?.focus());
722
+ // The panel owns the keyboard from here, in BOTH render shapes. `onKeydown`
723
+ // lives on the `<ul>` inside the portal and the trigger has no handler of
724
+ // its own, so leaving focus on the trigger — which is what the non-virtual
725
+ // shape used to do — made an overlay tree mouse-only: arrows, Home/End and
726
+ // Enter all did nothing, and the rows were reachable only by tabbing through
727
+ // the rest of the page, since the pane is appended to `<body>`. Virtual mode
728
+ // focuses the LIST (managed focus via `aria-activedescendant`); the
729
+ // non-virtual shape focuses the roving row, which carries the tab stop.
730
+ //
731
+ // `afterNextRender`, NOT `queueMicrotask`: under zoneless CD the scheduler
732
+ // runs change detection in a macrotask, so a microtask queued here fires
733
+ // first and focuses whatever the panel held before this open — the same trap
734
+ // that once left `wr-calendar`'s ring and real focus on different days.
735
+ afterNextRender(() => {
736
+ if (this.virtualized()) {
737
+ this.listElement?.focus();
738
+ return;
739
+ }
740
+ const row = this.overlayRef?.overlayElement.querySelector(`[data-tree-index="${this.cursorIndex()}"]`);
741
+ row?.focus();
742
+ }, { injector: this.injector });
725
743
  this.outsideClick
726
744
  .outsidePointerEvents(this.overlayRef)
727
745
  .pipe(takeUntilDestroyed(this.destroyRef))
@@ -754,12 +772,12 @@ class WrTree {
754
772
  if (inPanel)
755
773
  this.host.nativeElement.querySelector('.wr-tree__trigger')?.focus();
756
774
  }
757
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrTree, deps: [], target: i0.ɵɵFactoryTarget.Component });
758
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: WrTree, isStandalone: true, selector: "wr-tree", inputs: { nodes: { classPropertyName: "nodes", publicName: "nodes", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, openOn: { classPropertyName: "openOn", publicName: "openOn", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, maxTagCount: { classPropertyName: "maxTagCount", publicName: "maxTagCount", isSignal: true, isRequired: false, transformFunction: null }, defaultExpandAll: { classPropertyName: "defaultExpandAll", publicName: "defaultExpandAll", isSignal: true, isRequired: false, transformFunction: null }, virtualScroll: { classPropertyName: "virtualScroll", publicName: "virtualScroll", isSignal: true, isRequired: false, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, viewportHeight: { classPropertyName: "viewportHeight", publicName: "viewportHeight", isSignal: true, isRequired: false, transformFunction: null }, overscan: { classPropertyName: "overscan", publicName: "overscan", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selectedChange", expanded: "expandedChange", value: "valueChange", touch: "touch" }, host: { properties: { "class": "classes()" } }, viewQueries: [{ propertyName: "panelTpl", first: true, predicate: ["panelTpl"], descendants: true, read: TemplateRef, isSignal: true }], ngImport: i0, template: "@if (isOverlay()) {\n <button\n type=\"button\"\n class=\"wr-tree__trigger\"\n role=\"combobox\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [disabled]=\"disabled()\"\n [attr.aria-haspopup]=\"'tree'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"panelId\"\n (click)=\"onTriggerClick()\"\n (blur)=\"onBlur()\"\n >\n @if (isMulti()) {\n @if (hasSelection()) {\n <span class=\"wr-tree__chips\">\n @for (chip of visibleChips(); track chip.id) {\n <span class=\"wr-tree__chip\">\n <span class=\"wr-tree__chip-label\">{{ chip.label }}</span>\n <span\n class=\"wr-tree__chip-remove\"\n role=\"button\"\n [attr.aria-label]=\"chipRemoveLabel({ label: chip.label })\"\n tabindex=\"-1\"\n (click)=\"removeChip(chip.id, $event)\"\n (keydown.enter)=\"removeChip(chip.id, $event)\"\n (keydown.space)=\"removeChip(chip.id, $event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n </span>\n }\n @if (hiddenChipCount(); as extra) {\n <span class=\"wr-tree__chip wr-tree__chip--more\">+{{ extra }} more</span>\n }\n </span>\n } @else {\n <span class=\"wr-tree__placeholder\">{{ resolvedPlaceholder() }}</span>\n }\n } @else {\n @if (singleLabel(); as label) {\n <span class=\"wr-tree__value\">{{ label }}</span>\n } @else {\n <span class=\"wr-tree__placeholder\">{{ resolvedPlaceholder() }}</span>\n }\n }\n\n @if (clearable() && hasSelection() && !disabled()) {\n <span\n class=\"wr-tree__clear\"\n role=\"button\"\n [attr.aria-label]=\"clearLabel\"\n tabindex=\"-1\"\n (click)=\"clearSelection($event)\"\n (keydown.enter)=\"clearSelection($event)\"\n (keydown.space)=\"clearSelection($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n\n <svg\n class=\"wr-icon__svg wr-tree__chevron\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n\n <!-- Tree list lives inside the overlay panel. -->\n <ng-template #panelTpl>\n <div class=\"wr-tree-panel\" [class.wr-tree-panel--virtual]=\"virtualized()\" [attr.id]=\"panelId\">\n <ng-container [ngTemplateOutlet]=\"listTpl\" />\n </div>\n </ng-template>\n} @else {\n <ng-container [ngTemplateOutlet]=\"listTpl\" />\n}\n\n<!-- Shared row list \u2014 rendered inline (default) or projected into the\n overlay panel (when openOn=\"overlay\"). -->\n<ng-template #listTpl>\n <!-- Roving tabindex (non-virtual) / aria-activedescendant (virtual): the tree\n owns keyboard focus and routes arrow keys to its currently-focused row. -->\n <ul\n class=\"wr-tree__list\"\n role=\"tree\"\n [attr.aria-multiselectable]=\"isMulti() ? 'true' : null\"\n [class.wr-tree__list--virtual]=\"virtualized()\"\n [attr.tabindex]=\"virtualized() ? 0 : null\"\n [attr.aria-activedescendant]=\"activeRowId()\"\n [style.max-height]=\"virtualized() ? resolvedViewportHeight() : null\"\n [style.overflow-y]=\"virtualized() ? 'auto' : null\"\n (keydown)=\"onKeydown($event)\"\n >\n @if (virtualized() && virtualWindow().padTop > 0) {\n <!-- `aria-setsize` / `aria-posinset` in BOTH shapes: the list is flat with no\n `role=\"group\"` either way, so the set a row belongs to is never expressed\n by the DOM. Gated behind the virtual flag, the same tree announced a\n position with windowing on and nothing with it off. -->\n <li\n class=\"wr-tree__spacer\"\n role=\"presentation\"\n aria-hidden=\"true\"\n [style.height.px]=\"virtualWindow().padTop\"\n ></li>\n }\n @for (entry of windowRows(); track entry.node.id) {\n <!-- Click handler lives on focusable rows; keyboard handling is owned\n by the parent <ul> via the roving-tabindex pattern. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -->\n <li\n class=\"wr-tree__row\"\n role=\"treeitem\"\n [attr.id]=\"virtualized() ? rowId(entry.index) : null\"\n [class.wr-tree__row--selected]=\"selectedSet().has(entry.node.id)\"\n [class.wr-tree__row--active]=\"virtualized() && entry.index === cursorIndex()\"\n [class.wr-tree__row--disabled]=\"entry.node.disabled\"\n [attr.aria-level]=\"entry.depth + 1\"\n [attr.aria-setsize]=\"entry.setsize\"\n [attr.aria-posinset]=\"entry.posinset\"\n [attr.aria-expanded]=\"entry.hasChildren ? expandedSet().has(entry.node.id) : null\"\n [attr.aria-selected]=\"selectionMode() === 'none' ? null : selectedSet().has(entry.node.id)\"\n [attr.aria-disabled]=\"entry.node.disabled || disabled() || null\"\n [attr.data-tree-index]=\"entry.index\"\n [attr.tabindex]=\"virtualized() ? null : entry.index === cursorIndex() ? 0 : -1\"\n [style.--wr-tree-depth]=\"entry.depth\"\n (click)=\"onRowClick(entry, $event)\"\n >\n @if (entry.hasChildren) {\n <button\n type=\"button\"\n class=\"wr-tree__toggle\"\n tabindex=\"-1\"\n [attr.aria-label]=\"expandedSet().has(entry.node.id) ? collapseLabel : expandLabel\"\n (click)=\"onToggleClick(entry.node, $event)\"\n >\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path d=\"M3 1 L7 5 L3 9 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n } @else {\n <span class=\"wr-tree__toggle wr-tree__toggle--leaf\" aria-hidden=\"true\"></span>\n }\n <span class=\"wr-tree__label\">{{ entry.node.label }}</span>\n </li>\n }\n @if (virtualized() && virtualWindow().padBottom > 0) {\n <li\n class=\"wr-tree__spacer\"\n role=\"presentation\"\n aria-hidden=\"true\"\n [style.height.px]=\"virtualWindow().padBottom\"\n ></li>\n }\n </ul>\n</ng-template>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], encapsulation: i0.ViewEncapsulation.None });
775
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrTree, deps: [], target: i0.ɵɵFactoryTarget.Component });
776
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: WrTree, isStandalone: true, selector: "wr-tree", inputs: { nodes: { classPropertyName: "nodes", publicName: "nodes", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, openOn: { classPropertyName: "openOn", publicName: "openOn", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, maxTagCount: { classPropertyName: "maxTagCount", publicName: "maxTagCount", isSignal: true, isRequired: false, transformFunction: null }, defaultExpandAll: { classPropertyName: "defaultExpandAll", publicName: "defaultExpandAll", isSignal: true, isRequired: false, transformFunction: null }, virtualScroll: { classPropertyName: "virtualScroll", publicName: "virtualScroll", isSignal: true, isRequired: false, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, viewportHeight: { classPropertyName: "viewportHeight", publicName: "viewportHeight", isSignal: true, isRequired: false, transformFunction: null }, overscan: { classPropertyName: "overscan", publicName: "overscan", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selectedChange", expanded: "expandedChange", value: "valueChange", touch: "touch" }, host: { properties: { "class": "classes()" } }, viewQueries: [{ propertyName: "panelTpl", first: true, predicate: ["panelTpl"], descendants: true, read: TemplateRef, isSignal: true }], ngImport: i0, template: "@if (isOverlay()) {\n <button\n type=\"button\"\n class=\"wr-tree__trigger\"\n role=\"combobox\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [disabled]=\"disabled()\"\n [attr.aria-haspopup]=\"'tree'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"panelId\"\n (click)=\"onTriggerClick()\"\n (blur)=\"onBlur()\"\n >\n @if (isMulti()) {\n @if (hasSelection()) {\n <span class=\"wr-tree__chips\">\n @for (chip of visibleChips(); track chip.id) {\n <span class=\"wr-tree__chip\">\n <span class=\"wr-tree__chip-label\">{{ chip.label }}</span>\n <span\n class=\"wr-tree__chip-remove\"\n role=\"button\"\n [attr.aria-label]=\"chipRemoveLabel({ label: chip.label })\"\n tabindex=\"-1\"\n (click)=\"removeChip(chip.id, $event)\"\n (keydown.enter)=\"removeChip(chip.id, $event)\"\n (keydown.space)=\"removeChip(chip.id, $event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n </span>\n }\n @if (hiddenChipCount(); as extra) {\n <span class=\"wr-tree__chip wr-tree__chip--more\">+{{ extra }} more</span>\n }\n </span>\n } @else {\n <span class=\"wr-tree__placeholder\">{{ resolvedPlaceholder() }}</span>\n }\n } @else {\n @if (singleLabel(); as label) {\n <span class=\"wr-tree__value\">{{ label }}</span>\n } @else {\n <span class=\"wr-tree__placeholder\">{{ resolvedPlaceholder() }}</span>\n }\n }\n\n @if (clearable() && hasSelection() && !disabled()) {\n <span\n class=\"wr-tree__clear\"\n role=\"button\"\n [attr.aria-label]=\"clearLabel()\"\n tabindex=\"-1\"\n (click)=\"clearSelection($event)\"\n (keydown.enter)=\"clearSelection($event)\"\n (keydown.space)=\"clearSelection($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n\n <svg\n class=\"wr-icon__svg wr-tree__chevron\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n\n <!-- Tree list lives inside the overlay panel. -->\n <ng-template #panelTpl>\n <div class=\"wr-tree-panel\" [class.wr-tree-panel--virtual]=\"virtualized()\" [attr.id]=\"panelId\">\n <ng-container [ngTemplateOutlet]=\"listTpl\" />\n </div>\n </ng-template>\n} @else {\n <ng-container [ngTemplateOutlet]=\"listTpl\" />\n}\n\n<!-- Shared row list \u2014 rendered inline (default) or projected into the\n overlay panel (when openOn=\"overlay\"). -->\n<ng-template #listTpl>\n <!-- Roving tabindex (non-virtual) / aria-activedescendant (virtual): the tree\n owns keyboard focus and routes arrow keys to its currently-focused row. -->\n <ul\n class=\"wr-tree__list\"\n role=\"tree\"\n [attr.aria-multiselectable]=\"isMulti() ? 'true' : null\"\n [class.wr-tree__list--virtual]=\"virtualized()\"\n [attr.tabindex]=\"virtualized() ? 0 : null\"\n [attr.aria-activedescendant]=\"activeRowId()\"\n [style.max-height]=\"virtualized() ? resolvedViewportHeight() : null\"\n [style.overflow-y]=\"virtualized() ? 'auto' : null\"\n (keydown)=\"onKeydown($event)\"\n >\n @if (virtualized() && virtualWindow().padTop > 0) {\n <!-- `aria-setsize` / `aria-posinset` in BOTH shapes: the list is flat with no\n `role=\"group\"` either way, so the set a row belongs to is never expressed\n by the DOM. Gated behind the virtual flag, the same tree announced a\n position with windowing on and nothing with it off. -->\n <li\n class=\"wr-tree__spacer\"\n role=\"presentation\"\n aria-hidden=\"true\"\n [style.height.px]=\"virtualWindow().padTop\"\n ></li>\n }\n @for (entry of windowRows(); track entry.node.id) {\n <!-- Click handler lives on focusable rows; keyboard handling is owned\n by the parent <ul> via the roving-tabindex pattern. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -->\n <li\n class=\"wr-tree__row\"\n role=\"treeitem\"\n [attr.id]=\"virtualized() ? rowId(entry.index) : null\"\n [class.wr-tree__row--selected]=\"selectedSet().has(entry.node.id)\"\n [class.wr-tree__row--active]=\"virtualized() && entry.index === cursorIndex()\"\n [class.wr-tree__row--disabled]=\"entry.node.disabled\"\n [attr.aria-level]=\"entry.depth + 1\"\n [attr.aria-setsize]=\"entry.setsize\"\n [attr.aria-posinset]=\"entry.posinset\"\n [attr.aria-expanded]=\"entry.hasChildren ? expandedSet().has(entry.node.id) : null\"\n [attr.aria-selected]=\"selectionMode() === 'none' ? null : selectedSet().has(entry.node.id)\"\n [attr.aria-disabled]=\"entry.node.disabled || disabled() || null\"\n [attr.data-tree-index]=\"entry.index\"\n [attr.tabindex]=\"virtualized() ? null : entry.index === cursorIndex() ? 0 : -1\"\n [style.--wr-tree-depth]=\"entry.depth\"\n (click)=\"onRowClick(entry, $event)\"\n >\n @if (entry.hasChildren) {\n <button\n type=\"button\"\n class=\"wr-tree__toggle\"\n tabindex=\"-1\"\n [attr.aria-label]=\"expandedSet().has(entry.node.id) ? collapseLabel() : expandLabel()\"\n (click)=\"onToggleClick(entry.node, $event)\"\n >\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path d=\"M3 1 L7 5 L3 9 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n } @else {\n <span class=\"wr-tree__toggle wr-tree__toggle--leaf\" aria-hidden=\"true\"></span>\n }\n <span class=\"wr-tree__label\">{{ entry.node.label }}</span>\n </li>\n }\n @if (virtualized() && virtualWindow().padBottom > 0) {\n <li\n class=\"wr-tree__spacer\"\n role=\"presentation\"\n aria-hidden=\"true\"\n [style.height.px]=\"virtualWindow().padBottom\"\n ></li>\n }\n </ul>\n</ng-template>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], encapsulation: i0.ViewEncapsulation.None });
759
777
  }
760
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrTree, decorators: [{
778
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrTree, decorators: [{
761
779
  type: Component,
762
- args: [{ selector: 'wr-tree', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, imports: [NgTemplateOutlet], template: "@if (isOverlay()) {\n <button\n type=\"button\"\n class=\"wr-tree__trigger\"\n role=\"combobox\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [disabled]=\"disabled()\"\n [attr.aria-haspopup]=\"'tree'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"panelId\"\n (click)=\"onTriggerClick()\"\n (blur)=\"onBlur()\"\n >\n @if (isMulti()) {\n @if (hasSelection()) {\n <span class=\"wr-tree__chips\">\n @for (chip of visibleChips(); track chip.id) {\n <span class=\"wr-tree__chip\">\n <span class=\"wr-tree__chip-label\">{{ chip.label }}</span>\n <span\n class=\"wr-tree__chip-remove\"\n role=\"button\"\n [attr.aria-label]=\"chipRemoveLabel({ label: chip.label })\"\n tabindex=\"-1\"\n (click)=\"removeChip(chip.id, $event)\"\n (keydown.enter)=\"removeChip(chip.id, $event)\"\n (keydown.space)=\"removeChip(chip.id, $event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n </span>\n }\n @if (hiddenChipCount(); as extra) {\n <span class=\"wr-tree__chip wr-tree__chip--more\">+{{ extra }} more</span>\n }\n </span>\n } @else {\n <span class=\"wr-tree__placeholder\">{{ resolvedPlaceholder() }}</span>\n }\n } @else {\n @if (singleLabel(); as label) {\n <span class=\"wr-tree__value\">{{ label }}</span>\n } @else {\n <span class=\"wr-tree__placeholder\">{{ resolvedPlaceholder() }}</span>\n }\n }\n\n @if (clearable() && hasSelection() && !disabled()) {\n <span\n class=\"wr-tree__clear\"\n role=\"button\"\n [attr.aria-label]=\"clearLabel\"\n tabindex=\"-1\"\n (click)=\"clearSelection($event)\"\n (keydown.enter)=\"clearSelection($event)\"\n (keydown.space)=\"clearSelection($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n\n <svg\n class=\"wr-icon__svg wr-tree__chevron\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n\n <!-- Tree list lives inside the overlay panel. -->\n <ng-template #panelTpl>\n <div class=\"wr-tree-panel\" [class.wr-tree-panel--virtual]=\"virtualized()\" [attr.id]=\"panelId\">\n <ng-container [ngTemplateOutlet]=\"listTpl\" />\n </div>\n </ng-template>\n} @else {\n <ng-container [ngTemplateOutlet]=\"listTpl\" />\n}\n\n<!-- Shared row list \u2014 rendered inline (default) or projected into the\n overlay panel (when openOn=\"overlay\"). -->\n<ng-template #listTpl>\n <!-- Roving tabindex (non-virtual) / aria-activedescendant (virtual): the tree\n owns keyboard focus and routes arrow keys to its currently-focused row. -->\n <ul\n class=\"wr-tree__list\"\n role=\"tree\"\n [attr.aria-multiselectable]=\"isMulti() ? 'true' : null\"\n [class.wr-tree__list--virtual]=\"virtualized()\"\n [attr.tabindex]=\"virtualized() ? 0 : null\"\n [attr.aria-activedescendant]=\"activeRowId()\"\n [style.max-height]=\"virtualized() ? resolvedViewportHeight() : null\"\n [style.overflow-y]=\"virtualized() ? 'auto' : null\"\n (keydown)=\"onKeydown($event)\"\n >\n @if (virtualized() && virtualWindow().padTop > 0) {\n <!-- `aria-setsize` / `aria-posinset` in BOTH shapes: the list is flat with no\n `role=\"group\"` either way, so the set a row belongs to is never expressed\n by the DOM. Gated behind the virtual flag, the same tree announced a\n position with windowing on and nothing with it off. -->\n <li\n class=\"wr-tree__spacer\"\n role=\"presentation\"\n aria-hidden=\"true\"\n [style.height.px]=\"virtualWindow().padTop\"\n ></li>\n }\n @for (entry of windowRows(); track entry.node.id) {\n <!-- Click handler lives on focusable rows; keyboard handling is owned\n by the parent <ul> via the roving-tabindex pattern. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -->\n <li\n class=\"wr-tree__row\"\n role=\"treeitem\"\n [attr.id]=\"virtualized() ? rowId(entry.index) : null\"\n [class.wr-tree__row--selected]=\"selectedSet().has(entry.node.id)\"\n [class.wr-tree__row--active]=\"virtualized() && entry.index === cursorIndex()\"\n [class.wr-tree__row--disabled]=\"entry.node.disabled\"\n [attr.aria-level]=\"entry.depth + 1\"\n [attr.aria-setsize]=\"entry.setsize\"\n [attr.aria-posinset]=\"entry.posinset\"\n [attr.aria-expanded]=\"entry.hasChildren ? expandedSet().has(entry.node.id) : null\"\n [attr.aria-selected]=\"selectionMode() === 'none' ? null : selectedSet().has(entry.node.id)\"\n [attr.aria-disabled]=\"entry.node.disabled || disabled() || null\"\n [attr.data-tree-index]=\"entry.index\"\n [attr.tabindex]=\"virtualized() ? null : entry.index === cursorIndex() ? 0 : -1\"\n [style.--wr-tree-depth]=\"entry.depth\"\n (click)=\"onRowClick(entry, $event)\"\n >\n @if (entry.hasChildren) {\n <button\n type=\"button\"\n class=\"wr-tree__toggle\"\n tabindex=\"-1\"\n [attr.aria-label]=\"expandedSet().has(entry.node.id) ? collapseLabel : expandLabel\"\n (click)=\"onToggleClick(entry.node, $event)\"\n >\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path d=\"M3 1 L7 5 L3 9 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n } @else {\n <span class=\"wr-tree__toggle wr-tree__toggle--leaf\" aria-hidden=\"true\"></span>\n }\n <span class=\"wr-tree__label\">{{ entry.node.label }}</span>\n </li>\n }\n @if (virtualized() && virtualWindow().padBottom > 0) {\n <li\n class=\"wr-tree__spacer\"\n role=\"presentation\"\n aria-hidden=\"true\"\n [style.height.px]=\"virtualWindow().padBottom\"\n ></li>\n }\n </ul>\n</ng-template>\n" }]
780
+ args: [{ selector: 'wr-tree', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, imports: [NgTemplateOutlet], template: "@if (isOverlay()) {\n <button\n type=\"button\"\n class=\"wr-tree__trigger\"\n role=\"combobox\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [disabled]=\"disabled()\"\n [attr.aria-haspopup]=\"'tree'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"panelId\"\n (click)=\"onTriggerClick()\"\n (blur)=\"onBlur()\"\n >\n @if (isMulti()) {\n @if (hasSelection()) {\n <span class=\"wr-tree__chips\">\n @for (chip of visibleChips(); track chip.id) {\n <span class=\"wr-tree__chip\">\n <span class=\"wr-tree__chip-label\">{{ chip.label }}</span>\n <span\n class=\"wr-tree__chip-remove\"\n role=\"button\"\n [attr.aria-label]=\"chipRemoveLabel({ label: chip.label })\"\n tabindex=\"-1\"\n (click)=\"removeChip(chip.id, $event)\"\n (keydown.enter)=\"removeChip(chip.id, $event)\"\n (keydown.space)=\"removeChip(chip.id, $event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n </span>\n }\n @if (hiddenChipCount(); as extra) {\n <span class=\"wr-tree__chip wr-tree__chip--more\">+{{ extra }} more</span>\n }\n </span>\n } @else {\n <span class=\"wr-tree__placeholder\">{{ resolvedPlaceholder() }}</span>\n }\n } @else {\n @if (singleLabel(); as label) {\n <span class=\"wr-tree__value\">{{ label }}</span>\n } @else {\n <span class=\"wr-tree__placeholder\">{{ resolvedPlaceholder() }}</span>\n }\n }\n\n @if (clearable() && hasSelection() && !disabled()) {\n <span\n class=\"wr-tree__clear\"\n role=\"button\"\n [attr.aria-label]=\"clearLabel()\"\n tabindex=\"-1\"\n (click)=\"clearSelection($event)\"\n (keydown.enter)=\"clearSelection($event)\"\n (keydown.space)=\"clearSelection($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n\n <svg\n class=\"wr-icon__svg wr-tree__chevron\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n\n <!-- Tree list lives inside the overlay panel. -->\n <ng-template #panelTpl>\n <div class=\"wr-tree-panel\" [class.wr-tree-panel--virtual]=\"virtualized()\" [attr.id]=\"panelId\">\n <ng-container [ngTemplateOutlet]=\"listTpl\" />\n </div>\n </ng-template>\n} @else {\n <ng-container [ngTemplateOutlet]=\"listTpl\" />\n}\n\n<!-- Shared row list \u2014 rendered inline (default) or projected into the\n overlay panel (when openOn=\"overlay\"). -->\n<ng-template #listTpl>\n <!-- Roving tabindex (non-virtual) / aria-activedescendant (virtual): the tree\n owns keyboard focus and routes arrow keys to its currently-focused row. -->\n <ul\n class=\"wr-tree__list\"\n role=\"tree\"\n [attr.aria-multiselectable]=\"isMulti() ? 'true' : null\"\n [class.wr-tree__list--virtual]=\"virtualized()\"\n [attr.tabindex]=\"virtualized() ? 0 : null\"\n [attr.aria-activedescendant]=\"activeRowId()\"\n [style.max-height]=\"virtualized() ? resolvedViewportHeight() : null\"\n [style.overflow-y]=\"virtualized() ? 'auto' : null\"\n (keydown)=\"onKeydown($event)\"\n >\n @if (virtualized() && virtualWindow().padTop > 0) {\n <!-- `aria-setsize` / `aria-posinset` in BOTH shapes: the list is flat with no\n `role=\"group\"` either way, so the set a row belongs to is never expressed\n by the DOM. Gated behind the virtual flag, the same tree announced a\n position with windowing on and nothing with it off. -->\n <li\n class=\"wr-tree__spacer\"\n role=\"presentation\"\n aria-hidden=\"true\"\n [style.height.px]=\"virtualWindow().padTop\"\n ></li>\n }\n @for (entry of windowRows(); track entry.node.id) {\n <!-- Click handler lives on focusable rows; keyboard handling is owned\n by the parent <ul> via the roving-tabindex pattern. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -->\n <li\n class=\"wr-tree__row\"\n role=\"treeitem\"\n [attr.id]=\"virtualized() ? rowId(entry.index) : null\"\n [class.wr-tree__row--selected]=\"selectedSet().has(entry.node.id)\"\n [class.wr-tree__row--active]=\"virtualized() && entry.index === cursorIndex()\"\n [class.wr-tree__row--disabled]=\"entry.node.disabled\"\n [attr.aria-level]=\"entry.depth + 1\"\n [attr.aria-setsize]=\"entry.setsize\"\n [attr.aria-posinset]=\"entry.posinset\"\n [attr.aria-expanded]=\"entry.hasChildren ? expandedSet().has(entry.node.id) : null\"\n [attr.aria-selected]=\"selectionMode() === 'none' ? null : selectedSet().has(entry.node.id)\"\n [attr.aria-disabled]=\"entry.node.disabled || disabled() || null\"\n [attr.data-tree-index]=\"entry.index\"\n [attr.tabindex]=\"virtualized() ? null : entry.index === cursorIndex() ? 0 : -1\"\n [style.--wr-tree-depth]=\"entry.depth\"\n (click)=\"onRowClick(entry, $event)\"\n >\n @if (entry.hasChildren) {\n <button\n type=\"button\"\n class=\"wr-tree__toggle\"\n tabindex=\"-1\"\n [attr.aria-label]=\"expandedSet().has(entry.node.id) ? collapseLabel() : expandLabel()\"\n (click)=\"onToggleClick(entry.node, $event)\"\n >\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path d=\"M3 1 L7 5 L3 9 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n } @else {\n <span class=\"wr-tree__toggle wr-tree__toggle--leaf\" aria-hidden=\"true\"></span>\n }\n <span class=\"wr-tree__label\">{{ entry.node.label }}</span>\n </li>\n }\n @if (virtualized() && virtualWindow().padBottom > 0) {\n <li\n class=\"wr-tree__spacer\"\n role=\"presentation\"\n aria-hidden=\"true\"\n [style.height.px]=\"virtualWindow().padBottom\"\n ></li>\n }\n </ul>\n</ng-template>\n" }]
763
781
  }], ctorParameters: () => [], propDecorators: { nodes: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodes", required: false }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }, { type: i0.Output, args: ["selectedChange"] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }, { type: i0.Output, args: ["expandedChange"] }], selectionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionMode", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], openOn: [{ type: i0.Input, args: [{ isSignal: true, alias: "openOn", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], maxTagCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxTagCount", required: false }] }], defaultExpandAll: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultExpandAll", required: false }] }], virtualScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualScroll", required: false }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], viewportHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewportHeight", required: false }] }], overscan: [{ type: i0.Input, args: [{ isSignal: true, alias: "overscan", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touch: [{ type: i0.Output, args: ["touch"] }], panelTpl: [{ type: i0.ViewChild, args: ['panelTpl', { ...{ read: TemplateRef }, isSignal: true }] }] } });
764
782
 
765
783
  /**