ngwr 11.1.0 → 12.0.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 (481) hide show
  1. package/README.md +7 -7
  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/circular-text/styles/_index.scss +11 -0
  10. package/collapse/styles/_index.scss +2 -2
  11. package/color-picker/styles/_index.scss +3 -3
  12. package/command-palette/styles/_index.scss +12 -0
  13. package/compare/styles/_index.scss +18 -2
  14. package/context-menu/styles/_index.scss +21 -2
  15. package/date-picker/styles/_index.scss +5 -5
  16. package/descriptions/styles/_index.scss +1 -1
  17. package/drag-drop/styles/_index.scss +28 -1
  18. package/drawer/styles/_index.scss +37 -0
  19. package/dropdown/styles/_index.scss +2 -2
  20. package/event-calendar/styles/_index.scss +67 -10
  21. package/fesm2022/ngwr-action-sheet-testing.mjs +313 -0
  22. package/fesm2022/ngwr-action-sheet-testing.mjs.map +1 -0
  23. package/fesm2022/ngwr-action-sheet.mjs +3 -3
  24. package/fesm2022/ngwr-affix.mjs +3 -3
  25. package/fesm2022/ngwr-alert.mjs +3 -3
  26. package/fesm2022/ngwr-anchor.mjs +3 -3
  27. package/fesm2022/ngwr-aurora.mjs +3 -3
  28. package/fesm2022/ngwr-avatar.mjs +3 -3
  29. package/fesm2022/ngwr-back-top.mjs +3 -3
  30. package/fesm2022/ngwr-badge.mjs +6 -6
  31. package/fesm2022/ngwr-bar-chart-testing.mjs +107 -0
  32. package/fesm2022/ngwr-bar-chart-testing.mjs.map +1 -0
  33. package/fesm2022/ngwr-bar-chart.mjs +3 -3
  34. package/fesm2022/ngwr-blur-text-testing.mjs +203 -0
  35. package/fesm2022/ngwr-blur-text-testing.mjs.map +1 -0
  36. package/fesm2022/ngwr-blur-text.mjs +18 -4
  37. package/fesm2022/ngwr-blur-text.mjs.map +1 -1
  38. package/fesm2022/ngwr-border-glow-testing.mjs +325 -0
  39. package/fesm2022/ngwr-border-glow-testing.mjs.map +1 -0
  40. package/fesm2022/ngwr-border-glow.mjs +4 -4
  41. package/fesm2022/ngwr-border-glow.mjs.map +1 -1
  42. package/fesm2022/ngwr-breadcrumbs.mjs +29 -17
  43. package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
  44. package/fesm2022/ngwr-burger.mjs +3 -3
  45. package/fesm2022/ngwr-button.mjs +6 -6
  46. package/fesm2022/ngwr-calendar-heatmap-testing.mjs +95 -0
  47. package/fesm2022/ngwr-calendar-heatmap-testing.mjs.map +1 -0
  48. package/fesm2022/ngwr-calendar-heatmap.mjs +17 -5
  49. package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
  50. package/fesm2022/ngwr-calendar-testing.mjs +368 -0
  51. package/fesm2022/ngwr-calendar-testing.mjs.map +1 -0
  52. package/fesm2022/ngwr-calendar.mjs +228 -10
  53. package/fesm2022/ngwr-calendar.mjs.map +1 -1
  54. package/fesm2022/ngwr-card.mjs +10 -10
  55. package/fesm2022/ngwr-card.mjs.map +1 -1
  56. package/fesm2022/ngwr-carousel.mjs +124 -24
  57. package/fesm2022/ngwr-carousel.mjs.map +1 -1
  58. package/fesm2022/ngwr-cascader.mjs +60 -5
  59. package/fesm2022/ngwr-cascader.mjs.map +1 -1
  60. package/fesm2022/ngwr-checkbox.mjs +14 -6
  61. package/fesm2022/ngwr-checkbox.mjs.map +1 -1
  62. package/fesm2022/ngwr-circular-text-testing.mjs +230 -0
  63. package/fesm2022/ngwr-circular-text-testing.mjs.map +1 -0
  64. package/fesm2022/ngwr-circular-text.mjs +4 -4
  65. package/fesm2022/ngwr-circular-text.mjs.map +1 -1
  66. package/fesm2022/ngwr-click-spark.mjs +27 -7
  67. package/fesm2022/ngwr-click-spark.mjs.map +1 -1
  68. package/fesm2022/ngwr-clipboard.mjs +3 -3
  69. package/fesm2022/ngwr-collapse.mjs +9 -9
  70. package/fesm2022/ngwr-color-picker-testing.mjs +452 -0
  71. package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -0
  72. package/fesm2022/ngwr-color-picker.mjs +126 -10
  73. package/fesm2022/ngwr-color-picker.mjs.map +1 -1
  74. package/fesm2022/ngwr-command-palette.mjs +14 -5
  75. package/fesm2022/ngwr-command-palette.mjs.map +1 -1
  76. package/fesm2022/ngwr-compare.mjs +3 -3
  77. package/fesm2022/ngwr-confetti.mjs +31 -6
  78. package/fesm2022/ngwr-confetti.mjs.map +1 -1
  79. package/fesm2022/ngwr-config.mjs +9 -0
  80. package/fesm2022/ngwr-config.mjs.map +1 -1
  81. package/fesm2022/ngwr-context-menu-testing.mjs +30 -11
  82. package/fesm2022/ngwr-context-menu-testing.mjs.map +1 -1
  83. package/fesm2022/ngwr-context-menu.mjs +358 -48
  84. package/fesm2022/ngwr-context-menu.mjs.map +1 -1
  85. package/fesm2022/ngwr-cookie.mjs +3 -3
  86. package/fesm2022/ngwr-counter.mjs +25 -9
  87. package/fesm2022/ngwr-counter.mjs.map +1 -1
  88. package/fesm2022/{ngwr-date-adapter-fns.mjs → ngwr-date-adapters-fns.mjs} +37 -10
  89. package/fesm2022/ngwr-date-adapters-fns.mjs.map +1 -0
  90. package/fesm2022/{ngwr-date-adapter-luxon.mjs → ngwr-date-adapters-luxon.mjs} +5 -5
  91. package/fesm2022/ngwr-date-adapters-luxon.mjs.map +1 -0
  92. package/fesm2022/ngwr-date-picker-testing.mjs +21 -85
  93. package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
  94. package/fesm2022/ngwr-date-picker.mjs +119 -37
  95. package/fesm2022/ngwr-date-picker.mjs.map +1 -1
  96. package/fesm2022/{ngwr-date-adapter.mjs → ngwr-date.mjs} +36 -10
  97. package/fesm2022/ngwr-date.mjs.map +1 -0
  98. package/fesm2022/ngwr-decrypt-text-testing.mjs +261 -0
  99. package/fesm2022/ngwr-decrypt-text-testing.mjs.map +1 -0
  100. package/fesm2022/ngwr-decrypt-text.mjs +9 -4
  101. package/fesm2022/ngwr-decrypt-text.mjs.map +1 -1
  102. package/fesm2022/ngwr-density.mjs +6 -6
  103. package/fesm2022/ngwr-descriptions.mjs +6 -6
  104. package/fesm2022/ngwr-dialog.mjs +73 -18
  105. package/fesm2022/ngwr-dialog.mjs.map +1 -1
  106. package/fesm2022/ngwr-directives.mjs +12 -12
  107. package/fesm2022/ngwr-divider.mjs +52 -5
  108. package/fesm2022/ngwr-divider.mjs.map +1 -1
  109. package/fesm2022/ngwr-donut-chart-testing.mjs +97 -0
  110. package/fesm2022/ngwr-donut-chart-testing.mjs.map +1 -0
  111. package/fesm2022/ngwr-donut-chart.mjs +7 -7
  112. package/fesm2022/ngwr-donut-chart.mjs.map +1 -1
  113. package/fesm2022/ngwr-drag-drop.mjs +160 -11
  114. package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
  115. package/fesm2022/ngwr-drawer.mjs +156 -64
  116. package/fesm2022/ngwr-drawer.mjs.map +1 -1
  117. package/fesm2022/ngwr-dropdown.mjs +58 -12
  118. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  119. package/fesm2022/ngwr-empty.mjs +3 -3
  120. package/fesm2022/ngwr-event-calendar-testing.mjs +446 -0
  121. package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -0
  122. package/fesm2022/ngwr-event-calendar.mjs +66 -28
  123. package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
  124. package/fesm2022/ngwr-falling-text-testing.mjs +220 -0
  125. package/fesm2022/ngwr-falling-text-testing.mjs.map +1 -0
  126. package/fesm2022/ngwr-falling-text.mjs +75 -46
  127. package/fesm2022/ngwr-falling-text.mjs.map +1 -1
  128. package/fesm2022/ngwr-file-upload.mjs +3 -3
  129. package/fesm2022/ngwr-form.mjs +32 -15
  130. package/fesm2022/ngwr-form.mjs.map +1 -1
  131. package/fesm2022/ngwr-fuzzy-text-testing.mjs +134 -0
  132. package/fesm2022/ngwr-fuzzy-text-testing.mjs.map +1 -0
  133. package/fesm2022/ngwr-fuzzy-text.mjs +21 -11
  134. package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
  135. package/fesm2022/ngwr-gauge-testing.mjs +86 -0
  136. package/fesm2022/ngwr-gauge-testing.mjs.map +1 -0
  137. package/fesm2022/ngwr-gauge.mjs +3 -3
  138. package/fesm2022/ngwr-glitch-text-testing.mjs +215 -0
  139. package/fesm2022/ngwr-glitch-text-testing.mjs.map +1 -0
  140. package/fesm2022/ngwr-glitch-text.mjs +7 -7
  141. package/fesm2022/ngwr-glitch-text.mjs.map +1 -1
  142. package/fesm2022/ngwr-gradient-text-testing.mjs +258 -0
  143. package/fesm2022/ngwr-gradient-text-testing.mjs.map +1 -0
  144. package/fesm2022/ngwr-gradient-text.mjs +4 -4
  145. package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
  146. package/fesm2022/ngwr-hotkey.mjs +24 -9
  147. package/fesm2022/ngwr-hotkey.mjs.map +1 -1
  148. package/fesm2022/ngwr-i18n-en.mjs +87 -1
  149. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  150. package/fesm2022/ngwr-i18n-ru.mjs +69 -1
  151. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  152. package/fesm2022/ngwr-i18n.mjs +26 -15
  153. package/fesm2022/ngwr-i18n.mjs.map +1 -1
  154. package/fesm2022/ngwr-icon.mjs +6 -6
  155. package/fesm2022/ngwr-image-cropper-testing.mjs +200 -0
  156. package/fesm2022/ngwr-image-cropper-testing.mjs.map +1 -0
  157. package/fesm2022/ngwr-image-cropper.mjs +156 -11
  158. package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
  159. package/fesm2022/ngwr-input-number-testing.mjs +15 -10
  160. package/fesm2022/ngwr-input-number-testing.mjs.map +1 -1
  161. package/fesm2022/ngwr-input-number.mjs +89 -15
  162. package/fesm2022/ngwr-input-number.mjs.map +1 -1
  163. package/fesm2022/ngwr-input-otp-testing.mjs +12 -6
  164. package/fesm2022/ngwr-input-otp-testing.mjs.map +1 -1
  165. package/fesm2022/ngwr-input-otp.mjs +24 -5
  166. package/fesm2022/ngwr-input-otp.mjs.map +1 -1
  167. package/fesm2022/ngwr-input.mjs +15 -15
  168. package/fesm2022/ngwr-keyboard.mjs +3 -3
  169. package/fesm2022/ngwr-knob-testing.mjs +220 -0
  170. package/fesm2022/ngwr-knob-testing.mjs.map +1 -0
  171. package/fesm2022/ngwr-knob.mjs +3 -3
  172. package/fesm2022/ngwr-layout.mjs +15 -15
  173. package/fesm2022/ngwr-lightbox-testing.mjs +223 -0
  174. package/fesm2022/ngwr-lightbox-testing.mjs.map +1 -0
  175. package/fesm2022/ngwr-lightbox.mjs +11 -4
  176. package/fesm2022/ngwr-lightbox.mjs.map +1 -1
  177. package/fesm2022/ngwr-line-chart-testing.mjs +119 -0
  178. package/fesm2022/ngwr-line-chart-testing.mjs.map +1 -0
  179. package/fesm2022/ngwr-line-chart.mjs +73 -26
  180. package/fesm2022/ngwr-line-chart.mjs.map +1 -1
  181. package/fesm2022/ngwr-list.mjs +7 -7
  182. package/fesm2022/ngwr-list.mjs.map +1 -1
  183. package/fesm2022/ngwr-loading-bar.mjs +7 -7
  184. package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
  185. package/fesm2022/ngwr-markdown.mjs +66 -8
  186. package/fesm2022/ngwr-markdown.mjs.map +1 -1
  187. package/fesm2022/ngwr-marquee-testing.mjs +336 -0
  188. package/fesm2022/ngwr-marquee-testing.mjs.map +1 -0
  189. package/fesm2022/ngwr-marquee.mjs +4 -4
  190. package/fesm2022/ngwr-marquee.mjs.map +1 -1
  191. package/fesm2022/ngwr-media.mjs +3 -3
  192. package/fesm2022/ngwr-mention.mjs +108 -13
  193. package/fesm2022/ngwr-mention.mjs.map +1 -1
  194. package/fesm2022/ngwr-meta.mjs +6 -6
  195. package/fesm2022/ngwr-meter-group-testing.mjs +100 -0
  196. package/fesm2022/ngwr-meter-group-testing.mjs.map +1 -0
  197. package/fesm2022/ngwr-meter-group.mjs +3 -3
  198. package/fesm2022/ngwr-overlay.mjs +34 -7
  199. package/fesm2022/ngwr-overlay.mjs.map +1 -1
  200. package/fesm2022/ngwr-page-header.mjs +3 -3
  201. package/fesm2022/ngwr-pagination-testing.mjs +6 -6
  202. package/fesm2022/ngwr-pagination-testing.mjs.map +1 -1
  203. package/fesm2022/ngwr-pagination.mjs +17 -13
  204. package/fesm2022/ngwr-pagination.mjs.map +1 -1
  205. package/fesm2022/ngwr-pipes.mjs +39 -26
  206. package/fesm2022/ngwr-pipes.mjs.map +1 -1
  207. package/fesm2022/ngwr-platform.mjs +9 -9
  208. package/fesm2022/ngwr-popconfirm.mjs +6 -6
  209. package/fesm2022/ngwr-popover-testing.mjs +7 -2
  210. package/fesm2022/ngwr-popover-testing.mjs.map +1 -1
  211. package/fesm2022/ngwr-popover.mjs +169 -33
  212. package/fesm2022/ngwr-popover.mjs.map +1 -1
  213. package/fesm2022/ngwr-progress.mjs +3 -3
  214. package/fesm2022/ngwr-pull-to-refresh.mjs +3 -3
  215. package/fesm2022/ngwr-qr.mjs +42 -5
  216. package/fesm2022/ngwr-qr.mjs.map +1 -1
  217. package/fesm2022/ngwr-radio.mjs +6 -6
  218. package/fesm2022/ngwr-rating.mjs +3 -3
  219. package/fesm2022/ngwr-result.mjs +21 -30
  220. package/fesm2022/ngwr-result.mjs.map +1 -1
  221. package/fesm2022/ngwr-rotating-text-testing.mjs +220 -0
  222. package/fesm2022/ngwr-rotating-text-testing.mjs.map +1 -0
  223. package/fesm2022/ngwr-rotating-text.mjs +4 -4
  224. package/fesm2022/ngwr-rotating-text.mjs.map +1 -1
  225. package/fesm2022/ngwr-scroll.mjs +3 -3
  226. package/fesm2022/ngwr-segmented-testing.mjs +14 -1
  227. package/fesm2022/ngwr-segmented-testing.mjs.map +1 -1
  228. package/fesm2022/ngwr-segmented.mjs +29 -6
  229. package/fesm2022/ngwr-segmented.mjs.map +1 -1
  230. package/fesm2022/ngwr-select.mjs +227 -43
  231. package/fesm2022/ngwr-select.mjs.map +1 -1
  232. package/fesm2022/ngwr-shiny-text-testing.mjs +237 -0
  233. package/fesm2022/ngwr-shiny-text-testing.mjs.map +1 -0
  234. package/fesm2022/ngwr-shiny-text.mjs +11 -9
  235. package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
  236. package/fesm2022/ngwr-sidebar.mjs +31 -16
  237. package/fesm2022/ngwr-sidebar.mjs.map +1 -1
  238. package/fesm2022/ngwr-skeleton.mjs +3 -3
  239. package/fesm2022/ngwr-slider.mjs +143 -14
  240. package/fesm2022/ngwr-slider.mjs.map +1 -1
  241. package/fesm2022/ngwr-sparkline-testing.mjs +76 -0
  242. package/fesm2022/ngwr-sparkline-testing.mjs.map +1 -0
  243. package/fesm2022/ngwr-sparkline.mjs +3 -3
  244. package/fesm2022/ngwr-speed-dial-testing.mjs +281 -0
  245. package/fesm2022/ngwr-speed-dial-testing.mjs.map +1 -0
  246. package/fesm2022/ngwr-speed-dial.mjs +27 -12
  247. package/fesm2022/ngwr-speed-dial.mjs.map +1 -1
  248. package/fesm2022/ngwr-spinner.mjs +3 -3
  249. package/fesm2022/ngwr-splash-cursor-testing.mjs +89 -0
  250. package/fesm2022/ngwr-splash-cursor-testing.mjs.map +1 -0
  251. package/fesm2022/ngwr-splash-cursor.mjs +3 -3
  252. package/fesm2022/ngwr-split-text-testing.mjs +224 -0
  253. package/fesm2022/ngwr-split-text-testing.mjs.map +1 -0
  254. package/fesm2022/ngwr-split-text.mjs +31 -5
  255. package/fesm2022/ngwr-split-text.mjs.map +1 -1
  256. package/fesm2022/ngwr-splitter-testing.mjs +245 -0
  257. package/fesm2022/ngwr-splitter-testing.mjs.map +1 -0
  258. package/fesm2022/ngwr-splitter.mjs +3 -3
  259. package/fesm2022/ngwr-spotlight-card-testing.mjs +368 -0
  260. package/fesm2022/ngwr-spotlight-card-testing.mjs.map +1 -0
  261. package/fesm2022/ngwr-spotlight-card.mjs +7 -7
  262. package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
  263. package/fesm2022/ngwr-squircle.mjs +6 -6
  264. package/fesm2022/ngwr-star-border-testing.mjs +172 -0
  265. package/fesm2022/ngwr-star-border-testing.mjs.map +1 -0
  266. package/fesm2022/ngwr-star-border.mjs +4 -4
  267. package/fesm2022/ngwr-star-border.mjs.map +1 -1
  268. package/fesm2022/ngwr-statistic.mjs +9 -9
  269. package/fesm2022/ngwr-stepper-testing.mjs +8 -8
  270. package/fesm2022/ngwr-stepper-testing.mjs.map +1 -1
  271. package/fesm2022/ngwr-stepper.mjs +33 -8
  272. package/fesm2022/ngwr-stepper.mjs.map +1 -1
  273. package/fesm2022/ngwr-storage.mjs +17 -5
  274. package/fesm2022/ngwr-storage.mjs.map +1 -1
  275. package/fesm2022/ngwr-switch.mjs +3 -3
  276. package/fesm2022/ngwr-table.mjs +110 -40
  277. package/fesm2022/ngwr-table.mjs.map +1 -1
  278. package/fesm2022/ngwr-tabs-testing.mjs +11 -10
  279. package/fesm2022/ngwr-tabs-testing.mjs.map +1 -1
  280. package/fesm2022/ngwr-tabs.mjs +51 -11
  281. package/fesm2022/ngwr-tabs.mjs.map +1 -1
  282. package/fesm2022/ngwr-textarea.mjs +3 -3
  283. package/fesm2022/ngwr-theme.mjs +181 -4
  284. package/fesm2022/ngwr-theme.mjs.map +1 -1
  285. package/fesm2022/ngwr-tilt-card-testing.mjs +190 -0
  286. package/fesm2022/ngwr-tilt-card-testing.mjs.map +1 -0
  287. package/fesm2022/ngwr-tilt-card.mjs +26 -8
  288. package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
  289. package/fesm2022/ngwr-timeline.mjs +6 -6
  290. package/fesm2022/ngwr-toast.mjs +44 -15
  291. package/fesm2022/ngwr-toast.mjs.map +1 -1
  292. package/fesm2022/ngwr-toolbar.mjs +3 -3
  293. package/fesm2022/ngwr-tour-testing.mjs +173 -0
  294. package/fesm2022/ngwr-tour-testing.mjs.map +1 -0
  295. package/fesm2022/ngwr-tour.mjs +34 -10
  296. package/fesm2022/ngwr-tour.mjs.map +1 -1
  297. package/fesm2022/ngwr-transfer.mjs +3 -3
  298. package/fesm2022/ngwr-tree.mjs +36 -11
  299. package/fesm2022/ngwr-tree.mjs.map +1 -1
  300. package/fesm2022/ngwr-typewriter-testing.mjs +242 -0
  301. package/fesm2022/ngwr-typewriter-testing.mjs.map +1 -0
  302. package/fesm2022/ngwr-typewriter.mjs +13 -5
  303. package/fesm2022/ngwr-typewriter.mjs.map +1 -1
  304. package/fesm2022/ngwr-typography.mjs +3 -3
  305. package/fesm2022/ngwr-utils.mjs +5 -2
  306. package/fesm2022/ngwr-utils.mjs.map +1 -1
  307. package/fesm2022/ngwr-version.mjs +1 -1
  308. package/fesm2022/ngwr-version.mjs.map +1 -1
  309. package/fesm2022/ngwr-virtual-scroll.mjs +4 -4
  310. package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -1
  311. package/fesm2022/ngwr-waves-testing.mjs +128 -0
  312. package/fesm2022/ngwr-waves-testing.mjs.map +1 -0
  313. package/fesm2022/ngwr-waves.mjs +4 -4
  314. package/fesm2022/ngwr-waves.mjs.map +1 -1
  315. package/fesm2022/ngwr-window-testing.mjs +361 -0
  316. package/fesm2022/ngwr-window-testing.mjs.map +1 -0
  317. package/fesm2022/ngwr-window.mjs +175 -31
  318. package/fesm2022/ngwr-window.mjs.map +1 -1
  319. package/file-upload/styles/_index.scss +2 -2
  320. package/glitch-text/styles/_index.scss +27 -12
  321. package/i18n/en.json +71 -7
  322. package/i18n/ru.json +71 -7
  323. package/image-cropper/styles/_index.scss +19 -0
  324. package/input/styles/_index.scss +2 -2
  325. package/input-number/styles/_index.scss +2 -2
  326. package/input-otp/styles/_index.scss +9 -2
  327. package/knob/styles/_index.scss +1 -1
  328. package/lightbox/styles/_index.scss +16 -2
  329. package/line-chart/styles/_index.scss +28 -14
  330. package/list/styles/_index.scss +1 -1
  331. package/llms-full.txt +161 -19
  332. package/llms.txt +45 -10
  333. package/markdown/styles/_index.scss +3 -3
  334. package/mcp/api.js +6 -4
  335. package/mcp/catalog.js +40 -3
  336. package/mcp/providers.js +42 -0
  337. package/mcp/tools.js +22 -30
  338. package/meter-group/styles/_index.scss +1 -1
  339. package/overlay/styles/_index.scss +33 -1
  340. package/package.json +161 -12
  341. package/pagination/styles/_index.scss +23 -1
  342. package/popover/styles/_index.scss +16 -7
  343. package/progress/styles/_index.scss +1 -1
  344. package/radio/styles/_index.scss +2 -2
  345. package/rating/styles/_index.scss +1 -1
  346. package/schematics/collection.json +1 -1
  347. package/schematics/migrations/v12/index.js +65 -0
  348. package/schematics/migrations/v7/index.js +12 -1
  349. package/schematics/migrations/v8/index.js +10 -1
  350. package/schematics/migrations.json +5 -0
  351. package/schematics/ng-add/index.js +8 -3
  352. package/schematics/provider/index.js +80 -13
  353. package/schematics/use/index.js +9 -129
  354. package/schematics/use/rule.js +206 -0
  355. package/schematics/use/symbol-map.json +4 -4
  356. package/segmented/styles/_index.scss +14 -3
  357. package/select/styles/_index.scss +14 -2
  358. package/sidebar/styles/_index.scss +12 -10
  359. package/skills/ngwr/SKILL.md +117 -0
  360. package/skills/ngwr/references/catalog.md +212 -0
  361. package/skills/ngwr/references/setup.md +78 -0
  362. package/slider/styles/_index.scss +1 -1
  363. package/speed-dial/styles/_index.scss +1 -1
  364. package/stepper/styles/_index.scss +12 -3
  365. package/switch/styles/_index.scss +2 -2
  366. package/table/styles/_index.scss +61 -12
  367. package/tabs/styles/_index.scss +1 -1
  368. package/textarea/styles/_index.scss +1 -1
  369. package/theme/styles/_colors.scss +142 -56
  370. package/theme/styles/_dark.scss +46 -3
  371. package/theme/styles/_focus.scss +28 -5
  372. package/theme/styles/_mixins.scss +1 -1
  373. package/theme/styles/_variables.scss +67 -0
  374. package/transfer/styles/_index.scss +26 -3
  375. package/tree/styles/_index.scss +29 -6
  376. package/types/ngwr-action-sheet-testing.d.ts +260 -0
  377. package/types/ngwr-bar-chart-testing.d.ts +85 -0
  378. package/types/ngwr-blur-text-testing.d.ts +180 -0
  379. package/types/ngwr-blur-text.d.ts +20 -4
  380. package/types/ngwr-border-glow-testing.d.ts +219 -0
  381. package/types/ngwr-breadcrumbs.d.ts +17 -10
  382. package/types/ngwr-calendar-heatmap-testing.d.ts +79 -0
  383. package/types/ngwr-calendar-testing.d.ts +277 -0
  384. package/types/ngwr-calendar.d.ts +76 -0
  385. package/types/ngwr-carousel.d.ts +85 -6
  386. package/types/ngwr-cascader.d.ts +17 -0
  387. package/types/ngwr-checkbox.d.ts +13 -0
  388. package/types/ngwr-circular-text-testing.d.ts +196 -0
  389. package/types/ngwr-click-spark.d.ts +4 -0
  390. package/types/ngwr-color-picker-testing.d.ts +299 -0
  391. package/types/ngwr-color-picker.d.ts +34 -2
  392. package/types/ngwr-confetti.d.ts +8 -0
  393. package/types/ngwr-config.d.ts +10 -1
  394. package/types/ngwr-context-menu-testing.d.ts +24 -11
  395. package/types/ngwr-context-menu.d.ts +71 -1
  396. package/types/ngwr-counter.d.ts +1 -0
  397. package/types/{ngwr-date-adapter-fns.d.ts → ngwr-date-adapters-fns.d.ts} +33 -6
  398. package/types/{ngwr-date-adapter-luxon.d.ts → ngwr-date-adapters-luxon.d.ts} +1 -1
  399. package/types/ngwr-date-picker-testing.d.ts +17 -72
  400. package/types/ngwr-date-picker.d.ts +8 -4
  401. package/types/{ngwr-date-adapter.d.ts → ngwr-date.d.ts} +24 -5
  402. package/types/ngwr-decrypt-text-testing.d.ts +222 -0
  403. package/types/ngwr-dialog.d.ts +38 -1
  404. package/types/ngwr-divider.d.ts +25 -0
  405. package/types/ngwr-donut-chart-testing.d.ts +83 -0
  406. package/types/ngwr-donut-chart.d.ts +3 -3
  407. package/types/ngwr-drag-drop.d.ts +56 -2
  408. package/types/ngwr-drawer.d.ts +68 -5
  409. package/types/ngwr-dropdown.d.ts +9 -0
  410. package/types/ngwr-event-calendar-testing.d.ts +301 -0
  411. package/types/ngwr-event-calendar.d.ts +17 -8
  412. package/types/ngwr-falling-text-testing.d.ts +188 -0
  413. package/types/ngwr-falling-text.d.ts +1 -1
  414. package/types/ngwr-form.d.ts +12 -2
  415. package/types/ngwr-fuzzy-text-testing.d.ts +130 -0
  416. package/types/ngwr-gauge-testing.d.ts +71 -0
  417. package/types/ngwr-glitch-text-testing.d.ts +201 -0
  418. package/types/ngwr-gradient-text-testing.d.ts +186 -0
  419. package/types/ngwr-i18n.d.ts +13 -4
  420. package/types/ngwr-image-cropper-testing.d.ts +173 -0
  421. package/types/ngwr-image-cropper.d.ts +71 -5
  422. package/types/ngwr-input-number-testing.d.ts +15 -10
  423. package/types/ngwr-input-number.d.ts +44 -5
  424. package/types/ngwr-input-otp-testing.d.ts +12 -6
  425. package/types/ngwr-input-otp.d.ts +15 -1
  426. package/types/ngwr-knob-testing.d.ts +150 -0
  427. package/types/ngwr-lightbox-testing.d.ts +154 -0
  428. package/types/ngwr-lightbox.d.ts +6 -0
  429. package/types/ngwr-line-chart-testing.d.ts +93 -0
  430. package/types/ngwr-line-chart.d.ts +20 -3
  431. package/types/ngwr-markdown.d.ts +11 -0
  432. package/types/ngwr-marquee-testing.d.ts +280 -0
  433. package/types/ngwr-mention.d.ts +32 -0
  434. package/types/ngwr-meter-group-testing.d.ts +77 -0
  435. package/types/ngwr-overlay.d.ts +20 -1
  436. package/types/ngwr-pagination-testing.d.ts +4 -4
  437. package/types/ngwr-pipes.d.ts +1 -1
  438. package/types/ngwr-popover-testing.d.ts +7 -2
  439. package/types/ngwr-popover.d.ts +1 -0
  440. package/types/ngwr-result.d.ts +6 -0
  441. package/types/ngwr-rotating-text-testing.d.ts +191 -0
  442. package/types/ngwr-segmented-testing.d.ts +9 -0
  443. package/types/ngwr-segmented.d.ts +21 -1
  444. package/types/ngwr-select.d.ts +100 -6
  445. package/types/ngwr-shiny-text-testing.d.ts +165 -0
  446. package/types/ngwr-shiny-text.d.ts +4 -2
  447. package/types/ngwr-sidebar.d.ts +14 -5
  448. package/types/ngwr-slider.d.ts +84 -1
  449. package/types/ngwr-sparkline-testing.d.ts +69 -0
  450. package/types/ngwr-speed-dial-testing.d.ts +199 -0
  451. package/types/ngwr-speed-dial.d.ts +22 -3
  452. package/types/ngwr-splash-cursor-testing.d.ts +95 -0
  453. package/types/ngwr-split-text-testing.d.ts +209 -0
  454. package/types/ngwr-split-text.d.ts +21 -0
  455. package/types/ngwr-splitter-testing.d.ts +174 -0
  456. package/types/ngwr-spotlight-card-testing.d.ts +303 -0
  457. package/types/ngwr-star-border-testing.d.ts +135 -0
  458. package/types/ngwr-stepper-testing.d.ts +5 -5
  459. package/types/ngwr-stepper.d.ts +8 -0
  460. package/types/ngwr-storage.d.ts +1 -0
  461. package/types/ngwr-table.d.ts +114 -12
  462. package/types/ngwr-tabs-testing.d.ts +11 -10
  463. package/types/ngwr-tabs.d.ts +2 -0
  464. package/types/ngwr-theme.d.ts +82 -1
  465. package/types/ngwr-tilt-card-testing.d.ts +158 -0
  466. package/types/ngwr-tilt-card.d.ts +2 -0
  467. package/types/ngwr-toast.d.ts +14 -5
  468. package/types/ngwr-tour-testing.d.ts +143 -0
  469. package/types/ngwr-tour.d.ts +22 -4
  470. package/types/ngwr-tree.d.ts +12 -4
  471. package/types/ngwr-typewriter-testing.d.ts +211 -0
  472. package/types/ngwr-utils.d.ts +5 -2
  473. package/types/ngwr-version.d.ts +1 -1
  474. package/types/ngwr-waves-testing.d.ts +121 -0
  475. package/types/ngwr-window-testing.d.ts +263 -0
  476. package/types/ngwr-window.d.ts +66 -21
  477. package/typography/styles/_index.scss +1 -1
  478. package/window/styles/_index.scss +116 -42
  479. package/fesm2022/ngwr-date-adapter-fns.mjs.map +0 -1
  480. package/fesm2022/ngwr-date-adapter-luxon.mjs.map +0 -1
  481. package/fesm2022/ngwr-date-adapter.mjs.map +0 -1
@@ -93,7 +93,10 @@ class WrSidebar {
93
93
  uid = ++sidebarUid;
94
94
  router = inject(Router);
95
95
  currentUrl = toSignal(this.router.events.pipe(filter((e) => e instanceof NavigationEnd), map(e => e.urlAfterRedirects), startWith(this.router.url)), { initialValue: this.router.url });
96
- /** Last URL we auto-expanded — prevents re-opening a group the user collapsed. */
96
+ /**
97
+ * Last URL we resolved to a group — prevents re-opening one the user collapsed.
98
+ * A URL no entry claims is deliberately NOT recorded: see the effect below.
99
+ */
97
100
  lastAutoUrl = null;
98
101
  constructor() {
99
102
  // Seed `defaultOpen` groups — once per group, not once per `entries` emission.
@@ -118,9 +121,19 @@ class WrSidebar {
118
121
  const url = this.currentUrl();
119
122
  if (url === this.lastAutoUrl)
120
123
  return;
121
- this.lastAutoUrl = url;
124
+ // Latch AFTER the lookup, never before it. `entries` often arrives from an
125
+ // HTTP call, so the first run matches nothing — and latching first told the
126
+ // re-run that landed with the real nav that this URL was already handled.
127
+ // A deep link into a `defaultOpen: false` group then stayed collapsed, with
128
+ // the active link out of the tab order, until the user navigated away and
129
+ // back. Only a run that found a group has actually handled the URL; the
130
+ // user-collapse latch is unaffected, since that run is also the one that
131
+ // records it.
122
132
  const match = this.findGroupForUrl(url);
123
- if (!match || this.opened().has(match))
133
+ if (!match)
134
+ return;
135
+ this.lastAutoUrl = url;
136
+ if (this.opened().has(match))
124
137
  return;
125
138
  this.opened.update(prev => {
126
139
  const next = new Set(prev);
@@ -134,16 +147,18 @@ class WrSidebar {
134
147
  }
135
148
  /**
136
149
  * Stable id for the group body, referenced by the toggle's `aria-controls`.
137
- * Scoped to this sidebar and stripped of anything that is not id-safe: a title
138
- * like `Data & charts` used to produce `wr-sidebar-group-data-&-charts`, which
139
- * no selector can address.
150
+ *
151
+ * Keyed by POSITION, not by the title. Slugging the title kept the id readable
152
+ * but was not injective: it lowercases and collapses every non-`[a-z0-9]` run,
153
+ * so `Data & charts` and `Data / charts` produced the same string, and a title
154
+ * with no ASCII alphanumerics at all — `Компоненты`, `数据` — produced the empty
155
+ * one. A whole Russian or Chinese sidebar therefore emitted N identical ids and
156
+ * every toggle's `aria-controls` resolved to the FIRST group's body. The toggle
157
+ * and its body read `$index` from the same `@for` iteration, so the two always
158
+ * name each other — reordering `entries` renumbers both together.
140
159
  */
141
- groupBodyId(title) {
142
- const slug = title
143
- .toLowerCase()
144
- .replace(/[^a-z0-9]+/g, '-')
145
- .replace(/^-|-$/g, '');
146
- return `wr-sidebar-${this.uid}-group-${slug}`;
160
+ groupBodyId(index) {
161
+ return `wr-sidebar-${this.uid}-group-${index}`;
147
162
  }
148
163
  toggleGroup(title) {
149
164
  this.opened.update(prev => {
@@ -171,12 +186,12 @@ class WrSidebar {
171
186
  }
172
187
  return null;
173
188
  }
174
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrSidebar, deps: [], target: i0.ɵɵFactoryTarget.Component });
175
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: WrSidebar, isStandalone: true, selector: "wr-sidebar", inputs: { entries: { classPropertyName: "entries", publicName: "entries", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, defaultGroupIcon: { classPropertyName: "defaultGroupIcon", publicName: "defaultGroupIcon", isSignal: true, isRequired: false, transformFunction: null }, defaultItemIcon: { classPropertyName: "defaultItemIcon", publicName: "defaultItemIcon", isSignal: true, isRequired: false, transformFunction: null }, autoExpand: { classPropertyName: "autoExpand", publicName: "autoExpand", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "navigation" }, properties: { "attr.aria-label": "resolvedAriaLabel()" }, classAttribute: "wr-sidebar" }, ngImport: i0, template: "<!-- A div, not a <nav>: the host already IS the navigation landmark, and two\n nested ones announce the same list twice. -->\n<div class=\"wr-sidebar__inner\">\n @for (entry of entries(); track entry.title) {\n @if (!isGroup(entry)) {\n <a\n class=\"wr-sidebar__entry\"\n routerLinkActive=\"wr-sidebar__entry--active\"\n [routerLink]=\"asItem(entry).url\"\n [class.wr-sidebar__entry--disabled]=\"asItem(entry).disabled\"\n >\n @if (asItem(entry).icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__title\">{{ entry.title }}</span>\n @if (asItem(entry).badge) {\n <small class=\"wr-sidebar__badge\">{{ asItem(entry).badge }}</small>\n }\n </a>\n } @else {\n <section class=\"wr-sidebar__group\" [class.wr-sidebar__group--collapsed]=\"!isOpen(entry.title)\">\n <button\n type=\"button\"\n class=\"wr-sidebar__group-toggle\"\n [attr.aria-expanded]=\"isOpen(entry.title)\"\n [attr.aria-controls]=\"groupBodyId(entry.title)\"\n (click)=\"toggleGroup(entry.title)\"\n >\n @if (asGroup(entry).icon || defaultGroupIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__title\">{{ entry.title }}</span>\n <!-- Inline chrome chevron \u2014 not user-customizable. -->\n <svg\n class=\"wr-icon__svg wr-sidebar__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 <div\n class=\"wr-sidebar__group-wrap\"\n [attr.id]=\"groupBodyId(entry.title)\"\n [attr.inert]=\"!isOpen(entry.title) ? '' : null\"\n >\n <div class=\"wr-sidebar__group-body\">\n <ul class=\"wr-sidebar__list\">\n @for (child of asGroup(entry).children; track child.title) {\n <li>\n @if (child.disabled || !child.url.length) {\n <span class=\"wr-sidebar__item wr-sidebar__item--disabled\">\n @if (child.icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__item-title\">{{ child.title }}</span>\n @if (child.badge) {\n <small class=\"wr-sidebar__badge\">{{ child.badge }}</small>\n }\n </span>\n } @else {\n <a class=\"wr-sidebar__item\" routerLinkActive=\"wr-sidebar__item--active\" [routerLink]=\"child.url\">\n @if (child.icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__item-title\">{{ child.title }}</span>\n @if (child.badge) {\n <small class=\"wr-sidebar__badge\">{{ child.badge }}</small>\n }\n </a>\n }\n </li>\n }\n </ul>\n </div>\n </div>\n </section>\n }\n }\n</div>\n", dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "component", type: WrIcon, selector: "wr-icon", inputs: ["name"] }], encapsulation: i0.ViewEncapsulation.None });
189
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrSidebar, deps: [], target: i0.ɵɵFactoryTarget.Component });
190
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: WrSidebar, isStandalone: true, selector: "wr-sidebar", inputs: { entries: { classPropertyName: "entries", publicName: "entries", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, defaultGroupIcon: { classPropertyName: "defaultGroupIcon", publicName: "defaultGroupIcon", isSignal: true, isRequired: false, transformFunction: null }, defaultItemIcon: { classPropertyName: "defaultItemIcon", publicName: "defaultItemIcon", isSignal: true, isRequired: false, transformFunction: null }, autoExpand: { classPropertyName: "autoExpand", publicName: "autoExpand", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "navigation" }, properties: { "attr.aria-label": "resolvedAriaLabel()" }, classAttribute: "wr-sidebar" }, ngImport: i0, template: "<!-- A div, not a <nav>: the host already IS the navigation landmark, and two\n nested ones announce the same list twice. -->\n<div class=\"wr-sidebar__inner\">\n @for (entry of entries(); track entry.title) {\n @if (!isGroup(entry)) {\n <!--\n A span, not a greyed anchor, exactly as the nested branch below does it.\n `--disabled` only paints: `pointer-events: none` stops a real pointer but\n not the keyboard, so a disabled entry stayed in the tab order and Enter\n navigated \u2014 `disabled` on a `WrSidebarItem` is documented as \"no\n navigation\". An empty `url` is the same case: `[routerLink]=\"[]\"` resolves\n to the current route, which is a link that goes nowhere.\n -->\n @if (asItem(entry).disabled || !asItem(entry).url.length) {\n <span class=\"wr-sidebar__entry wr-sidebar__entry--disabled\">\n @if (asItem(entry).icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__title\">{{ entry.title }}</span>\n @if (asItem(entry).badge) {\n <small class=\"wr-sidebar__badge\">{{ asItem(entry).badge }}</small>\n }\n </span>\n } @else {\n <a class=\"wr-sidebar__entry\" routerLinkActive=\"wr-sidebar__entry--active\" [routerLink]=\"asItem(entry).url\">\n @if (asItem(entry).icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__title\">{{ entry.title }}</span>\n @if (asItem(entry).badge) {\n <small class=\"wr-sidebar__badge\">{{ asItem(entry).badge }}</small>\n }\n </a>\n }\n } @else {\n <section class=\"wr-sidebar__group\" [class.wr-sidebar__group--collapsed]=\"!isOpen(entry.title)\">\n <button\n type=\"button\"\n class=\"wr-sidebar__group-toggle\"\n [attr.aria-expanded]=\"isOpen(entry.title)\"\n [attr.aria-controls]=\"groupBodyId($index)\"\n (click)=\"toggleGroup(entry.title)\"\n >\n @if (asGroup(entry).icon || defaultGroupIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__title\">{{ entry.title }}</span>\n <!-- Inline chrome chevron \u2014 not user-customizable. -->\n <svg\n class=\"wr-icon__svg wr-sidebar__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 <div\n class=\"wr-sidebar__group-wrap\"\n [attr.id]=\"groupBodyId($index)\"\n [attr.inert]=\"!isOpen(entry.title) ? '' : null\"\n >\n <div class=\"wr-sidebar__group-body\">\n <ul class=\"wr-sidebar__list\">\n @for (child of asGroup(entry).children; track child.title) {\n <li>\n @if (child.disabled || !child.url.length) {\n <span class=\"wr-sidebar__item wr-sidebar__item--disabled\">\n @if (child.icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__item-title\">{{ child.title }}</span>\n @if (child.badge) {\n <small class=\"wr-sidebar__badge\">{{ child.badge }}</small>\n }\n </span>\n } @else {\n <a class=\"wr-sidebar__item\" routerLinkActive=\"wr-sidebar__item--active\" [routerLink]=\"child.url\">\n @if (child.icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__item-title\">{{ child.title }}</span>\n @if (child.badge) {\n <small class=\"wr-sidebar__badge\">{{ child.badge }}</small>\n }\n </a>\n }\n </li>\n }\n </ul>\n </div>\n </div>\n </section>\n }\n }\n</div>\n", dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "component", type: WrIcon, selector: "wr-icon", inputs: ["name"] }], encapsulation: i0.ViewEncapsulation.None });
176
191
  }
177
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrSidebar, decorators: [{
192
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrSidebar, decorators: [{
178
193
  type: Component,
179
- args: [{ selector: 'wr-sidebar', encapsulation: ViewEncapsulation.None, host: { class: 'wr-sidebar', role: 'navigation', '[attr.aria-label]': 'resolvedAriaLabel()' }, imports: [RouterLink, RouterLinkActive, WrIcon], template: "<!-- A div, not a <nav>: the host already IS the navigation landmark, and two\n nested ones announce the same list twice. -->\n<div class=\"wr-sidebar__inner\">\n @for (entry of entries(); track entry.title) {\n @if (!isGroup(entry)) {\n <a\n class=\"wr-sidebar__entry\"\n routerLinkActive=\"wr-sidebar__entry--active\"\n [routerLink]=\"asItem(entry).url\"\n [class.wr-sidebar__entry--disabled]=\"asItem(entry).disabled\"\n >\n @if (asItem(entry).icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__title\">{{ entry.title }}</span>\n @if (asItem(entry).badge) {\n <small class=\"wr-sidebar__badge\">{{ asItem(entry).badge }}</small>\n }\n </a>\n } @else {\n <section class=\"wr-sidebar__group\" [class.wr-sidebar__group--collapsed]=\"!isOpen(entry.title)\">\n <button\n type=\"button\"\n class=\"wr-sidebar__group-toggle\"\n [attr.aria-expanded]=\"isOpen(entry.title)\"\n [attr.aria-controls]=\"groupBodyId(entry.title)\"\n (click)=\"toggleGroup(entry.title)\"\n >\n @if (asGroup(entry).icon || defaultGroupIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__title\">{{ entry.title }}</span>\n <!-- Inline chrome chevron \u2014 not user-customizable. -->\n <svg\n class=\"wr-icon__svg wr-sidebar__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 <div\n class=\"wr-sidebar__group-wrap\"\n [attr.id]=\"groupBodyId(entry.title)\"\n [attr.inert]=\"!isOpen(entry.title) ? '' : null\"\n >\n <div class=\"wr-sidebar__group-body\">\n <ul class=\"wr-sidebar__list\">\n @for (child of asGroup(entry).children; track child.title) {\n <li>\n @if (child.disabled || !child.url.length) {\n <span class=\"wr-sidebar__item wr-sidebar__item--disabled\">\n @if (child.icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__item-title\">{{ child.title }}</span>\n @if (child.badge) {\n <small class=\"wr-sidebar__badge\">{{ child.badge }}</small>\n }\n </span>\n } @else {\n <a class=\"wr-sidebar__item\" routerLinkActive=\"wr-sidebar__item--active\" [routerLink]=\"child.url\">\n @if (child.icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__item-title\">{{ child.title }}</span>\n @if (child.badge) {\n <small class=\"wr-sidebar__badge\">{{ child.badge }}</small>\n }\n </a>\n }\n </li>\n }\n </ul>\n </div>\n </div>\n </section>\n }\n }\n</div>\n" }]
194
+ args: [{ selector: 'wr-sidebar', encapsulation: ViewEncapsulation.None, host: { class: 'wr-sidebar', role: 'navigation', '[attr.aria-label]': 'resolvedAriaLabel()' }, imports: [RouterLink, RouterLinkActive, WrIcon], template: "<!-- A div, not a <nav>: the host already IS the navigation landmark, and two\n nested ones announce the same list twice. -->\n<div class=\"wr-sidebar__inner\">\n @for (entry of entries(); track entry.title) {\n @if (!isGroup(entry)) {\n <!--\n A span, not a greyed anchor, exactly as the nested branch below does it.\n `--disabled` only paints: `pointer-events: none` stops a real pointer but\n not the keyboard, so a disabled entry stayed in the tab order and Enter\n navigated \u2014 `disabled` on a `WrSidebarItem` is documented as \"no\n navigation\". An empty `url` is the same case: `[routerLink]=\"[]\"` resolves\n to the current route, which is a link that goes nowhere.\n -->\n @if (asItem(entry).disabled || !asItem(entry).url.length) {\n <span class=\"wr-sidebar__entry wr-sidebar__entry--disabled\">\n @if (asItem(entry).icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__title\">{{ entry.title }}</span>\n @if (asItem(entry).badge) {\n <small class=\"wr-sidebar__badge\">{{ asItem(entry).badge }}</small>\n }\n </span>\n } @else {\n <a class=\"wr-sidebar__entry\" routerLinkActive=\"wr-sidebar__entry--active\" [routerLink]=\"asItem(entry).url\">\n @if (asItem(entry).icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__title\">{{ entry.title }}</span>\n @if (asItem(entry).badge) {\n <small class=\"wr-sidebar__badge\">{{ asItem(entry).badge }}</small>\n }\n </a>\n }\n } @else {\n <section class=\"wr-sidebar__group\" [class.wr-sidebar__group--collapsed]=\"!isOpen(entry.title)\">\n <button\n type=\"button\"\n class=\"wr-sidebar__group-toggle\"\n [attr.aria-expanded]=\"isOpen(entry.title)\"\n [attr.aria-controls]=\"groupBodyId($index)\"\n (click)=\"toggleGroup(entry.title)\"\n >\n @if (asGroup(entry).icon || defaultGroupIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__title\">{{ entry.title }}</span>\n <!-- Inline chrome chevron \u2014 not user-customizable. -->\n <svg\n class=\"wr-icon__svg wr-sidebar__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 <div\n class=\"wr-sidebar__group-wrap\"\n [attr.id]=\"groupBodyId($index)\"\n [attr.inert]=\"!isOpen(entry.title) ? '' : null\"\n >\n <div class=\"wr-sidebar__group-body\">\n <ul class=\"wr-sidebar__list\">\n @for (child of asGroup(entry).children; track child.title) {\n <li>\n @if (child.disabled || !child.url.length) {\n <span class=\"wr-sidebar__item wr-sidebar__item--disabled\">\n @if (child.icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__item-title\">{{ child.title }}</span>\n @if (child.badge) {\n <small class=\"wr-sidebar__badge\">{{ child.badge }}</small>\n }\n </span>\n } @else {\n <a class=\"wr-sidebar__item\" routerLinkActive=\"wr-sidebar__item--active\" [routerLink]=\"child.url\">\n @if (child.icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__item-title\">{{ child.title }}</span>\n @if (child.badge) {\n <small class=\"wr-sidebar__badge\">{{ child.badge }}</small>\n }\n </a>\n }\n </li>\n }\n </ul>\n </div>\n </div>\n </section>\n }\n }\n</div>\n" }]
180
195
  }], ctorParameters: () => [], propDecorators: { entries: [{ type: i0.Input, args: [{ isSignal: true, alias: "entries", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], defaultGroupIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultGroupIcon", required: false }] }], defaultItemIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultItemIcon", required: false }] }], autoExpand: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoExpand", required: false }] }] } });
181
196
 
182
197
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-sidebar.mjs","sources":["../../../projects/lib/sidebar/sidebar.ts","../../../projects/lib/sidebar/sidebar.html","../../../projects/lib/sidebar/ngwr-sidebar.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, effect, inject, input, signal } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { NavigationEnd, Router, RouterLink, RouterLinkActive } from '@angular/router';\n\nimport { filter, map, startWith } from 'rxjs';\n\nimport { useI18nText } from 'ngwr/i18n';\nimport { WrIcon } from 'ngwr/icon';\n\nimport type { WrSidebarEntry, WrSidebarGroup, WrSidebarItem } from './interfaces';\n\nfunction isGroup(entry: WrSidebarEntry): entry is WrSidebarGroup {\n return 'children' in entry;\n}\n\n/**\n * Instance counter for the group-body ids. Deterministic under prerender, unlike\n * `randomId()`, and unique across sidebars — a title-only id collided between two\n * sidebars on one page, and `aria-controls` then pointed at the wrong body.\n */\nlet sidebarUid = 0;\n\n/**\n * A `routerLink` segment array as an absolute path.\n *\n * The documented form carries the leading slash on the FIRST segment\n * (`['/settings', 'profile']`), and prefixing another one produced\n * `//settings/profile` — a path no URL ever equals, which is why auto-expand\n * never fired for anyone following the example. Both forms normalise here.\n */\nfunction routePath(segments: readonly (string | number)[]): string {\n return `/${segments.join('/')}`.replace(/\\/{2,}/g, '/');\n}\n\n/**\n * Whether `url` is `route` or something below it. A bare `startsWith` matched a\n * SIBLING whose path merely began the same way, so visiting `/table` expanded the\n * group that owns `/tabs`.\n */\nfunction matchesRoute(url: string, route: string): boolean {\n if (!url.startsWith(route)) return false;\n const rest = url.slice(route.length);\n return rest === '' || rest.startsWith('/') || rest.startsWith('?') || rest.startsWith('#');\n}\n\n/**\n * Data-driven sidebar nav. Pass `[entries]` — either flat\n * `WrSidebarItem`s (direct links) or `WrSidebarGroup`s (expand to reveal\n * child items). Active route auto-expands its containing group.\n *\n * @example\n * ```html\n * <wr-sidebar [entries]=\"[\n * { title: 'Dashboard', icon: 'home', url: ['/dashboard'] },\n * {\n * title: 'Settings',\n * icon: 'cog',\n * children: [\n * { title: 'Profile', url: ['/settings', 'profile'] },\n * { title: 'Billing', url: ['/settings', 'billing'] }\n * ]\n * }\n * ]\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/sidebar\n */\n@Component({\n selector: 'wr-sidebar',\n templateUrl: './sidebar.html',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-sidebar', role: 'navigation', '[attr.aria-label]': 'resolvedAriaLabel()' },\n imports: [RouterLink, RouterLinkActive, WrIcon],\n})\nexport class WrSidebar {\n readonly entries = input<readonly WrSidebarEntry[]>([]);\n\n /**\n * Accessible name for the navigation landmark. Falls back to `sidebar.label`,\n * then `'Sidebar'` — a page with a sidebar AND a table of contents has two\n * navigation landmarks, and an unnamed one is announced as just \"navigation\".\n */\n readonly ariaLabel = input<string | null>(null);\n\n protected readonly resolvedAriaLabel = useI18nText(this.ariaLabel, 'sidebar.label', 'Sidebar');\n\n /** Default icon for entries without one. @default 'folder' */\n readonly defaultGroupIcon = input<string>('folder');\n\n /** Default icon for items without one. @default 'caret-forward' */\n readonly defaultItemIcon = input<string>('caret-forward');\n\n /** Auto-expand the group containing the active route. @default true */\n readonly autoExpand = input(true, { transform: coerceBooleanProperty });\n\n protected readonly isGroup = isGroup;\n\n private readonly opened = signal<ReadonlySet<string>>(new Set());\n\n /** Group titles already seeded from `defaultOpen`, so it seeds once and not again. */\n private readonly seeded = new Set<string>();\n\n private readonly uid = ++sidebarUid;\n\n private readonly router = inject(Router);\n private readonly currentUrl = toSignal(\n this.router.events.pipe(\n filter((e): e is NavigationEnd => e instanceof NavigationEnd),\n map(e => e.urlAfterRedirects),\n startWith(this.router.url)\n ),\n { initialValue: this.router.url }\n );\n\n /** Last URL we auto-expanded — prevents re-opening a group the user collapsed. */\n private lastAutoUrl: string | null = null;\n\n constructor() {\n // Seed `defaultOpen` groups — once per group, not once per `entries` emission.\n // Re-seeding meant any later change to the array (a badge count, a filtered\n // list) re-opened a group the user had deliberately collapsed.\n effect(() => {\n const fresh: string[] = [];\n for (const entry of this.entries()) {\n if (isGroup(entry) && entry.defaultOpen && !this.seeded.has(entry.title)) {\n this.seeded.add(entry.title);\n fresh.push(entry.title);\n }\n }\n if (fresh.length > 0) {\n this.opened.update(prev => new Set([...prev, ...fresh]));\n }\n });\n\n // Auto-expand the group containing the current URL.\n effect(() => {\n if (!this.autoExpand()) return;\n const url = this.currentUrl();\n if (url === this.lastAutoUrl) return;\n this.lastAutoUrl = url;\n const match = this.findGroupForUrl(url);\n if (!match || this.opened().has(match)) return;\n this.opened.update(prev => {\n const next = new Set(prev);\n next.add(match);\n return next;\n });\n });\n }\n\n protected isOpen(title: string): boolean {\n return this.opened().has(title);\n }\n\n /**\n * Stable id for the group body, referenced by the toggle's `aria-controls`.\n * Scoped to this sidebar and stripped of anything that is not id-safe: a title\n * like `Data & charts` used to produce `wr-sidebar-group-data-&-charts`, which\n * no selector can address.\n */\n protected groupBodyId(title: string): string {\n const slug = title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '');\n return `wr-sidebar-${this.uid}-group-${slug}`;\n }\n\n protected toggleGroup(title: string): void {\n this.opened.update(prev => {\n const next = new Set(prev);\n if (next.has(title)) next.delete(title);\n else next.add(title);\n return next;\n });\n }\n\n protected asItem(entry: WrSidebarEntry): WrSidebarItem {\n return entry as WrSidebarItem;\n }\n\n protected asGroup(entry: WrSidebarEntry): WrSidebarGroup {\n return entry as WrSidebarGroup;\n }\n\n private findGroupForUrl(url: string): string | null {\n for (const entry of this.entries()) {\n if (!isGroup(entry)) continue;\n const hit = entry.children.some(child => matchesRoute(url, routePath(child.url)));\n if (hit) return entry.title;\n }\n return null;\n }\n}\n","<!-- A div, not a <nav>: the host already IS the navigation landmark, and two\n nested ones announce the same list twice. -->\n<div class=\"wr-sidebar__inner\">\n @for (entry of entries(); track entry.title) {\n @if (!isGroup(entry)) {\n <a\n class=\"wr-sidebar__entry\"\n routerLinkActive=\"wr-sidebar__entry--active\"\n [routerLink]=\"asItem(entry).url\"\n [class.wr-sidebar__entry--disabled]=\"asItem(entry).disabled\"\n >\n @if (asItem(entry).icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__title\">{{ entry.title }}</span>\n @if (asItem(entry).badge) {\n <small class=\"wr-sidebar__badge\">{{ asItem(entry).badge }}</small>\n }\n </a>\n } @else {\n <section class=\"wr-sidebar__group\" [class.wr-sidebar__group--collapsed]=\"!isOpen(entry.title)\">\n <button\n type=\"button\"\n class=\"wr-sidebar__group-toggle\"\n [attr.aria-expanded]=\"isOpen(entry.title)\"\n [attr.aria-controls]=\"groupBodyId(entry.title)\"\n (click)=\"toggleGroup(entry.title)\"\n >\n @if (asGroup(entry).icon || defaultGroupIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__title\">{{ entry.title }}</span>\n <!-- Inline chrome chevron — not user-customizable. -->\n <svg\n class=\"wr-icon__svg wr-sidebar__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 <div\n class=\"wr-sidebar__group-wrap\"\n [attr.id]=\"groupBodyId(entry.title)\"\n [attr.inert]=\"!isOpen(entry.title) ? '' : null\"\n >\n <div class=\"wr-sidebar__group-body\">\n <ul class=\"wr-sidebar__list\">\n @for (child of asGroup(entry).children; track child.title) {\n <li>\n @if (child.disabled || !child.url.length) {\n <span class=\"wr-sidebar__item wr-sidebar__item--disabled\">\n @if (child.icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__item-title\">{{ child.title }}</span>\n @if (child.badge) {\n <small class=\"wr-sidebar__badge\">{{ child.badge }}</small>\n }\n </span>\n } @else {\n <a class=\"wr-sidebar__item\" routerLinkActive=\"wr-sidebar__item--active\" [routerLink]=\"child.url\">\n @if (child.icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__item-title\">{{ child.title }}</span>\n @if (child.badge) {\n <small class=\"wr-sidebar__badge\">{{ child.badge }}</small>\n }\n </a>\n }\n </li>\n }\n </ul>\n </div>\n </div>\n </section>\n }\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;;;;AAAA;;;;;AAKG;AAcH,SAAS,OAAO,CAAC,KAAqB,EAAA;IACpC,OAAO,UAAU,IAAI,KAAK;AAC5B;AAEA;;;;AAIG;AACH,IAAI,UAAU,GAAG,CAAC;AAElB;;;;;;;AAOG;AACH,SAAS,SAAS,CAAC,QAAsC,EAAA;AACvD,IAAA,OAAO,CAAA,CAAA,EAAI,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA,CAAE,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;AACzD;AAEA;;;;AAIG;AACH,SAAS,YAAY,CAAC,GAAW,EAAE,KAAa,EAAA;AAC9C,IAAA,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC;AAAE,QAAA,OAAO,KAAK;IACxC,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC;IACpC,OAAO,IAAI,KAAK,EAAE,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;AAC5F;AAEA;;;;;;;;;;;;;;;;;;;;;AAqBG;MAQU,SAAS,CAAA;IACX,OAAO,GAAG,KAAK,CAA4B,EAAE;gFAAC;AAEvD;;;;AAIG;IACM,SAAS,GAAG,KAAK,CAAgB,IAAI;kFAAC;IAE5B,iBAAiB,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,EAAE,SAAS,CAAC;;IAGrF,gBAAgB,GAAG,KAAK,CAAS,QAAQ;yFAAC;;IAG1C,eAAe,GAAG,KAAK,CAAS,eAAe;wFAAC;;IAGhD,UAAU,GAAG,KAAK,CAAC,IAAI,kFAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;IAEpD,OAAO,GAAG,OAAO;AAEnB,IAAA,MAAM,GAAG,MAAM,CAAsB,IAAI,GAAG,EAAE;+EAAC;;AAG/C,IAAA,MAAM,GAAG,IAAI,GAAG,EAAU;IAE1B,GAAG,GAAG,EAAE,UAAU;AAElB,IAAA,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IACvB,UAAU,GAAG,QAAQ,CACpC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CACrB,MAAM,CAAC,CAAC,CAAC,KAAyB,CAAC,YAAY,aAAa,CAAC,EAC7D,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,iBAAiB,CAAC,EAC7B,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAC3B,EACD,EAAE,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAClC;;IAGO,WAAW,GAAkB,IAAI;AAEzC,IAAA,WAAA,GAAA;;;;QAIE,MAAM,CAAC,MAAK;YACV,MAAM,KAAK,GAAa,EAAE;YAC1B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE;gBAClC,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;oBACxE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC;AAC5B,oBAAA,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;gBACzB;YACF;AACA,YAAA,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;gBACpB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC;YAC1D;AACF,QAAA,CAAC,CAAC;;QAGF,MAAM,CAAC,MAAK;AACV,YAAA,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE;gBAAE;AACxB,YAAA,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE;AAC7B,YAAA,IAAI,GAAG,KAAK,IAAI,CAAC,WAAW;gBAAE;AAC9B,YAAA,IAAI,CAAC,WAAW,GAAG,GAAG;YACtB,MAAM,KAAK,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC;YACvC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE;AACxC,YAAA,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,IAAG;AACxB,gBAAA,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC;AAC1B,gBAAA,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;AACf,gBAAA,OAAO,IAAI;AACb,YAAA,CAAC,CAAC;AACJ,QAAA,CAAC,CAAC;IACJ;AAEU,IAAA,MAAM,CAAC,KAAa,EAAA;QAC5B,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC;IACjC;AAEA;;;;;AAKG;AACO,IAAA,WAAW,CAAC,KAAa,EAAA;QACjC,MAAM,IAAI,GAAG;AACV,aAAA,WAAW;AACX,aAAA,OAAO,CAAC,aAAa,EAAE,GAAG;AAC1B,aAAA,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;AACxB,QAAA,OAAO,cAAc,IAAI,CAAC,GAAG,CAAA,OAAA,EAAU,IAAI,EAAE;IAC/C;AAEU,IAAA,WAAW,CAAC,KAAa,EAAA;AACjC,QAAA,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,IAAG;AACxB,YAAA,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC;AAC1B,YAAA,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;AAAE,gBAAA,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;;AAClC,gBAAA,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;AACpB,YAAA,OAAO,IAAI;AACb,QAAA,CAAC,CAAC;IACJ;AAEU,IAAA,MAAM,CAAC,KAAqB,EAAA;AACpC,QAAA,OAAO,KAAsB;IAC/B;AAEU,IAAA,OAAO,CAAC,KAAqB,EAAA;AACrC,QAAA,OAAO,KAAuB;IAChC;AAEQ,IAAA,eAAe,CAAC,GAAW,EAAA;QACjC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE;AAClC,YAAA,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;gBAAE;YACrB,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,YAAY,CAAC,GAAG,EAAE,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;AACjF,YAAA,IAAI,GAAG;gBAAE,OAAO,KAAK,CAAC,KAAK;QAC7B;AACA,QAAA,OAAO,IAAI;IACb;uGAtHW,SAAS,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAT,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,SAAS,k5BClFtB,6lHAsFA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDNY,UAAU,EAAA,QAAA,EAAA,cAAA,EAAA,MAAA,EAAA,CAAA,QAAA,EAAA,aAAA,EAAA,UAAA,EAAA,qBAAA,EAAA,OAAA,EAAA,MAAA,EAAA,YAAA,EAAA,kBAAA,EAAA,oBAAA,EAAA,YAAA,EAAA,YAAA,EAAA,YAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,gBAAgB,8MAAE,MAAM,EAAA,QAAA,EAAA,SAAA,EAAA,MAAA,EAAA,CAAA,MAAA,CAAA,EAAA,CAAA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FAEnC,SAAS,EAAA,UAAA,EAAA,CAAA;kBAPrB,SAAS;+BACE,YAAY,EAAA,aAAA,EAEP,iBAAiB,CAAC,IAAI,EAAA,IAAA,EAC/B,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,EAAA,OAAA,EACpF,CAAC,UAAU,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAA,QAAA,EAAA,6lHAAA,EAAA;;;AEhFjD;;AAEG;;;;"}
1
+ {"version":3,"file":"ngwr-sidebar.mjs","sources":["../../../projects/lib/sidebar/sidebar.ts","../../../projects/lib/sidebar/sidebar.html","../../../projects/lib/sidebar/ngwr-sidebar.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, effect, inject, input, signal } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { NavigationEnd, Router, RouterLink, RouterLinkActive } from '@angular/router';\n\nimport { filter, map, startWith } from 'rxjs';\n\nimport { useI18nText } from 'ngwr/i18n';\nimport { WrIcon } from 'ngwr/icon';\n\nimport type { WrSidebarEntry, WrSidebarGroup, WrSidebarItem } from './interfaces';\n\nfunction isGroup(entry: WrSidebarEntry): entry is WrSidebarGroup {\n return 'children' in entry;\n}\n\n/**\n * Instance counter for the group-body ids. Deterministic under prerender, unlike\n * `randomId()`, and unique across sidebars — a title-only id collided between two\n * sidebars on one page, and `aria-controls` then pointed at the wrong body.\n */\nlet sidebarUid = 0;\n\n/**\n * A `routerLink` segment array as an absolute path.\n *\n * The documented form carries the leading slash on the FIRST segment\n * (`['/settings', 'profile']`), and prefixing another one produced\n * `//settings/profile` — a path no URL ever equals, which is why auto-expand\n * never fired for anyone following the example. Both forms normalise here.\n */\nfunction routePath(segments: readonly (string | number)[]): string {\n return `/${segments.join('/')}`.replace(/\\/{2,}/g, '/');\n}\n\n/**\n * Whether `url` is `route` or something below it. A bare `startsWith` matched a\n * SIBLING whose path merely began the same way, so visiting `/table` expanded the\n * group that owns `/tabs`.\n */\nfunction matchesRoute(url: string, route: string): boolean {\n if (!url.startsWith(route)) return false;\n const rest = url.slice(route.length);\n return rest === '' || rest.startsWith('/') || rest.startsWith('?') || rest.startsWith('#');\n}\n\n/**\n * Data-driven sidebar nav. Pass `[entries]` — either flat\n * `WrSidebarItem`s (direct links) or `WrSidebarGroup`s (expand to reveal\n * child items). Active route auto-expands its containing group.\n *\n * @example\n * ```html\n * <wr-sidebar [entries]=\"[\n * { title: 'Dashboard', icon: 'home', url: ['/dashboard'] },\n * {\n * title: 'Settings',\n * icon: 'cog',\n * children: [\n * { title: 'Profile', url: ['/settings', 'profile'] },\n * { title: 'Billing', url: ['/settings', 'billing'] }\n * ]\n * }\n * ]\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/sidebar\n */\n@Component({\n selector: 'wr-sidebar',\n templateUrl: './sidebar.html',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-sidebar', role: 'navigation', '[attr.aria-label]': 'resolvedAriaLabel()' },\n imports: [RouterLink, RouterLinkActive, WrIcon],\n})\nexport class WrSidebar {\n readonly entries = input<readonly WrSidebarEntry[]>([]);\n\n /**\n * Accessible name for the navigation landmark. Falls back to `sidebar.label`,\n * then `'Sidebar'` — a page with a sidebar AND a table of contents has two\n * navigation landmarks, and an unnamed one is announced as just \"navigation\".\n */\n readonly ariaLabel = input<string | null>(null);\n\n protected readonly resolvedAriaLabel = useI18nText(this.ariaLabel, 'sidebar.label', 'Sidebar');\n\n /** Default icon for entries without one. @default 'folder' */\n readonly defaultGroupIcon = input<string>('folder');\n\n /** Default icon for items without one. @default 'caret-forward' */\n readonly defaultItemIcon = input<string>('caret-forward');\n\n /** Auto-expand the group containing the active route. @default true */\n readonly autoExpand = input(true, { transform: coerceBooleanProperty });\n\n protected readonly isGroup = isGroup;\n\n private readonly opened = signal<ReadonlySet<string>>(new Set());\n\n /** Group titles already seeded from `defaultOpen`, so it seeds once and not again. */\n private readonly seeded = new Set<string>();\n\n private readonly uid = ++sidebarUid;\n\n private readonly router = inject(Router);\n private readonly currentUrl = toSignal(\n this.router.events.pipe(\n filter((e): e is NavigationEnd => e instanceof NavigationEnd),\n map(e => e.urlAfterRedirects),\n startWith(this.router.url)\n ),\n { initialValue: this.router.url }\n );\n\n /**\n * Last URL we resolved to a group — prevents re-opening one the user collapsed.\n * A URL no entry claims is deliberately NOT recorded: see the effect below.\n */\n private lastAutoUrl: string | null = null;\n\n constructor() {\n // Seed `defaultOpen` groups — once per group, not once per `entries` emission.\n // Re-seeding meant any later change to the array (a badge count, a filtered\n // list) re-opened a group the user had deliberately collapsed.\n effect(() => {\n const fresh: string[] = [];\n for (const entry of this.entries()) {\n if (isGroup(entry) && entry.defaultOpen && !this.seeded.has(entry.title)) {\n this.seeded.add(entry.title);\n fresh.push(entry.title);\n }\n }\n if (fresh.length > 0) {\n this.opened.update(prev => new Set([...prev, ...fresh]));\n }\n });\n\n // Auto-expand the group containing the current URL.\n effect(() => {\n if (!this.autoExpand()) return;\n const url = this.currentUrl();\n if (url === this.lastAutoUrl) return;\n // Latch AFTER the lookup, never before it. `entries` often arrives from an\n // HTTP call, so the first run matches nothing — and latching first told the\n // re-run that landed with the real nav that this URL was already handled.\n // A deep link into a `defaultOpen: false` group then stayed collapsed, with\n // the active link out of the tab order, until the user navigated away and\n // back. Only a run that found a group has actually handled the URL; the\n // user-collapse latch is unaffected, since that run is also the one that\n // records it.\n const match = this.findGroupForUrl(url);\n if (!match) return;\n this.lastAutoUrl = url;\n if (this.opened().has(match)) return;\n this.opened.update(prev => {\n const next = new Set(prev);\n next.add(match);\n return next;\n });\n });\n }\n\n protected isOpen(title: string): boolean {\n return this.opened().has(title);\n }\n\n /**\n * Stable id for the group body, referenced by the toggle's `aria-controls`.\n *\n * Keyed by POSITION, not by the title. Slugging the title kept the id readable\n * but was not injective: it lowercases and collapses every non-`[a-z0-9]` run,\n * so `Data & charts` and `Data / charts` produced the same string, and a title\n * with no ASCII alphanumerics at all — `Компоненты`, `数据` — produced the empty\n * one. A whole Russian or Chinese sidebar therefore emitted N identical ids and\n * every toggle's `aria-controls` resolved to the FIRST group's body. The toggle\n * and its body read `$index` from the same `@for` iteration, so the two always\n * name each other — reordering `entries` renumbers both together.\n */\n protected groupBodyId(index: number): string {\n return `wr-sidebar-${this.uid}-group-${index}`;\n }\n\n protected toggleGroup(title: string): void {\n this.opened.update(prev => {\n const next = new Set(prev);\n if (next.has(title)) next.delete(title);\n else next.add(title);\n return next;\n });\n }\n\n protected asItem(entry: WrSidebarEntry): WrSidebarItem {\n return entry as WrSidebarItem;\n }\n\n protected asGroup(entry: WrSidebarEntry): WrSidebarGroup {\n return entry as WrSidebarGroup;\n }\n\n private findGroupForUrl(url: string): string | null {\n for (const entry of this.entries()) {\n if (!isGroup(entry)) continue;\n const hit = entry.children.some(child => matchesRoute(url, routePath(child.url)));\n if (hit) return entry.title;\n }\n return null;\n }\n}\n","<!-- A div, not a <nav>: the host already IS the navigation landmark, and two\n nested ones announce the same list twice. -->\n<div class=\"wr-sidebar__inner\">\n @for (entry of entries(); track entry.title) {\n @if (!isGroup(entry)) {\n <!--\n A span, not a greyed anchor, exactly as the nested branch below does it.\n `--disabled` only paints: `pointer-events: none` stops a real pointer but\n not the keyboard, so a disabled entry stayed in the tab order and Enter\n navigated — `disabled` on a `WrSidebarItem` is documented as \"no\n navigation\". An empty `url` is the same case: `[routerLink]=\"[]\"` resolves\n to the current route, which is a link that goes nowhere.\n -->\n @if (asItem(entry).disabled || !asItem(entry).url.length) {\n <span class=\"wr-sidebar__entry wr-sidebar__entry--disabled\">\n @if (asItem(entry).icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__title\">{{ entry.title }}</span>\n @if (asItem(entry).badge) {\n <small class=\"wr-sidebar__badge\">{{ asItem(entry).badge }}</small>\n }\n </span>\n } @else {\n <a class=\"wr-sidebar__entry\" routerLinkActive=\"wr-sidebar__entry--active\" [routerLink]=\"asItem(entry).url\">\n @if (asItem(entry).icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__title\">{{ entry.title }}</span>\n @if (asItem(entry).badge) {\n <small class=\"wr-sidebar__badge\">{{ asItem(entry).badge }}</small>\n }\n </a>\n }\n } @else {\n <section class=\"wr-sidebar__group\" [class.wr-sidebar__group--collapsed]=\"!isOpen(entry.title)\">\n <button\n type=\"button\"\n class=\"wr-sidebar__group-toggle\"\n [attr.aria-expanded]=\"isOpen(entry.title)\"\n [attr.aria-controls]=\"groupBodyId($index)\"\n (click)=\"toggleGroup(entry.title)\"\n >\n @if (asGroup(entry).icon || defaultGroupIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__title\">{{ entry.title }}</span>\n <!-- Inline chrome chevron — not user-customizable. -->\n <svg\n class=\"wr-icon__svg wr-sidebar__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 <div\n class=\"wr-sidebar__group-wrap\"\n [attr.id]=\"groupBodyId($index)\"\n [attr.inert]=\"!isOpen(entry.title) ? '' : null\"\n >\n <div class=\"wr-sidebar__group-body\">\n <ul class=\"wr-sidebar__list\">\n @for (child of asGroup(entry).children; track child.title) {\n <li>\n @if (child.disabled || !child.url.length) {\n <span class=\"wr-sidebar__item wr-sidebar__item--disabled\">\n @if (child.icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__item-title\">{{ child.title }}</span>\n @if (child.badge) {\n <small class=\"wr-sidebar__badge\">{{ child.badge }}</small>\n }\n </span>\n } @else {\n <a class=\"wr-sidebar__item\" routerLinkActive=\"wr-sidebar__item--active\" [routerLink]=\"child.url\">\n @if (child.icon || defaultItemIcon(); as name) {\n <wr-icon class=\"wr-sidebar__icon\" [name]=\"name\" />\n }\n <span class=\"wr-sidebar__item-title\">{{ child.title }}</span>\n @if (child.badge) {\n <small class=\"wr-sidebar__badge\">{{ child.badge }}</small>\n }\n </a>\n }\n </li>\n }\n </ul>\n </div>\n </div>\n </section>\n }\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -49,10 +49,10 @@ class WrSkeleton {
49
49
  return parts.join(' ');
50
50
  }, /* @ts-ignore */
51
51
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
52
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrSkeleton, deps: [], target: i0.ɵɵFactoryTarget.Component });
53
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.1", type: WrSkeleton, isStandalone: true, selector: "wr-skeleton", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, animated: { classPropertyName: "animated", publicName: "animated", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "aria-busy": "true", "aria-live": "polite" }, properties: { "class": "classes()" } }, ngImport: i0, template: '', isInline: true, encapsulation: i0.ViewEncapsulation.None });
52
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrSkeleton, deps: [], target: i0.ɵɵFactoryTarget.Component });
53
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.2", type: WrSkeleton, isStandalone: true, selector: "wr-skeleton", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, animated: { classPropertyName: "animated", publicName: "animated", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "aria-busy": "true", "aria-live": "polite" }, properties: { "class": "classes()" } }, ngImport: i0, template: '', isInline: true, encapsulation: i0.ViewEncapsulation.None });
54
54
  }
55
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrSkeleton, decorators: [{
55
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrSkeleton, decorators: [{
56
56
  type: Component,
57
57
  args: [{
58
58
  selector: 'wr-skeleton',
@@ -1,7 +1,9 @@
1
1
  import { Directionality } from '@angular/cdk/bidi';
2
2
  import { coerceNumberProperty, coerceBooleanProperty } from '@angular/cdk/coercion';
3
3
  import * as i0 from '@angular/core';
4
- import { input, computed, model, output, signal, viewChild, inject, effect, ViewEncapsulation, Component } from '@angular/core';
4
+ import { input, computed, inject, model, output, signal, viewChild, effect, untracked, ViewEncapsulation, Component } from '@angular/core';
5
+ import { WR_FORM_FIELD } from 'ngwr/form';
6
+ import { useI18nText } from 'ngwr/i18n';
5
7
  import { clamp, round } from 'ngwr/utils';
6
8
 
7
9
  /**
@@ -73,6 +75,69 @@ class WrSlider {
73
75
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
74
76
  /** Render the current value below the track. @default true */
75
77
  showLabel = input(true, { ...(ngDevMode ? { debugName: "showLabel" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
78
+ /**
79
+ * Accessible name of the thumb — the single one, or the LOWER one in range
80
+ * mode. Falls back to `slider.label` / `slider.lower`.
81
+ */
82
+ ariaLabel = input(null, /* @ts-ignore */
83
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
84
+ /** Accessible name of the upper thumb in range mode. Falls back to `slider.upper`. */
85
+ upperLabel = input(null, /* @ts-ignore */
86
+ ...(ngDevMode ? [{ debugName: "upperLabel" }] : /* istanbul ignore next */ []));
87
+ /**
88
+ * A thumb projects no text, so these ARE the accessible names — and they were
89
+ * hard-coded English with no key behind them, so every slider on a page
90
+ * announced the same untranslated "Value" whatever it controlled.
91
+ *
92
+ * Range mode reads `ariaLabel` as the LOWER end rather than as the whole
93
+ * control: a name given once must land on something, and naming both thumbs
94
+ * the same thing would read two different values under one name.
95
+ */
96
+ singleLabel = useI18nText(this.ariaLabel, 'slider.label', 'Value');
97
+ lowerLabel = useI18nText(this.ariaLabel, 'slider.lower', 'Lower value');
98
+ resolvedUpperLabel = useI18nText(this.upperLabel, 'slider.upper', 'Upper value');
99
+ resolvedLowLabel = computed(() => (this.range() ? this.lowerLabel() : this.singleLabel()), /* @ts-ignore */
100
+ ...(ngDevMode ? [{ debugName: "resolvedLowLabel" }] : /* istanbul ignore next */ []));
101
+ field = inject(WR_FORM_FIELD, { optional: true });
102
+ /**
103
+ * Id the surrounding `<wr-form-field>`'s `<label for>` points at, put on the
104
+ * single thumb — the LOWER one in range mode, the same end `ariaLabel` names.
105
+ *
106
+ * The field renders its label before it can see what was projected into it, so
107
+ * the id travels the other way and the control adopts it, exactly as
108
+ * `[wrInput]` does. Without this the `for` named an element that was nowhere
109
+ * in the document: clicking the label did nothing, and the field's own text
110
+ * reached the thumb through no path at all.
111
+ *
112
+ * It lands on the THUMB rather than on the host because only a labelable
113
+ * element can be a label's target, and `<wr-slider>` is not one — a `for`
114
+ * pointing at the host would resolve and still name nothing. The thumb is a
115
+ * real `<button>`, so it is.
116
+ *
117
+ * What this does NOT change is the thumb's name: an `aria-label` outranks a
118
+ * `<label>` in the accname order, and the thumb keeps its own because the
119
+ * field cannot promise it has one to give — `<wr-form-field>` renders a label
120
+ * only when `label` is set, and a slider that traded a generic name for no
121
+ * name would be the worse bug. Set `[ariaLabel]` to the field's label where
122
+ * the two should read alike.
123
+ */
124
+ thumbId = computed(() => this.field?.controlId() ?? null, /* @ts-ignore */
125
+ ...(ngDevMode ? [{ debugName: "thumbId" }] : /* istanbul ignore next */ []));
126
+ /**
127
+ * The field's error message, wired to the thumb the same way `wrInput` wires it
128
+ * to its native element.
129
+ *
130
+ * Without this the messages `<wr-form-field>` renders are visible and nothing
131
+ * else: a screen reader on the thumb never learns the field is invalid, nor
132
+ * what the message says. `aria-invalid` is keyed on the message EXISTING rather
133
+ * than on `errorKeys()`, because the field only publishes an id once it is
134
+ * showing something — announcing "invalid" while pointing at nothing is worse
135
+ * than staying quiet.
136
+ */
137
+ describedBy = computed(() => this.field?.describedBy() ?? null, /* @ts-ignore */
138
+ ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
139
+ ariaInvalid = computed(() => (this.field?.describedBy() ? 'true' : null), /* @ts-ignore */
140
+ ...(ngDevMode ? [{ debugName: "ariaInvalid" }] : /* istanbul ignore next */ []));
76
141
  /**
77
142
  * Current value. Bound by `[formField]`, or two-way via `[(value)]`. Shape
78
143
  * follows `range`: a plain `number`, or `[low, high]` in range mode.
@@ -83,8 +148,23 @@ class WrSlider {
83
148
  touch = output();
84
149
  low = signal(0, /* @ts-ignore */
85
150
  ...(ngDevMode ? [{ debugName: "low" }] : /* istanbul ignore next */ []));
151
+ /**
152
+ * The high thumb's cell. Its literal default stands in for `maxValue()`,
153
+ * which cannot be read at field-initialisation time — see `highSeeded` and
154
+ * the constructor effect.
155
+ */
86
156
  high = signal(100, /* @ts-ignore */
87
157
  ...(ngDevMode ? [{ debugName: "high" }] : /* istanbul ignore next */ []));
158
+ /**
159
+ * Whether `high` has ever been told a real value by the model.
160
+ *
161
+ * `<wr-slider range [min]="200" [max]="300" />` binds no value, so the model
162
+ * holds its own scalar default and the tuple branch below never runs: `high`
163
+ * kept the literal 100, which is not inside every `[min, max]`. The thumb
164
+ * rendered off the track and the first interaction committed `[low, 100]` —
165
+ * a value the slider itself calls out of range.
166
+ */
167
+ highSeeded = false;
88
168
  track = viewChild.required('track', /* @ts-ignore */
89
169
  ...(ngDevMode ? [{ debugName: "track" }] : /* istanbul ignore next */ []));
90
170
  /**
@@ -121,16 +201,39 @@ class WrSlider {
121
201
  constructor() {
122
202
  // Keep the internal thumbs in sync with external writes to `value`
123
203
  // (the old `writeValue`): split the tuple / clamp into the low & high cells.
204
+ // The cells are clamped and the MODEL IS NOT TOUCHED, and the asymmetry is
205
+ // deliberate. A thumb cannot render outside its own track, so the display
206
+ // has no choice — but writing that clamp back into `value` would erase the
207
+ // error it represents: under `[formField]` the bounds reach this component
208
+ // only through the schema's `min()` / `max()` rules (binding `[min]` beside
209
+ // `[formField]` is an NG8022 compile error), so the write-back deleted the
210
+ // out-of-range value those rules exist to report, and marked a pristine
211
+ // form dirty on first paint besides. The first real interaction emits an
212
+ // in-range number through `emitChange` and the two agree from then on.
124
213
  effect(() => {
125
214
  const v = this.value();
126
215
  if (Array.isArray(v)) {
127
216
  const tuple = v;
128
- this.low.set(this.clampToBounds(tuple[0]));
129
- this.high.set(this.clampToBounds(tuple[1]));
130
- }
131
- else if (typeof v === 'number') {
132
- this.low.set(this.clampToBounds(v));
217
+ const lo = this.clampToBounds(tuple[0]);
218
+ const hi = this.clampToBounds(tuple[1]);
219
+ this.low.set(lo);
220
+ this.high.set(hi);
221
+ this.highSeeded = true;
222
+ return;
133
223
  }
224
+ if (typeof v !== 'number')
225
+ return;
226
+ const lo = this.clampToBounds(v);
227
+ this.low.set(lo);
228
+ if (!this.range())
229
+ return;
230
+ // Range mode holding a scalar: the high thumb has never been told
231
+ // anything, and its literal default is not inside every `[min, max]`.
232
+ // Seed it at the top of the range — which is what the 100 meant back when
233
+ // the bounds were 0–100. The MODEL is left alone here too; the shape it
234
+ // holds is the consumer's business until they move a thumb.
235
+ this.high.set(this.highSeeded ? this.clampToBounds(untracked(this.high)) : this.maxValue());
236
+ this.highSeeded = true;
134
237
  });
135
238
  }
136
239
  // Interaction
@@ -140,15 +243,26 @@ class WrSlider {
140
243
  event.preventDefault();
141
244
  const target = event.currentTarget;
142
245
  target.setPointerCapture(event.pointerId);
246
+ // `preventDefault` above also suppresses the default focus, so the arrows
247
+ // did nothing after a mouse drag until the thumb was found again with Tab —
248
+ // while a click on the bare TRACK focused it. Synchronously, not through
249
+ // `focusThumb`: `event.currentTarget` is already the thumb, so that
250
+ // helper's `queueMicrotask` would only defer a focus nothing is waiting on.
251
+ target.focus();
143
252
  const move = (e) => this.updateFromEvent(e, thumb);
144
- const up = (e) => {
253
+ const cleanup = (e) => {
145
254
  target.releasePointerCapture(e.pointerId);
146
255
  target.removeEventListener('pointermove', move);
147
- target.removeEventListener('pointerup', up);
256
+ target.removeEventListener('pointerup', cleanup);
257
+ target.removeEventListener('pointercancel', cleanup);
148
258
  this.touch.emit();
149
259
  };
150
260
  target.addEventListener('pointermove', move);
151
- target.addEventListener('pointerup', up);
261
+ target.addEventListener('pointerup', cleanup);
262
+ // `pointercancel` is never followed by a `pointerup`, so without this the
263
+ // move listener outlived the drag: the thumb kept following a pointer that
264
+ // was merely hovering over it, with no button held.
265
+ target.addEventListener('pointercancel', cleanup);
152
266
  this.updateFromEvent(event, thumb);
153
267
  }
154
268
  onTrackPointerDown(event) {
@@ -199,6 +313,21 @@ class WrSlider {
199
313
  this.emitChange();
200
314
  this.touch.emit();
201
315
  }
316
+ /**
317
+ * A bound field learns it may show its validation copy from `touch`, and
318
+ * pointer-up plus a value-changing keypress cannot be the only sources: a
319
+ * slider tabbed into and out of without an arrow press left the field
320
+ * untouched forever — which is exactly the case `touched` exists for, and
321
+ * what this output has always documented itself as doing. `wr-knob` and
322
+ * `wr-rating` bind `(blur)` on the one element they own; this component has
323
+ * two thumbs, so it listens for the bubbling `focusout` on its host instead
324
+ * — and ignores the one that only moves focus from one thumb to the other.
325
+ */
326
+ onFocusOut(event) {
327
+ if (this.track().nativeElement.contains(event.relatedTarget))
328
+ return;
329
+ this.touch.emit();
330
+ }
202
331
  // Internals
203
332
  isRtl() {
204
333
  return this.dir?.value === 'rtl';
@@ -268,13 +397,13 @@ class WrSlider {
268
397
  const value = this.range() ? [this.low(), this.high()] : this.low();
269
398
  this.value.set(value);
270
399
  }
271
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrSlider, deps: [], target: i0.ɵɵFactoryTarget.Component });
272
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: WrSlider, isStandalone: true, selector: "wr-slider", inputs: { min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, range: { classPropertyName: "range", publicName: "range", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showLabel: { classPropertyName: "showLabel", publicName: "showLabel", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch" }, host: { properties: { "class": "classes()" } }, viewQueries: [{ propertyName: "track", first: true, predicate: ["track"], descendants: true, isSignal: true }], ngImport: i0, template: "<div #track class=\"wr-slider__track\" (pointerdown)=\"onTrackPointerDown($event)\">\n <!--\n Positioned on the INLINE axis, not with `left`: `inset-inline-start` is the\n same edge in LTR and mirrors under `dir=\"rtl\"`, so the track paints itself\n the right way round without the component reading a direction to draw.\n -->\n <span class=\"wr-slider__fill\" [style.inset-inline-start.%]=\"fillStart()\" [style.width.%]=\"fillWidth()\"></span>\n\n <button\n type=\"button\"\n class=\"wr-slider__thumb wr-slider__thumb--low\"\n role=\"slider\"\n [style.inset-inline-start.%]=\"lowPercent()\"\n [disabled]=\"disabled()\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"range() ? high() : max()\"\n [attr.aria-valuenow]=\"low()\"\n [attr.aria-label]=\"range() ? 'Lower value' : 'Value'\"\n (pointerdown)=\"onPointerDown($event, 'low')\"\n (keydown)=\"onKey($event, 'low')\"\n ></button>\n\n @if (range()) {\n <button\n type=\"button\"\n class=\"wr-slider__thumb wr-slider__thumb--high\"\n role=\"slider\"\n [style.inset-inline-start.%]=\"highPercent()\"\n [disabled]=\"disabled()\"\n [attr.aria-valuemin]=\"low()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-valuenow]=\"high()\"\n aria-label=\"Upper value\"\n (pointerdown)=\"onPointerDown($event, 'high')\"\n (keydown)=\"onKey($event, 'high')\"\n ></button>\n }\n</div>\n\n@if (showLabel()) {\n <div class=\"wr-slider__label\">{{ label() }}</div>\n}\n", encapsulation: i0.ViewEncapsulation.None });
400
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrSlider, deps: [], target: i0.ɵɵFactoryTarget.Component });
401
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: WrSlider, isStandalone: true, selector: "wr-slider", inputs: { min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, range: { classPropertyName: "range", publicName: "range", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showLabel: { classPropertyName: "showLabel", publicName: "showLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, upperLabel: { classPropertyName: "upperLabel", publicName: "upperLabel", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch" }, host: { listeners: { "focusout": "onFocusOut($event)" }, properties: { "class": "classes()" } }, viewQueries: [{ propertyName: "track", first: true, predicate: ["track"], descendants: true, isSignal: true }], ngImport: i0, template: "<div #track class=\"wr-slider__track\" (pointerdown)=\"onTrackPointerDown($event)\">\n <!--\n Positioned on the INLINE axis, not with `left`: `inset-inline-start` is the\n same edge in LTR and mirrors under `dir=\"rtl\"`, so the track paints itself\n the right way round without the component reading a direction to draw.\n -->\n <span class=\"wr-slider__fill\" [style.inset-inline-start.%]=\"fillStart()\" [style.width.%]=\"fillWidth()\"></span>\n\n <button\n type=\"button\"\n class=\"wr-slider__thumb wr-slider__thumb--low\"\n role=\"slider\"\n [attr.id]=\"thumbId()\"\n [style.inset-inline-start.%]=\"lowPercent()\"\n [disabled]=\"disabled()\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"range() ? high() : max()\"\n [attr.aria-valuenow]=\"low()\"\n [attr.aria-label]=\"resolvedLowLabel()\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"ariaInvalid()\"\n (pointerdown)=\"onPointerDown($event, 'low')\"\n (keydown)=\"onKey($event, 'low')\"\n ></button>\n\n @if (range()) {\n <button\n type=\"button\"\n class=\"wr-slider__thumb wr-slider__thumb--high\"\n role=\"slider\"\n [style.inset-inline-start.%]=\"highPercent()\"\n [disabled]=\"disabled()\"\n [attr.aria-valuemin]=\"low()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-valuenow]=\"high()\"\n [attr.aria-label]=\"resolvedUpperLabel()\"\n (pointerdown)=\"onPointerDown($event, 'high')\"\n (keydown)=\"onKey($event, 'high')\"\n ></button>\n }\n</div>\n\n@if (showLabel()) {\n <div class=\"wr-slider__label\">{{ label() }}</div>\n}\n", encapsulation: i0.ViewEncapsulation.None });
273
402
  }
274
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrSlider, decorators: [{
403
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrSlider, decorators: [{
275
404
  type: Component,
276
- args: [{ selector: 'wr-slider', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, template: "<div #track class=\"wr-slider__track\" (pointerdown)=\"onTrackPointerDown($event)\">\n <!--\n Positioned on the INLINE axis, not with `left`: `inset-inline-start` is the\n same edge in LTR and mirrors under `dir=\"rtl\"`, so the track paints itself\n the right way round without the component reading a direction to draw.\n -->\n <span class=\"wr-slider__fill\" [style.inset-inline-start.%]=\"fillStart()\" [style.width.%]=\"fillWidth()\"></span>\n\n <button\n type=\"button\"\n class=\"wr-slider__thumb wr-slider__thumb--low\"\n role=\"slider\"\n [style.inset-inline-start.%]=\"lowPercent()\"\n [disabled]=\"disabled()\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"range() ? high() : max()\"\n [attr.aria-valuenow]=\"low()\"\n [attr.aria-label]=\"range() ? 'Lower value' : 'Value'\"\n (pointerdown)=\"onPointerDown($event, 'low')\"\n (keydown)=\"onKey($event, 'low')\"\n ></button>\n\n @if (range()) {\n <button\n type=\"button\"\n class=\"wr-slider__thumb wr-slider__thumb--high\"\n role=\"slider\"\n [style.inset-inline-start.%]=\"highPercent()\"\n [disabled]=\"disabled()\"\n [attr.aria-valuemin]=\"low()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-valuenow]=\"high()\"\n aria-label=\"Upper value\"\n (pointerdown)=\"onPointerDown($event, 'high')\"\n (keydown)=\"onKey($event, 'high')\"\n ></button>\n }\n</div>\n\n@if (showLabel()) {\n <div class=\"wr-slider__label\">{{ label() }}</div>\n}\n" }]
277
- }], ctorParameters: () => [], propDecorators: { min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], range: [{ type: i0.Input, args: [{ isSignal: true, alias: "range", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLabel", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touch: [{ type: i0.Output, args: ["touch"] }], track: [{ type: i0.ViewChild, args: ['track', { isSignal: true }] }] } });
405
+ args: [{ selector: 'wr-slider', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()', '(focusout)': 'onFocusOut($event)' }, template: "<div #track class=\"wr-slider__track\" (pointerdown)=\"onTrackPointerDown($event)\">\n <!--\n Positioned on the INLINE axis, not with `left`: `inset-inline-start` is the\n same edge in LTR and mirrors under `dir=\"rtl\"`, so the track paints itself\n the right way round without the component reading a direction to draw.\n -->\n <span class=\"wr-slider__fill\" [style.inset-inline-start.%]=\"fillStart()\" [style.width.%]=\"fillWidth()\"></span>\n\n <button\n type=\"button\"\n class=\"wr-slider__thumb wr-slider__thumb--low\"\n role=\"slider\"\n [attr.id]=\"thumbId()\"\n [style.inset-inline-start.%]=\"lowPercent()\"\n [disabled]=\"disabled()\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"range() ? high() : max()\"\n [attr.aria-valuenow]=\"low()\"\n [attr.aria-label]=\"resolvedLowLabel()\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"ariaInvalid()\"\n (pointerdown)=\"onPointerDown($event, 'low')\"\n (keydown)=\"onKey($event, 'low')\"\n ></button>\n\n @if (range()) {\n <button\n type=\"button\"\n class=\"wr-slider__thumb wr-slider__thumb--high\"\n role=\"slider\"\n [style.inset-inline-start.%]=\"highPercent()\"\n [disabled]=\"disabled()\"\n [attr.aria-valuemin]=\"low()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-valuenow]=\"high()\"\n [attr.aria-label]=\"resolvedUpperLabel()\"\n (pointerdown)=\"onPointerDown($event, 'high')\"\n (keydown)=\"onKey($event, 'high')\"\n ></button>\n }\n</div>\n\n@if (showLabel()) {\n <div class=\"wr-slider__label\">{{ label() }}</div>\n}\n" }]
406
+ }], ctorParameters: () => [], propDecorators: { min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], range: [{ type: i0.Input, args: [{ isSignal: true, alias: "range", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLabel", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], upperLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "upperLabel", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touch: [{ type: i0.Output, args: ["touch"] }], track: [{ type: i0.ViewChild, args: ['track', { isSignal: true }] }] } });
278
407
 
279
408
  /**
280
409
  * Generated bundle index. Do not edit.