ngwr 6.1.1 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (609) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +179 -32
  3. package/affix/styles/_index.scss +12 -0
  4. package/alert/styles/_index.scss +75 -32
  5. package/anchor/styles/_index.scss +59 -0
  6. package/aurora/aurora.scss +1 -0
  7. package/aurora/styles/_index.scss +30 -0
  8. package/avatar/styles/_index.scss +30 -14
  9. package/back-top/styles/_index.scss +42 -0
  10. package/badge/styles/_index.scss +80 -0
  11. package/badge/styles/_tag.scss +81 -0
  12. package/bar-chart/styles/_index.scss +56 -0
  13. package/blur-text/blur-text.scss +1 -0
  14. package/blur-text/styles/_index.scss +14 -0
  15. package/border-glow/border-glow.scss +4 -0
  16. package/border-glow/styles/_index.scss +228 -0
  17. package/breadcrumbs/breadcrumbs.scss +1 -0
  18. package/breadcrumbs/styles/_index.scss +76 -0
  19. package/burger/styles/_index.scss +101 -0
  20. package/button/styles/_index.scss +148 -125
  21. package/calendar/styles/_index.scss +252 -0
  22. package/calendar-heatmap/styles/_index.scss +69 -0
  23. package/card/card.scss +1 -0
  24. package/card/styles/_index.scss +96 -0
  25. package/carousel/styles/_index.scss +102 -0
  26. package/cascader/cascader.scss +1 -0
  27. package/cascader/styles/_index.scss +174 -0
  28. package/checkbox/styles/_index.scss +76 -60
  29. package/circular-text/circular-text.scss +1 -0
  30. package/circular-text/styles/_index.scss +45 -0
  31. package/click-spark/click-spark.scss +1 -0
  32. package/click-spark/styles/_index.scss +16 -0
  33. package/collapse/styles/_index.scss +92 -0
  34. package/color-picker/styles/_index.scss +247 -0
  35. package/command-palette/styles/_index.scss +188 -0
  36. package/compare/styles/_index.scss +100 -0
  37. package/context-menu/styles/_index.scss +112 -0
  38. package/counter/styles/_index.scss +44 -0
  39. package/date-picker/styles/_index.scss +204 -0
  40. package/decrypt-text/decrypt-text.scss +1 -0
  41. package/decrypt-text/styles/_index.scss +26 -0
  42. package/density/styles/_index.scss +37 -0
  43. package/descriptions/styles/_index.scss +104 -0
  44. package/dialog/styles/_index.scss +101 -27
  45. package/divider/styles/_index.scss +43 -24
  46. package/donut-chart/styles/_index.scss +76 -0
  47. package/drag-drop/sortable-list.scss +1 -0
  48. package/drag-drop/styles/_index.scss +99 -0
  49. package/drawer/styles/_index.scss +164 -0
  50. package/dropdown/styles/_index.scss +76 -56
  51. package/empty/styles/_index.scss +49 -0
  52. package/falling-text/falling-text.scss +1 -0
  53. package/falling-text/styles/_index.scss +30 -0
  54. package/fesm2022/ngwr-affix.mjs +102 -0
  55. package/fesm2022/ngwr-affix.mjs.map +1 -0
  56. package/fesm2022/ngwr-alert.mjs +70 -52
  57. package/fesm2022/ngwr-alert.mjs.map +1 -1
  58. package/fesm2022/ngwr-anchor.mjs +103 -0
  59. package/fesm2022/ngwr-anchor.mjs.map +1 -0
  60. package/fesm2022/ngwr-aurora.mjs +309 -0
  61. package/fesm2022/ngwr-aurora.mjs.map +1 -0
  62. package/fesm2022/ngwr-avatar.mjs +81 -71
  63. package/fesm2022/ngwr-avatar.mjs.map +1 -1
  64. package/fesm2022/ngwr-back-top.mjs +81 -0
  65. package/fesm2022/ngwr-back-top.mjs.map +1 -0
  66. package/fesm2022/ngwr-badge.mjs +192 -0
  67. package/fesm2022/ngwr-badge.mjs.map +1 -0
  68. package/fesm2022/ngwr-bar-chart.mjs +69 -0
  69. package/fesm2022/ngwr-bar-chart.mjs.map +1 -0
  70. package/fesm2022/ngwr-blur-text.mjs +181 -0
  71. package/fesm2022/ngwr-blur-text.mjs.map +1 -0
  72. package/fesm2022/ngwr-border-glow.mjs +278 -0
  73. package/fesm2022/ngwr-border-glow.mjs.map +1 -0
  74. package/fesm2022/ngwr-breadcrumbs.mjs +101 -0
  75. package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -0
  76. package/fesm2022/ngwr-burger.mjs +60 -0
  77. package/fesm2022/ngwr-burger.mjs.map +1 -0
  78. package/fesm2022/ngwr-button.mjs +179 -104
  79. package/fesm2022/ngwr-button.mjs.map +1 -1
  80. package/fesm2022/ngwr-calendar-heatmap.mjs +145 -0
  81. package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -0
  82. package/fesm2022/ngwr-calendar.mjs +402 -0
  83. package/fesm2022/ngwr-calendar.mjs.map +1 -0
  84. package/fesm2022/ngwr-card.mjs +100 -0
  85. package/fesm2022/ngwr-card.mjs.map +1 -0
  86. package/fesm2022/ngwr-carousel.mjs +128 -0
  87. package/fesm2022/ngwr-carousel.mjs.map +1 -0
  88. package/fesm2022/ngwr-cascader.mjs +280 -0
  89. package/fesm2022/ngwr-cascader.mjs.map +1 -0
  90. package/fesm2022/ngwr-checkbox.mjs +214 -54
  91. package/fesm2022/ngwr-checkbox.mjs.map +1 -1
  92. package/fesm2022/ngwr-circular-text.mjs +168 -0
  93. package/fesm2022/ngwr-circular-text.mjs.map +1 -0
  94. package/fesm2022/ngwr-click-spark.mjs +163 -0
  95. package/fesm2022/ngwr-click-spark.mjs.map +1 -0
  96. package/fesm2022/ngwr-clipboard.mjs +130 -0
  97. package/fesm2022/ngwr-clipboard.mjs.map +1 -0
  98. package/fesm2022/ngwr-collapse.mjs +232 -0
  99. package/fesm2022/ngwr-collapse.mjs.map +1 -0
  100. package/fesm2022/ngwr-color-picker.mjs +646 -0
  101. package/fesm2022/ngwr-color-picker.mjs.map +1 -0
  102. package/fesm2022/ngwr-command-palette.mjs +259 -0
  103. package/fesm2022/ngwr-command-palette.mjs.map +1 -0
  104. package/fesm2022/ngwr-compare.mjs +137 -0
  105. package/fesm2022/ngwr-compare.mjs.map +1 -0
  106. package/fesm2022/ngwr-confetti.mjs +160 -0
  107. package/fesm2022/ngwr-confetti.mjs.map +1 -0
  108. package/fesm2022/ngwr-context-menu.mjs +653 -0
  109. package/fesm2022/ngwr-context-menu.mjs.map +1 -0
  110. package/fesm2022/ngwr-cookie.mjs +127 -0
  111. package/fesm2022/ngwr-cookie.mjs.map +1 -0
  112. package/fesm2022/ngwr-counter.mjs +415 -0
  113. package/fesm2022/ngwr-counter.mjs.map +1 -0
  114. package/fesm2022/ngwr-date-adapter-fns.mjs +180 -0
  115. package/fesm2022/ngwr-date-adapter-fns.mjs.map +1 -0
  116. package/fesm2022/ngwr-date-adapter-luxon.mjs +201 -0
  117. package/fesm2022/ngwr-date-adapter-luxon.mjs.map +1 -0
  118. package/fesm2022/ngwr-date-adapter.mjs +419 -0
  119. package/fesm2022/ngwr-date-adapter.mjs.map +1 -0
  120. package/fesm2022/ngwr-date-picker.mjs +624 -0
  121. package/fesm2022/ngwr-date-picker.mjs.map +1 -0
  122. package/fesm2022/ngwr-decrypt-text.mjs +396 -0
  123. package/fesm2022/ngwr-decrypt-text.mjs.map +1 -0
  124. package/fesm2022/ngwr-density.mjs +157 -0
  125. package/fesm2022/ngwr-density.mjs.map +1 -0
  126. package/fesm2022/ngwr-descriptions.mjs +83 -0
  127. package/fesm2022/ngwr-descriptions.mjs.map +1 -0
  128. package/fesm2022/ngwr-dialog.mjs +249 -377
  129. package/fesm2022/ngwr-dialog.mjs.map +1 -1
  130. package/fesm2022/ngwr-directives.mjs +315 -0
  131. package/fesm2022/ngwr-directives.mjs.map +1 -0
  132. package/fesm2022/ngwr-divider.mjs +74 -59
  133. package/fesm2022/ngwr-divider.mjs.map +1 -1
  134. package/fesm2022/ngwr-donut-chart.mjs +131 -0
  135. package/fesm2022/ngwr-donut-chart.mjs.map +1 -0
  136. package/fesm2022/ngwr-drag-drop.mjs +117 -0
  137. package/fesm2022/ngwr-drag-drop.mjs.map +1 -0
  138. package/fesm2022/ngwr-drawer.mjs +315 -0
  139. package/fesm2022/ngwr-drawer.mjs.map +1 -0
  140. package/fesm2022/ngwr-dropdown.mjs +276 -229
  141. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  142. package/fesm2022/ngwr-empty.mjs +57 -0
  143. package/fesm2022/ngwr-empty.mjs.map +1 -0
  144. package/fesm2022/ngwr-falling-text.mjs +316 -0
  145. package/fesm2022/ngwr-falling-text.mjs.map +1 -0
  146. package/fesm2022/ngwr-file-upload.mjs +260 -0
  147. package/fesm2022/ngwr-file-upload.mjs.map +1 -0
  148. package/fesm2022/ngwr-form.mjs +133 -48
  149. package/fesm2022/ngwr-form.mjs.map +1 -1
  150. package/fesm2022/ngwr-fuzzy-text.mjs +373 -0
  151. package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -0
  152. package/fesm2022/ngwr-gauge.mjs +83 -0
  153. package/fesm2022/ngwr-gauge.mjs.map +1 -0
  154. package/fesm2022/ngwr-glitch-text.mjs +67 -0
  155. package/fesm2022/ngwr-glitch-text.mjs.map +1 -0
  156. package/fesm2022/ngwr-gradient-text.mjs +109 -0
  157. package/fesm2022/ngwr-gradient-text.mjs.map +1 -0
  158. package/fesm2022/ngwr-hotkey.mjs +263 -0
  159. package/fesm2022/ngwr-hotkey.mjs.map +1 -0
  160. package/fesm2022/ngwr-i18n-en.mjs +168 -0
  161. package/fesm2022/ngwr-i18n-en.mjs.map +1 -0
  162. package/fesm2022/ngwr-i18n-ru.mjs +156 -0
  163. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -0
  164. package/fesm2022/ngwr-i18n.mjs +478 -0
  165. package/fesm2022/ngwr-i18n.mjs.map +1 -0
  166. package/fesm2022/ngwr-icon-adapters-feather.mjs +74 -0
  167. package/fesm2022/ngwr-icon-adapters-feather.mjs.map +1 -0
  168. package/fesm2022/ngwr-icon-adapters-lucide.mjs +88 -0
  169. package/fesm2022/ngwr-icon-adapters-lucide.mjs.map +1 -0
  170. package/fesm2022/ngwr-icon.mjs +189 -649
  171. package/fesm2022/ngwr-icon.mjs.map +1 -1
  172. package/fesm2022/ngwr-image-cropper.mjs +299 -0
  173. package/fesm2022/ngwr-image-cropper.mjs.map +1 -0
  174. package/fesm2022/ngwr-input-number.mjs +239 -0
  175. package/fesm2022/ngwr-input-number.mjs.map +1 -0
  176. package/fesm2022/ngwr-input-otp.mjs +216 -0
  177. package/fesm2022/ngwr-input-otp.mjs.map +1 -0
  178. package/fesm2022/ngwr-input.mjs +183 -104
  179. package/fesm2022/ngwr-input.mjs.map +1 -1
  180. package/fesm2022/ngwr-keyboard.mjs +47 -0
  181. package/fesm2022/ngwr-keyboard.mjs.map +1 -0
  182. package/fesm2022/ngwr-knob.mjs +227 -0
  183. package/fesm2022/ngwr-knob.mjs.map +1 -0
  184. package/fesm2022/ngwr-layout.mjs +197 -0
  185. package/fesm2022/ngwr-layout.mjs.map +1 -0
  186. package/fesm2022/ngwr-lightbox.mjs +131 -0
  187. package/fesm2022/ngwr-lightbox.mjs.map +1 -0
  188. package/fesm2022/ngwr-line-chart.mjs +176 -0
  189. package/fesm2022/ngwr-line-chart.mjs.map +1 -0
  190. package/fesm2022/ngwr-list.mjs +132 -0
  191. package/fesm2022/ngwr-list.mjs.map +1 -0
  192. package/fesm2022/ngwr-loading-bar.mjs +160 -0
  193. package/fesm2022/ngwr-loading-bar.mjs.map +1 -0
  194. package/fesm2022/ngwr-marquee.mjs +224 -0
  195. package/fesm2022/ngwr-marquee.mjs.map +1 -0
  196. package/fesm2022/ngwr-media.mjs +132 -0
  197. package/fesm2022/ngwr-media.mjs.map +1 -0
  198. package/fesm2022/ngwr-mention.mjs +374 -0
  199. package/fesm2022/ngwr-mention.mjs.map +1 -0
  200. package/fesm2022/ngwr-meta.mjs +323 -0
  201. package/fesm2022/ngwr-meta.mjs.map +1 -0
  202. package/fesm2022/ngwr-meter-group.mjs +73 -0
  203. package/fesm2022/ngwr-meter-group.mjs.map +1 -0
  204. package/fesm2022/ngwr-overlay.mjs +145 -0
  205. package/fesm2022/ngwr-overlay.mjs.map +1 -0
  206. package/fesm2022/ngwr-page-header.mjs +53 -0
  207. package/fesm2022/ngwr-page-header.mjs.map +1 -0
  208. package/fesm2022/ngwr-pagination.mjs +152 -96
  209. package/fesm2022/ngwr-pagination.mjs.map +1 -1
  210. package/fesm2022/ngwr-pipes.mjs +290 -11
  211. package/fesm2022/ngwr-pipes.mjs.map +1 -1
  212. package/fesm2022/ngwr-platform.mjs +63 -0
  213. package/fesm2022/ngwr-platform.mjs.map +1 -0
  214. package/fesm2022/ngwr-popconfirm.mjs +188 -0
  215. package/fesm2022/ngwr-popconfirm.mjs.map +1 -0
  216. package/fesm2022/ngwr-popover.mjs +365 -0
  217. package/fesm2022/ngwr-popover.mjs.map +1 -0
  218. package/fesm2022/ngwr-progress.mjs +47 -39
  219. package/fesm2022/ngwr-progress.mjs.map +1 -1
  220. package/fesm2022/ngwr-qr.mjs +160 -179
  221. package/fesm2022/ngwr-qr.mjs.map +1 -1
  222. package/fesm2022/ngwr-radio.mjs +204 -0
  223. package/fesm2022/ngwr-radio.mjs.map +1 -0
  224. package/fesm2022/ngwr-rating.mjs +180 -0
  225. package/fesm2022/ngwr-rating.mjs.map +1 -0
  226. package/fesm2022/ngwr-result.mjs +119 -0
  227. package/fesm2022/ngwr-result.mjs.map +1 -0
  228. package/fesm2022/ngwr-rotating-text.mjs +256 -0
  229. package/fesm2022/ngwr-rotating-text.mjs.map +1 -0
  230. package/fesm2022/ngwr-scramble-text.mjs +165 -0
  231. package/fesm2022/ngwr-scramble-text.mjs.map +1 -0
  232. package/fesm2022/ngwr-scroll.mjs +107 -0
  233. package/fesm2022/ngwr-scroll.mjs.map +1 -0
  234. package/fesm2022/ngwr-segmented.mjs +96 -0
  235. package/fesm2022/ngwr-segmented.mjs.map +1 -0
  236. package/fesm2022/ngwr-select.mjs +844 -309
  237. package/fesm2022/ngwr-select.mjs.map +1 -1
  238. package/fesm2022/ngwr-shiny-text.mjs +127 -0
  239. package/fesm2022/ngwr-shiny-text.mjs.map +1 -0
  240. package/fesm2022/ngwr-sidebar.mjs +134 -0
  241. package/fesm2022/ngwr-sidebar.mjs.map +1 -0
  242. package/fesm2022/ngwr-skeleton.mjs +59 -34
  243. package/fesm2022/ngwr-skeleton.mjs.map +1 -1
  244. package/fesm2022/ngwr-slider.mjs +234 -0
  245. package/fesm2022/ngwr-slider.mjs.map +1 -0
  246. package/fesm2022/ngwr-sparkline.mjs +104 -0
  247. package/fesm2022/ngwr-sparkline.mjs.map +1 -0
  248. package/fesm2022/ngwr-speed-dial.mjs +79 -0
  249. package/fesm2022/ngwr-speed-dial.mjs.map +1 -0
  250. package/fesm2022/ngwr-spinner.mjs +34 -28
  251. package/fesm2022/ngwr-spinner.mjs.map +1 -1
  252. package/fesm2022/ngwr-splash-cursor.mjs +970 -0
  253. package/fesm2022/ngwr-splash-cursor.mjs.map +1 -0
  254. package/fesm2022/ngwr-split-text.mjs +200 -0
  255. package/fesm2022/ngwr-split-text.mjs.map +1 -0
  256. package/fesm2022/ngwr-splitter.mjs +125 -0
  257. package/fesm2022/ngwr-splitter.mjs.map +1 -0
  258. package/fesm2022/ngwr-spotlight-card.mjs +138 -0
  259. package/fesm2022/ngwr-spotlight-card.mjs.map +1 -0
  260. package/fesm2022/ngwr-squircle.mjs +320 -0
  261. package/fesm2022/ngwr-squircle.mjs.map +1 -0
  262. package/fesm2022/ngwr-star-border.mjs +72 -0
  263. package/fesm2022/ngwr-star-border.mjs.map +1 -0
  264. package/fesm2022/ngwr-statistic.mjs +220 -0
  265. package/fesm2022/ngwr-statistic.mjs.map +1 -0
  266. package/fesm2022/ngwr-stepper.mjs +186 -0
  267. package/fesm2022/ngwr-stepper.mjs.map +1 -0
  268. package/fesm2022/ngwr-storage.mjs +317 -0
  269. package/fesm2022/ngwr-storage.mjs.map +1 -0
  270. package/fesm2022/ngwr-switch.mjs +99 -0
  271. package/fesm2022/ngwr-switch.mjs.map +1 -0
  272. package/fesm2022/ngwr-table.mjs +217 -206
  273. package/fesm2022/ngwr-table.mjs.map +1 -1
  274. package/fesm2022/ngwr-tabs.mjs +217 -0
  275. package/fesm2022/ngwr-tabs.mjs.map +1 -0
  276. package/fesm2022/ngwr-textarea.mjs +106 -120
  277. package/fesm2022/ngwr-textarea.mjs.map +1 -1
  278. package/fesm2022/ngwr-theme.mjs +152 -0
  279. package/fesm2022/ngwr-theme.mjs.map +1 -0
  280. package/fesm2022/ngwr-tilt-card.mjs +137 -0
  281. package/fesm2022/ngwr-tilt-card.mjs.map +1 -0
  282. package/fesm2022/ngwr-timeline.mjs +73 -0
  283. package/fesm2022/ngwr-timeline.mjs.map +1 -0
  284. package/fesm2022/ngwr-toast.mjs +446 -0
  285. package/fesm2022/ngwr-toast.mjs.map +1 -0
  286. package/fesm2022/ngwr-toolbar.mjs +46 -0
  287. package/fesm2022/ngwr-toolbar.mjs.map +1 -0
  288. package/fesm2022/ngwr-tree.mjs +483 -0
  289. package/fesm2022/ngwr-tree.mjs.map +1 -0
  290. package/fesm2022/ngwr-typewriter.mjs +229 -0
  291. package/fesm2022/ngwr-typewriter.mjs.map +1 -0
  292. package/fesm2022/ngwr-typography.mjs +79 -0
  293. package/fesm2022/ngwr-typography.mjs.map +1 -0
  294. package/fesm2022/ngwr-utils.mjs +535 -0
  295. package/fesm2022/ngwr-utils.mjs.map +1 -0
  296. package/fesm2022/ngwr-validators.mjs +214 -0
  297. package/fesm2022/ngwr-validators.mjs.map +1 -0
  298. package/fesm2022/ngwr-version.mjs +51 -0
  299. package/fesm2022/ngwr-version.mjs.map +1 -0
  300. package/fesm2022/ngwr-virtual-scroll.mjs +99 -0
  301. package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -0
  302. package/fesm2022/ngwr-waves.mjs +356 -0
  303. package/fesm2022/ngwr-waves.mjs.map +1 -0
  304. package/fesm2022/ngwr-window.mjs +1141 -0
  305. package/fesm2022/ngwr-window.mjs.map +1 -0
  306. package/fesm2022/ngwr.mjs +0 -3
  307. package/fesm2022/ngwr.mjs.map +1 -1
  308. package/file-upload/styles/_index.scss +152 -0
  309. package/form/styles/_index.scss +85 -42
  310. package/gauge/styles/_index.scss +37 -0
  311. package/glitch-text/glitch-text.scss +1 -0
  312. package/glitch-text/styles/_index.scss +144 -0
  313. package/gradient-text/gradient-text.scss +1 -0
  314. package/gradient-text/styles/_index.scss +113 -0
  315. package/icon/styles/_index.scss +21 -15
  316. package/image-cropper/styles/_index.scss +114 -0
  317. package/input/styles/_index.scss +133 -93
  318. package/input-number/styles/_index.scss +75 -0
  319. package/input-otp/styles/_index.scss +53 -0
  320. package/keyboard/styles/_index.scss +55 -0
  321. package/knob/styles/_index.scss +54 -0
  322. package/layout/styles/_index.scss +65 -0
  323. package/lightbox/lightbox.scss +1 -0
  324. package/lightbox/styles/_index.scss +144 -0
  325. package/line-chart/styles/_index.scss +93 -0
  326. package/list/list.scss +1 -0
  327. package/list/styles/_index.scss +101 -0
  328. package/loading-bar/loading-bar.scss +1 -0
  329. package/loading-bar/styles/_index.scss +29 -0
  330. package/marquee/marquee.scss +1 -0
  331. package/marquee/styles/_index.scss +138 -0
  332. package/mention/styles/_index.scss +47 -0
  333. package/meter-group/styles/_index.scss +61 -0
  334. package/package.json +586 -104
  335. package/page-header/styles/_index.scss +78 -0
  336. package/pagination/styles/_index.scss +89 -34
  337. package/popconfirm/styles/_index.scss +32 -0
  338. package/popover/styles/_index.scss +99 -0
  339. package/progress/styles/_index.scss +29 -14
  340. package/qr/styles/_index.scss +6 -3
  341. package/radio/styles/_index.scss +111 -0
  342. package/rating/styles/_index.scss +63 -0
  343. package/result/styles/_index.scss +69 -0
  344. package/rotating-text/rotating-text.scss +1 -0
  345. package/rotating-text/styles/_index.scss +39 -0
  346. package/schematics/collection.json +37 -0
  347. package/schematics/component-style/index.js +49 -0
  348. package/{cdk/utils/index.d.ts → schematics/component-style/schema.js} +2 -8
  349. package/schematics/component-style/schema.json +20 -0
  350. package/schematics/icon-set/index.js +96 -0
  351. package/schematics/icon-set/schema.js +8 -0
  352. package/schematics/icon-set/schema.json +39 -0
  353. package/schematics/migrations/v7/index.js +187 -0
  354. package/schematics/migrations.json +10 -0
  355. package/schematics/ng-add/index.js +180 -0
  356. package/schematics/ng-add/schema.js +8 -0
  357. package/schematics/ng-add/schema.json +80 -0
  358. package/schematics/page/index.js +63 -0
  359. package/schematics/page/schema.js +8 -0
  360. package/schematics/page/schema.json +39 -0
  361. package/schematics/page/templates.js +198 -0
  362. package/schematics/provider/index.js +126 -0
  363. package/schematics/provider/schema.js +8 -0
  364. package/schematics/provider/schema.json +52 -0
  365. package/schematics/use/index.js +143 -0
  366. package/schematics/use/schema.js +8 -0
  367. package/schematics/use/schema.json +25 -0
  368. package/schematics/use/symbol-map.json +357 -0
  369. package/scramble-text/scramble-text.scss +1 -0
  370. package/scramble-text/styles/_index.scss +8 -0
  371. package/segmented/styles/_index.scss +92 -0
  372. package/select/styles/_index.scss +245 -159
  373. package/shiny-text/shiny-text.scss +1 -0
  374. package/shiny-text/styles/_index.scss +101 -0
  375. package/sidebar/styles/_index.scss +208 -0
  376. package/skeleton/styles/_index.scss +33 -33
  377. package/slider/styles/_index.scss +77 -0
  378. package/sparkline/styles/_index.scss +9 -0
  379. package/speed-dial/styles/_index.scss +158 -0
  380. package/spinner/styles/_index.scss +38 -22
  381. package/splash-cursor/splash-cursor.scss +1 -0
  382. package/splash-cursor/styles/_index.scss +27 -0
  383. package/split-text/split-text.scss +1 -0
  384. package/split-text/styles/_index.scss +22 -0
  385. package/splitter/styles/_index.scss +85 -0
  386. package/spotlight-card/spotlight-card.scss +1 -0
  387. package/spotlight-card/styles/_index.scss +43 -0
  388. package/squircle/styles/_index.scss +34 -0
  389. package/star-border/star-border.scss +1 -0
  390. package/star-border/styles/_index.scss +118 -0
  391. package/statistic/styles/_index.scss +54 -0
  392. package/stepper/styles/_index.scss +162 -0
  393. package/styles/_animations.scss +198 -0
  394. package/styles/_breakpoints.scss +75 -0
  395. package/styles/_grid.scss +71 -0
  396. package/styles/_reset.scss +123 -0
  397. package/styles/_typography.scss +122 -0
  398. package/styles.scss +103 -13
  399. package/switch/styles/_index.scss +82 -0
  400. package/table/styles/_index.scss +255 -4
  401. package/tabs/styles/_index.scss +71 -0
  402. package/textarea/styles/_index.scss +47 -56
  403. package/theme/styles/_colors.scss +83 -0
  404. package/theme/styles/_dark.scss +85 -0
  405. package/theme/styles/_focus.scss +93 -0
  406. package/theme/styles/_index.scss +5 -0
  407. package/theme/styles/_mixins.scss +57 -0
  408. package/theme/styles/_variables.scss +118 -0
  409. package/tilt-card/styles/_index.scss +40 -0
  410. package/tilt-card/tilt-card.scss +1 -0
  411. package/timeline/styles/_index.scss +127 -0
  412. package/toast/styles/_index.scss +263 -0
  413. package/toolbar/styles/_index.scss +65 -0
  414. package/tree/styles/_index.scss +271 -0
  415. package/types/ngwr-affix.d.ts +43 -0
  416. package/types/ngwr-alert.d.ts +68 -0
  417. package/types/ngwr-anchor.d.ts +53 -0
  418. package/types/ngwr-aurora.d.ts +52 -0
  419. package/types/ngwr-avatar.d.ts +90 -0
  420. package/types/ngwr-back-top.d.ts +37 -0
  421. package/types/ngwr-badge.d.ts +158 -0
  422. package/types/ngwr-bar-chart.d.ts +47 -0
  423. package/types/ngwr-blur-text.d.ts +62 -0
  424. package/types/ngwr-border-glow.d.ts +66 -0
  425. package/types/ngwr-breadcrumbs.d.ts +61 -0
  426. package/types/ngwr-burger.d.ts +36 -0
  427. package/types/ngwr-button.d.ts +176 -0
  428. package/types/ngwr-calendar-heatmap.d.ts +58 -0
  429. package/types/ngwr-calendar.d.ts +116 -0
  430. package/types/ngwr-card.d.ts +50 -0
  431. package/types/ngwr-carousel.d.ts +57 -0
  432. package/types/ngwr-cascader.d.ts +107 -0
  433. package/types/ngwr-checkbox.d.ts +139 -0
  434. package/types/ngwr-circular-text.d.ts +49 -0
  435. package/types/ngwr-click-spark.d.ts +52 -0
  436. package/types/ngwr-clipboard.d.ts +53 -0
  437. package/types/ngwr-collapse.d.ts +139 -0
  438. package/types/ngwr-color-picker.d.ts +302 -0
  439. package/types/ngwr-command-palette.d.ts +113 -0
  440. package/types/ngwr-compare.d.ts +55 -0
  441. package/types/ngwr-confetti.d.ts +70 -0
  442. package/types/ngwr-context-menu.d.ts +207 -0
  443. package/types/ngwr-cookie.d.ts +60 -0
  444. package/types/ngwr-counter.d.ts +182 -0
  445. package/types/ngwr-date-adapter-fns.d.ts +73 -0
  446. package/types/ngwr-date-adapter-luxon.d.ts +73 -0
  447. package/types/ngwr-date-adapter.d.ts +198 -0
  448. package/types/ngwr-date-picker.d.ts +117 -0
  449. package/types/ngwr-decrypt-text.d.ts +85 -0
  450. package/types/ngwr-density.d.ts +97 -0
  451. package/types/ngwr-descriptions.d.ts +41 -0
  452. package/types/ngwr-dialog.d.ts +210 -0
  453. package/types/ngwr-directives.d.ts +125 -0
  454. package/types/ngwr-divider.d.ts +73 -0
  455. package/types/ngwr-donut-chart.d.ts +53 -0
  456. package/types/ngwr-drag-drop.d.ts +83 -0
  457. package/types/ngwr-drawer.d.ts +155 -0
  458. package/types/ngwr-dropdown.d.ts +124 -0
  459. package/types/ngwr-empty.d.ts +38 -0
  460. package/types/ngwr-falling-text.d.ts +63 -0
  461. package/types/ngwr-file-upload.d.ts +94 -0
  462. package/types/ngwr-form.d.ts +95 -0
  463. package/types/ngwr-fuzzy-text.d.ts +74 -0
  464. package/types/ngwr-gauge.d.ts +39 -0
  465. package/types/ngwr-glitch-text.d.ts +31 -0
  466. package/types/ngwr-gradient-text.d.ts +46 -0
  467. package/types/ngwr-hotkey.d.ts +142 -0
  468. package/types/ngwr-i18n-en.d.ts +18 -0
  469. package/types/ngwr-i18n-ru.d.ts +6 -0
  470. package/types/ngwr-i18n.d.ts +336 -0
  471. package/types/ngwr-icon-adapters-feather.d.ts +44 -0
  472. package/types/ngwr-icon-adapters-lucide.d.ts +54 -0
  473. package/types/ngwr-icon.d.ts +180 -0
  474. package/types/ngwr-image-cropper.d.ts +110 -0
  475. package/types/ngwr-input-number.d.ts +77 -0
  476. package/types/ngwr-input-otp.d.ts +78 -0
  477. package/types/ngwr-input.d.ts +131 -0
  478. package/types/ngwr-keyboard.d.ts +34 -0
  479. package/types/ngwr-knob.d.ts +77 -0
  480. package/types/ngwr-layout.d.ts +89 -0
  481. package/types/ngwr-lightbox.d.ts +50 -0
  482. package/types/ngwr-line-chart.d.ts +90 -0
  483. package/types/ngwr-list.d.ts +71 -0
  484. package/types/ngwr-loading-bar.d.ts +79 -0
  485. package/types/ngwr-marquee.d.ts +95 -0
  486. package/types/ngwr-media.d.ts +74 -0
  487. package/types/ngwr-mention.d.ts +110 -0
  488. package/types/ngwr-meta.d.ts +188 -0
  489. package/types/ngwr-meter-group.d.ts +57 -0
  490. package/types/ngwr-overlay.d.ts +110 -0
  491. package/types/ngwr-page-header.d.ts +35 -0
  492. package/types/ngwr-pagination.d.ts +119 -0
  493. package/types/ngwr-pipes.d.ts +177 -0
  494. package/types/ngwr-platform.d.ts +38 -0
  495. package/types/ngwr-popconfirm.d.ts +79 -0
  496. package/types/ngwr-popover.d.ts +90 -0
  497. package/types/ngwr-progress.d.ts +33 -0
  498. package/types/ngwr-qr.d.ts +56 -0
  499. package/types/ngwr-radio.d.ts +120 -0
  500. package/types/ngwr-rating.d.ts +62 -0
  501. package/types/ngwr-result.d.ts +64 -0
  502. package/types/ngwr-rotating-text.d.ts +72 -0
  503. package/types/ngwr-scramble-text.d.ts +49 -0
  504. package/types/ngwr-scroll.d.ts +65 -0
  505. package/types/ngwr-segmented.d.ts +65 -0
  506. package/types/ngwr-select.d.ts +413 -0
  507. package/types/ngwr-shiny-text.d.ts +70 -0
  508. package/types/ngwr-sidebar.d.ts +83 -0
  509. package/types/ngwr-skeleton.d.ts +41 -0
  510. package/types/ngwr-slider.d.ts +86 -0
  511. package/types/ngwr-sparkline.d.ts +48 -0
  512. package/types/ngwr-speed-dial.d.ts +51 -0
  513. package/types/ngwr-spinner.d.ts +41 -0
  514. package/types/ngwr-splash-cursor.d.ts +94 -0
  515. package/types/ngwr-split-text.d.ts +83 -0
  516. package/types/ngwr-splitter.d.ts +52 -0
  517. package/types/ngwr-spotlight-card.d.ts +71 -0
  518. package/types/ngwr-squircle.d.ts +132 -0
  519. package/types/ngwr-star-border.d.ts +40 -0
  520. package/types/ngwr-statistic.d.ts +88 -0
  521. package/types/ngwr-stepper.d.ts +99 -0
  522. package/types/ngwr-storage.d.ts +178 -0
  523. package/types/ngwr-switch.d.ts +39 -0
  524. package/types/ngwr-table.d.ts +237 -0
  525. package/types/ngwr-tabs.d.ts +125 -0
  526. package/types/ngwr-textarea.d.ts +60 -0
  527. package/types/ngwr-theme.d.ts +124 -0
  528. package/types/ngwr-tilt-card.d.ts +63 -0
  529. package/types/ngwr-timeline.d.ts +40 -0
  530. package/types/ngwr-toast.d.ts +230 -0
  531. package/types/ngwr-toolbar.d.ts +30 -0
  532. package/types/ngwr-tree.d.ts +150 -0
  533. package/types/ngwr-typewriter.d.ts +84 -0
  534. package/types/ngwr-typography.d.ts +56 -0
  535. package/types/ngwr-utils.d.ts +447 -0
  536. package/types/ngwr-validators.d.ts +70 -0
  537. package/types/ngwr-version.d.ts +44 -0
  538. package/types/ngwr-virtual-scroll.d.ts +64 -0
  539. package/types/ngwr-waves.d.ts +71 -0
  540. package/types/ngwr-window.d.ts +437 -0
  541. package/typewriter/styles/_index.scss +33 -0
  542. package/typewriter/typewriter.scss +1 -0
  543. package/typography/styles/_index.scss +211 -0
  544. package/virtual-scroll/styles/_index.scss +27 -0
  545. package/virtual-scroll/virtual-scroll.scss +1 -0
  546. package/waves/styles/_index.scss +24 -0
  547. package/waves/waves.scss +1 -0
  548. package/window/styles/_index.scss +430 -0
  549. package/alert/index.d.ts +0 -32
  550. package/alert/styles/theming.scss +0 -26
  551. package/alert/styles/vars.scss +0 -11
  552. package/avatar/index.d.ts +0 -30
  553. package/button/index.d.ts +0 -45
  554. package/button/styles/vars.scss +0 -13
  555. package/cdk/index.d.ts +0 -24
  556. package/cdk/rxjs/index.d.ts +0 -40
  557. package/cdk/types/index.d.ts +0 -27
  558. package/checkbox/index.d.ts +0 -31
  559. package/checkbox/styles/vars.scss +0 -16
  560. package/dialog/index.d.ts +0 -193
  561. package/dialog/styles/vars.scss +0 -7
  562. package/divider/index.d.ts +0 -16
  563. package/dropdown/index.d.ts +0 -68
  564. package/dropdown/styles/vars.scss +0 -18
  565. package/fesm2022/ngwr-cdk-rxjs.mjs +0 -53
  566. package/fesm2022/ngwr-cdk-rxjs.mjs.map +0 -1
  567. package/fesm2022/ngwr-cdk-types.mjs +0 -44
  568. package/fesm2022/ngwr-cdk-types.mjs.map +0 -1
  569. package/fesm2022/ngwr-cdk-utils.mjs +0 -30
  570. package/fesm2022/ngwr-cdk-utils.mjs.map +0 -1
  571. package/fesm2022/ngwr-cdk.mjs +0 -46
  572. package/fesm2022/ngwr-cdk.mjs.map +0 -1
  573. package/fesm2022/ngwr-tag.mjs +0 -87
  574. package/fesm2022/ngwr-tag.mjs.map +0 -1
  575. package/form/index.d.ts +0 -28
  576. package/icon/index.d.ts +0 -830
  577. package/icon/styles/vars.scss +0 -3
  578. package/input/index.d.ts +0 -43
  579. package/input/styles/vars.scss +0 -17
  580. package/pagination/index.d.ts +0 -34
  581. package/pagination/styles/vars.scss +0 -14
  582. package/pipes/index.d.ts +0 -17
  583. package/progress/index.d.ts +0 -13
  584. package/progress/styles/theming.scss +0 -8
  585. package/progress/styles/vars.scss +0 -6
  586. package/qr/index.d.ts +0 -127
  587. package/select/index.d.ts +0 -121
  588. package/select/styles/vars.scss +0 -32
  589. package/skeleton/index.d.ts +0 -13
  590. package/spinner/index.d.ts +0 -23
  591. package/styles/animations.scss +0 -92
  592. package/styles/cdk.scss +0 -5
  593. package/styles/colors.scss +0 -72
  594. package/styles/grid.scss +0 -99
  595. package/styles/maps.scss +0 -31
  596. package/styles/reset.scss +0 -112
  597. package/styles/variables.scss +0 -72
  598. package/table/index.d.ts +0 -101
  599. package/table/styles/table-filter.scss +0 -202
  600. package/table/styles/table-sort.scss +0 -47
  601. package/table/styles/table.scss +0 -232
  602. package/table/styles/vars.scss +0 -21
  603. package/tag/index.d.ts +0 -24
  604. package/tag/styles/_index.scss +0 -32
  605. package/tag/styles/theming.scss +0 -21
  606. package/tag/styles/vars.scss +0 -12
  607. package/textarea/index.d.ts +0 -47
  608. package/textarea/styles/vars.scss +0 -17
  609. /package/{index.d.ts → types/ngwr.d.ts} +0 -0
@@ -1,12 +1,14 @@
1
- import { NgTemplateOutlet } from '@angular/common';
1
+ import { coerceBooleanProperty } from '@angular/cdk/coercion';
2
+ import { ScrollStrategyOptions } from '@angular/cdk/overlay';
3
+ import { TemplatePortal } from '@angular/cdk/portal';
2
4
  import * as i0 from '@angular/core';
3
- import { inject, booleanAttribute, TemplateRef, Component, ChangeDetectionStrategy, ViewEncapsulation, Input, ContentChild, HostBinding, ContentChildren, signal, computed, ElementRef, ChangeDetectorRef, numberAttribute, forwardRef, ViewChild, HostListener } from '@angular/core';
4
- import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
5
+ import { InjectionToken, input, inject, ElementRef, computed, effect, DestroyRef, ViewEncapsulation, Component, signal, viewChild, TemplateRef, ViewContainerRef, forwardRef } from '@angular/core';
6
+ import { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
5
7
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
6
- import { noop } from 'rxjs';
7
- import { WrAbstractBase } from 'ngwr/cdk';
8
- import { provideWrIcons, chevronDown, close, WrIconComponent } from 'ngwr/icon';
9
- import { WrTagComponent } from 'ngwr/tag';
8
+ import { debounceTime, switchMap, of, isObservable, from, finalize } from 'rxjs';
9
+ import { useI18nText, useI18nFormatter } from 'ngwr/i18n';
10
+ import { WR_OVERLAY } from 'ngwr/overlay';
11
+ import { noop } from 'ngwr/utils';
10
12
 
11
13
  /**
12
14
  * @license
@@ -14,64 +16,13 @@ import { WrTagComponent } from 'ngwr/tag';
14
16
  * Use of this source code is governed by an MIT-style license that can be
15
17
  * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
16
18
  */
17
- class WrOptionComponent {
18
- constructor() {
19
- this.disabled = false;
20
- this.context = {
21
- $implicit: this,
22
- option: this,
23
- };
24
- this.selectComponent = inject(WrSelectComponent);
25
- }
26
- get hostClasses() {
27
- return {
28
- 'wr-option': true,
29
- 'wr-option--selected': this.isSelected,
30
- 'wr-option--disabled': this.disabled,
31
- 'wr-option--highlighted': this.isHighlighted,
32
- 'wr-option--filtered': this.isFiltered,
33
- };
34
- }
35
- get isSelected() {
36
- const currentValue = this.selectComponent.currentValue;
37
- if (this.selectComponent.isMultiple) {
38
- return Array.isArray(currentValue) && currentValue.includes(this.value);
39
- }
40
- return currentValue === this.value;
41
- }
42
- get isHighlighted() {
43
- return this.selectComponent.isHighlightedOption(this);
44
- }
45
- get isFiltered() {
46
- return this.selectComponent.isFilteredOption(this);
47
- }
48
- select() {
49
- if (!this.disabled) {
50
- this.selectComponent.onOptionSelect(this);
51
- }
52
- }
53
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.10", ngImport: i0, type: WrOptionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
54
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.10", type: WrOptionComponent, isStandalone: true, selector: "wr-option", inputs: { value: "value", label: "label", disabled: ["disabled", "disabled", booleanAttribute] }, host: { listeners: { "click": "select()" }, properties: { "class": "this.hostClasses" } }, queries: [{ propertyName: "customTemplate", first: true, predicate: TemplateRef, descendants: true }], ngImport: i0, template: "<div class=\"wr-option__content\">\n @if (customTemplate) {\n <ng-container *ngTemplateOutlet=\"customTemplate; context: context\"> </ng-container>\n } @else {\n {{ label }}\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
55
- }
56
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.10", ngImport: i0, type: WrOptionComponent, decorators: [{
57
- type: Component,
58
- args: [{ selector: 'wr-option', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
59
- '(click)': 'select()',
60
- }, imports: [NgTemplateOutlet], template: "<div class=\"wr-option__content\">\n @if (customTemplate) {\n <ng-container *ngTemplateOutlet=\"customTemplate; context: context\"> </ng-container>\n } @else {\n {{ label }}\n }\n</div>\n" }]
61
- }], propDecorators: { value: [{
62
- type: Input
63
- }], label: [{
64
- type: Input
65
- }], disabled: [{
66
- type: Input,
67
- args: [{ transform: booleanAttribute }]
68
- }], customTemplate: [{
69
- type: ContentChild,
70
- args: [TemplateRef]
71
- }], hostClasses: [{
72
- type: HostBinding,
73
- args: ['class']
74
- }] } });
19
+ /**
20
+ * Token a `<wr-option>` injects to register itself with and notify its
21
+ * parent `<wr-select>`.
22
+ *
23
+ * @internal
24
+ */
25
+ const WR_SELECT = new InjectionToken('WR_SELECT');
75
26
 
76
27
  /**
77
28
  * @license
@@ -79,40 +30,115 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.10", ngImpo
79
30
  * Use of this source code is governed by an MIT-style license that can be
80
31
  * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
81
32
  */
82
- class WrOptionGroupComponent {
33
+ let uid = 0;
34
+ /**
35
+ * Single option inside a `<wr-select>`.
36
+ *
37
+ * The option's display label is taken from its projected text content;
38
+ * the form value is its `value` input.
39
+ *
40
+ * @example
41
+ * ```html
42
+ * <wr-option value="sm">Small</wr-option>
43
+ * <wr-option [value]="42">Forty-two</wr-option>
44
+ * ```
45
+ */
46
+ class WrOption {
47
+ /** The value contributed when this option is chosen. Required. */
48
+ value = input.required(/* @ts-ignore */
49
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
50
+ /** Disable this option. @default false */
51
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
52
+ /** Stable id used for `aria-activedescendant`. */
53
+ id = `wr-option-${++uid}`;
54
+ host = inject(ElementRef);
55
+ parent = inject(WR_SELECT, { optional: true });
56
+ /**
57
+ * @internal — true when this option is currently selected. Works for
58
+ * both single and multi-select parents via `WrSelectContext.isSelected`.
59
+ */
60
+ selected = computed(() => {
61
+ const parent = this.parent;
62
+ if (!parent)
63
+ return false;
64
+ // Re-read parent's value signal so the computed recomputes on change.
65
+ parent.value();
66
+ return parent.isSelected(this.value());
67
+ }, /* @ts-ignore */
68
+ ...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
69
+ /** @internal — true when this option is the keyboard cursor target. */
70
+ active = computed(() => this.parent?.activeOptionId() === this.id, /* @ts-ignore */
71
+ ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
72
+ /**
73
+ * @internal — search mode only. True when the parent has a query that
74
+ * the option's text content does not match (case-insensitive substring).
75
+ * Hidden options stay in the DOM so registration order survives but
76
+ * collapse via CSS.
77
+ */
78
+ hidden = computed(() => {
79
+ const parent = this.parent;
80
+ if (!parent?.isSearch())
81
+ return false;
82
+ const q = parent.searchQuery().trim().toLowerCase();
83
+ if (!q)
84
+ return false;
85
+ const label = (this.host.nativeElement.textContent ?? '').trim().toLowerCase();
86
+ return !label.includes(q);
87
+ }, /* @ts-ignore */
88
+ ...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
89
+ classes = computed(() => {
90
+ const parts = ['wr-option'];
91
+ if (this.selected())
92
+ parts.push('wr-option--selected');
93
+ if (this.active())
94
+ parts.push('wr-option--active');
95
+ if (this.disabled())
96
+ parts.push('wr-option--disabled');
97
+ if (this.hidden())
98
+ parts.push('wr-option--hidden');
99
+ return parts.join(' ');
100
+ }, /* @ts-ignore */
101
+ ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
83
102
  constructor() {
84
- this.label = '';
85
- this.disabled = false;
86
- }
87
- get hostClasses() {
88
- return {
89
- 'wr-select-group': true,
90
- 'wr-select-group--disabled': this.disabled,
91
- };
92
- }
93
- ngAfterContentInit() {
94
- if (this.disabled) {
95
- this.options.forEach(option => (option.disabled = true));
103
+ if (this.parent) {
104
+ const parent = this.parent;
105
+ // Re-register when disabled or value changes so the parent has fresh metadata.
106
+ effect(onCleanup => {
107
+ const unreg = parent.registerOption({
108
+ id: this.id,
109
+ value: this.value(),
110
+ disabled: this.disabled(),
111
+ getLabel: () => this.host.nativeElement.textContent?.trim() ?? '',
112
+ });
113
+ onCleanup(() => unreg());
114
+ });
115
+ inject(DestroyRef);
96
116
  }
97
117
  }
98
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.10", ngImport: i0, type: WrOptionGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
99
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "20.3.10", type: WrOptionGroupComponent, isStandalone: true, selector: "wr-option-group", inputs: { label: "label", disabled: ["disabled", "disabled", booleanAttribute] }, host: { properties: { "class": "this.hostClasses" } }, queries: [{ propertyName: "options", predicate: WrOptionComponent }], ngImport: i0, template: "<div class=\"wr-select-group__label\">{{ label }}</div>\n<div class=\"wr-select-group__content\">\n <ng-content></ng-content>\n</div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
118
+ onClick() {
119
+ if (this.disabled() || !this.parent)
120
+ return;
121
+ this.parent.selectOption(this.value());
122
+ }
123
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: WrOption, deps: [], target: i0.ɵɵFactoryTarget.Component });
124
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.2", type: WrOption, isStandalone: true, selector: "wr-option", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "option" }, listeners: { "click": "onClick()" }, properties: { "attr.id": "id", "class": "classes()", "attr.aria-selected": "selected()", "attr.aria-disabled": "disabled() ? true : null" } }, ngImport: i0, template: '<ng-content />', isInline: true, encapsulation: i0.ViewEncapsulation.None });
100
125
  }
101
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.10", ngImport: i0, type: WrOptionGroupComponent, decorators: [{
126
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: WrOption, decorators: [{
102
127
  type: Component,
103
- args: [{ selector: 'wr-option-group', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div class=\"wr-select-group__label\">{{ label }}</div>\n<div class=\"wr-select-group__content\">\n <ng-content></ng-content>\n</div>\n" }]
104
- }], propDecorators: { label: [{
105
- type: Input
106
- }], disabled: [{
107
- type: Input,
108
- args: [{ transform: booleanAttribute }]
109
- }], options: [{
110
- type: ContentChildren,
111
- args: [WrOptionComponent]
112
- }], hostClasses: [{
113
- type: HostBinding,
114
- args: ['class']
115
- }] } });
128
+ args: [{
129
+ selector: 'wr-option',
130
+ template: '<ng-content />',
131
+ encapsulation: ViewEncapsulation.None,
132
+ host: {
133
+ role: 'option',
134
+ '[attr.id]': 'id',
135
+ '[class]': 'classes()',
136
+ '[attr.aria-selected]': 'selected()',
137
+ '[attr.aria-disabled]': 'disabled() ? true : null',
138
+ '(click)': 'onClick()',
139
+ },
140
+ }]
141
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
116
142
 
117
143
  /**
118
144
  * @license
@@ -120,275 +146,762 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.10", ngImpo
120
146
  * Use of this source code is governed by an MIT-style license that can be
121
147
  * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
122
148
  */
149
+ let listboxUid = 0;
123
150
  /**
124
- * NGWR select component.
151
+ * Combobox primitive. Renders a button that opens a CDK overlay
152
+ * containing the projected `<wr-option>` (and optionally
153
+ * `<wr-option-group>`) children. Every shape is the same component
154
+ * with a different `[mode]`:
155
+ *
156
+ * - `'single'` (default) — one value, no input. Classic dropdown.
157
+ * - `'multi'` — array value, chips on the trigger; clicking an option
158
+ * toggles instead of closing, Backspace removes the last chip.
159
+ * - `'search'` — type-ahead with sync `[options]` filter or async
160
+ * `[loader]`. Replaces the standalone `<wr-autocomplete>`.
161
+ * - `'tag'` — free-text + chips with `[separators]` / `[validate]` /
162
+ * `[allowDuplicates]`. Replaces `<wr-chips-input>`.
125
163
  *
126
- * {@tutorial} [How to use wr-select]{@link http://ngwr.dev/docs/components/select}
164
+ * Implements `ControlValueAccessor` — usable with `[(ngModel)]`,
165
+ * `formControl`, or `formControlName`.
166
+ *
167
+ * @example
168
+ * ```html
169
+ * <!-- Single -->
170
+ * <wr-select placeholder="Pick a size" [(ngModel)]="size">
171
+ * <wr-option value="sm">Small</wr-option>
172
+ * <wr-option value="md">Medium</wr-option>
173
+ * <wr-option value="lg">Large</wr-option>
174
+ * </wr-select>
175
+ *
176
+ * <!-- Multi -->
177
+ * <wr-select mode="multi" placeholder="Pick tags" [(ngModel)]="tags">
178
+ * <wr-option value="ts">TypeScript</wr-option>
179
+ * <wr-option value="ng">Angular</wr-option>
180
+ * </wr-select>
181
+ * ```
182
+ *
183
+ * The legacy `[multi]="true"` boolean is still accepted but is now an
184
+ * alias for `[mode]="'multi'"` — prefer the explicit mode.
185
+ *
186
+ * @see https://ngwr.dev/components/select
127
187
  */
128
- class WrSelectComponent extends WrAbstractBase {
129
- constructor() {
130
- super(...arguments);
131
- this.placeholder = '';
132
- this.noItemsLabel = 'No Items';
133
- this.multiple = false;
134
- this.searchable = false;
135
- this.clearable = false;
136
- this.value = signal(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
137
- this.isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
138
- this.isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : []));
139
- this.isDisabled = signal(false, ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
140
- this.searchValue = signal('', ...(ngDevMode ? [{ debugName: "searchValue" }] : []));
141
- this.highlightedOptionIndex = signal(-1, ...(ngDevMode ? [{ debugName: "highlightedOptionIndex" }] : []));
142
- this.selectedOptions = computed(() => {
143
- const currentValue = this.value();
144
- const optionsArray = this.options?.toArray() || [];
145
- if (this.multiple) {
146
- return optionsArray.filter(option => Array.isArray(currentValue) && currentValue.includes(option.value));
147
- }
148
- return optionsArray.filter(option => option.value === currentValue);
149
- }, ...(ngDevMode ? [{ debugName: "selectedOptions" }] : []));
150
- this.selectedLabel = computed(() => {
151
- const selected = this.selectedOptions();
152
- return selected.length ? selected[0].label || selected[0].value : '';
153
- }, ...(ngDevMode ? [{ debugName: "selectedLabel" }] : []));
154
- this.filteredOptions = computed(() => {
155
- const search = this.searchValue().toLowerCase();
156
- const optionsArray = this.options?.toArray() || [];
157
- let filteredArray = optionsArray;
158
- if (this.multiple) {
159
- const currentValue = this.value();
160
- filteredArray = optionsArray.filter(option => !Array.isArray(currentValue) || !currentValue.includes(option.value));
161
- }
162
- if (!search) {
163
- return filteredArray;
164
- }
165
- return filteredArray.filter(option => (option.label || option.value.toString()).toLowerCase().includes(search));
166
- }, ...(ngDevMode ? [{ debugName: "filteredOptions" }] : []));
167
- this.elementRef = inject(ElementRef);
168
- this.cdr = inject(ChangeDetectorRef);
169
- this.onChange = noop;
170
- this.onTouch = noop;
171
- }
172
- set disabled(value) {
173
- this.isDisabled.set(value);
174
- }
175
- get disabled() {
176
- return this.isDisabled();
177
- }
178
- get hostClasses() {
179
- return {
180
- 'wr-select': true,
181
- 'wr-select--open': this.isOpen(),
182
- 'wr-select--multiple': this.multiple,
183
- 'wr-select--disabled': this.isDisabled(),
184
- 'wr-select--focused': this.isFocused(),
185
- };
186
- }
187
- onKeyDown(event) {
188
- if (!this.isOpen()) {
188
+ class WrSelect {
189
+ /** Placeholder shown when no option is selected. Falls back to `select.placeholder`. */
190
+ placeholder = input(null, /* @ts-ignore */
191
+ ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
192
+ /** Clear-selection (×) button aria-label. Falls back to `select.clearSelection`. */
193
+ clearLabel = input(null, /* @ts-ignore */
194
+ ...(ngDevMode ? [{ debugName: "clearLabel" }] : /* istanbul ignore next */ []));
195
+ resolvedPlaceholder = useI18nText(this.placeholder, 'select.placeholder', '');
196
+ resolvedClear = useI18nText(this.clearLabel, 'select.clearSelection', 'Clear selection');
197
+ /** Per-chip ARIA label — interpolates `{{label}}`. @internal */
198
+ chipRemoveLabel = useI18nFormatter('select.removeItem', 'Remove {{label}}');
199
+ /** Disable the select. Also set by Angular forms via `setDisabledState`. */
200
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
201
+ /** Pill-shaped corners on the trigger. @default false */
202
+ rounded = input(false, { ...(ngDevMode ? { debugName: "rounded" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
203
+ /**
204
+ * Behavior mode. `<wr-select>` is the unified combobox primitive —
205
+ * pick the shape via `[mode]`:
206
+ *
207
+ * - `'single'` (default) — one value, no input. Classic dropdown.
208
+ * - `'multi'` — array value, chips on the trigger.
209
+ * - `'search'` *(planned)* — type-ahead with sync filter or async loader.
210
+ * - `'tag'` *(planned)* — free-text + chips with optional `allowCreate`.
211
+ *
212
+ */
213
+ mode = input(null, /* @ts-ignore */
214
+ ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
215
+ /** Resolved mode — `'single'` unless `[mode]` says otherwise. */
216
+ effectiveMode = computed(() => this.mode() ?? 'single', /* @ts-ignore */
217
+ ...(ngDevMode ? [{ debugName: "effectiveMode" }] : /* istanbul ignore next */ []));
218
+ /** Convenience — `effectiveMode() === 'multi'`. */
219
+ isMulti = computed(() => this.effectiveMode() === 'multi', /* @ts-ignore */
220
+ ...(ngDevMode ? [{ debugName: "isMulti" }] : /* istanbul ignore next */ []));
221
+ /** Convenience — `effectiveMode() === 'tag'`. */
222
+ isTag = computed(() => this.effectiveMode() === 'tag', /* @ts-ignore */
223
+ ...(ngDevMode ? [{ debugName: "isTag" }] : /* istanbul ignore next */ []));
224
+ /** Convenience — `effectiveMode() === 'search'`. Exposed via context for options. */
225
+ isSearch = computed(() => this.effectiveMode() === 'search', /* @ts-ignore */
226
+ ...(ngDevMode ? [{ debugName: "isSearch" }] : /* istanbul ignore next */ []));
227
+ /** WrSelectContext — resolved multi-selection flag for child options. */
228
+ multi = computed(() => this.effectiveMode() === 'multi' || this.effectiveMode() === 'tag', /* @ts-ignore */
229
+ ...(ngDevMode ? [{ debugName: "multi" }] : /* istanbul ignore next */ []));
230
+ /** Both multi and tag render chips on the trigger. */
231
+ hasChips = computed(() => {
232
+ const m = this.effectiveMode();
233
+ return m === 'multi' || m === 'tag';
234
+ }, /* @ts-ignore */
235
+ ...(ngDevMode ? [{ debugName: "hasChips" }] : /* istanbul ignore next */ []));
236
+ /**
237
+ * Search-mode query. Empty string = no filter. Exposed via context so
238
+ * each `<wr-option>` can self-hide non-matching rows.
239
+ */
240
+ searchQuery = signal('', /* @ts-ignore */
241
+ ...(ngDevMode ? [{ debugName: "searchQuery" }] : /* istanbul ignore next */ []));
242
+ // Search-mode-only inputs (ignored in other modes)
243
+ /**
244
+ * Search mode: dynamic option array. Each item is rendered as a
245
+ * `<wr-option>` whose label comes from `[displayWith]`. Works
246
+ * alongside projected `<wr-option>` children — both lists are
247
+ * filtered by the search query.
248
+ */
249
+ options = input([], /* @ts-ignore */
250
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
251
+ /** Search mode: map a dynamic option item to its display label. @default String */
252
+ displayWith = input((item) => String(item), /* @ts-ignore */
253
+ ...(ngDevMode ? [{ debugName: "displayWith" }] : /* istanbul ignore next */ []));
254
+ /**
255
+ * Search mode: async loader. When set, the loader is called on every
256
+ * (debounced) keystroke and its result replaces `[options]`. Supports
257
+ * Observables, Promises, and plain arrays.
258
+ */
259
+ loader = input(null, /* @ts-ignore */
260
+ ...(ngDevMode ? [{ debugName: "loader" }] : /* istanbul ignore next */ []));
261
+ /** Search mode: debounce (ms) applied to the loader. @default 250 */
262
+ debounceMs = input(250, { ...(ngDevMode ? { debugName: "debounceMs" } : /* istanbul ignore next */ {}), transform: (v) => Math.max(0, Number(v) || 0) });
263
+ /** Search mode: minimum query length before the panel opens / loader fires. @default 0 */
264
+ minChars = input(0, { ...(ngDevMode ? { debugName: "minChars" } : /* istanbul ignore next */ {}), transform: (v) => Math.max(0, Number(v) || 0) });
265
+ /**
266
+ * Search mode: allow values not in the options list. Enter on an
267
+ * unmatched query commits the raw text as the form value. @default false
268
+ */
269
+ freeText = input(false, { ...(ngDevMode ? { debugName: "freeText" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
270
+ /** Search mode: text shown when the filter / loader returns nothing. Falls back to `select.noResults`. */
271
+ noResultsText = input(null, /* @ts-ignore */
272
+ ...(ngDevMode ? [{ debugName: "noResultsText" }] : /* istanbul ignore next */ []));
273
+ /** Search mode: text shown while the async loader is in flight. Falls back to `select.loading`. */
274
+ loadingText = input(null, /* @ts-ignore */
275
+ ...(ngDevMode ? [{ debugName: "loadingText" }] : /* istanbul ignore next */ []));
276
+ resolvedNoResults = useI18nText(this.noResultsText, 'select.noResults', 'No results');
277
+ resolvedLoading = useI18nText(this.loadingText, 'select.loading', 'Loading…');
278
+ /** Search mode: results returned by the async loader. @internal */
279
+ loadedOptions = signal([], /* @ts-ignore */
280
+ ...(ngDevMode ? [{ debugName: "loadedOptions" }] : /* istanbul ignore next */ []));
281
+ /** Search mode: true while the loader is in flight. @internal */
282
+ loading = signal(false, /* @ts-ignore */
283
+ ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
284
+ // Tag-mode-only inputs (ignored in other modes)
285
+ /**
286
+ * Tag mode: keys / characters that commit the current draft into a chip.
287
+ * `'Enter'` is the key name; everything else is a literal character watched
288
+ * in keypresses and pastes. @default ['Enter', ',']
289
+ */
290
+ separators = input(['Enter', ','], /* @ts-ignore */
291
+ ...(ngDevMode ? [{ debugName: "separators" }] : /* istanbul ignore next */ []));
292
+ /** Tag mode: allow the same value to appear more than once. @default false */
293
+ allowDuplicates = input(false, { ...(ngDevMode ? { debugName: "allowDuplicates" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
294
+ /**
295
+ * Tag mode: custom validator — return `true` to accept the value, `false`
296
+ * to silently reject. Receives the trimmed draft + the existing chips.
297
+ */
298
+ validate = input(null, /* @ts-ignore */
299
+ ...(ngDevMode ? [{ debugName: "validate" }] : /* istanbul ignore next */ []));
300
+ /** Tag-mode draft (text currently typed in the inline input). @internal */
301
+ draft = signal('', /* @ts-ignore */
302
+ ...(ngDevMode ? [{ debugName: "draft" }] : /* istanbul ignore next */ []));
303
+ /**
304
+ * Show a clear-all (×) button at the end of the chip row when at
305
+ * least one option is selected (multi mode only). @default true
306
+ */
307
+ clearable = input(true, { ...(ngDevMode ? { debugName: "clearable" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
308
+ /**
309
+ * Cap on selected items (multi mode). `0` = unlimited. Once reached,
310
+ * additional clicks on unselected options are ignored. @default 0
311
+ */
312
+ maxItems = input(0, { ...(ngDevMode ? { debugName: "maxItems" } : /* istanbul ignore next */ {}), transform: (v) => Math.max(0, Number(v) || 0) });
313
+ /**
314
+ * Maximum number of chips rendered before collapsing the rest into a
315
+ * `+N more` indicator. `0` = render every chip. @default 0
316
+ */
317
+ maxTagCount = input(0, { ...(ngDevMode ? { debugName: "maxTagCount" } : /* istanbul ignore next */ {}), transform: (v) => Math.max(0, Number(v) || 0) });
318
+ /**
319
+ * Unified value signal. Single mode holds `T | null`; multi mode
320
+ * holds `readonly T[]`. Internal — consumers go through `[(ngModel)]`
321
+ * or `[formControl]` via CVA.
322
+ * @internal
323
+ */
324
+ value = signal(null, /* @ts-ignore */
325
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
326
+ /** Listbox id used by the trigger's `aria-controls`. */
327
+ listboxId = `wr-select-listbox-${++listboxUid}`;
328
+ /** Registered options (insertion order). Internal — distinct from the public `[options]` input. */
329
+ registry = signal([], /* @ts-ignore */
330
+ ...(ngDevMode ? [{ debugName: "registry" }] : /* istanbul ignore next */ []));
331
+ /** Keyboard cursor index into `options`. -1 = none. */
332
+ activeIndex = signal(-1, /* @ts-ignore */
333
+ ...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
334
+ /** Id of the active option (for `aria-activedescendant`). */
335
+ activeOptionId = computed(() => {
336
+ const idx = this.activeIndex();
337
+ const list = this.registry();
338
+ return idx >= 0 && idx < list.length ? list[idx].id : null;
339
+ }, /* @ts-ignore */
340
+ ...(ngDevMode ? [{ debugName: "activeOptionId" }] : /* istanbul ignore next */ []));
341
+ open = signal(false, /* @ts-ignore */
342
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
343
+ selectedLabel = signal(null, /* @ts-ignore */
344
+ ...(ngDevMode ? [{ debugName: "selectedLabel" }] : /* istanbul ignore next */ []));
345
+ /**
346
+ * Selected chips (multi + tag modes). Multi reads labels from the
347
+ * registered `<wr-option>` children; tag uses each raw string as
348
+ * both value and label.
349
+ */
350
+ selectedChips = computed(() => {
351
+ if (!this.hasChips())
352
+ return [];
353
+ const arr = this.asArray(this.value());
354
+ if (this.isTag()) {
355
+ return arr.map(v => ({ value: v, label: String(v) }));
356
+ }
357
+ const list = this.registry();
358
+ return arr.map(v => {
359
+ const found = list.find(o => o.value === v);
360
+ return { value: v, label: found?.getLabel() ?? String(v) };
361
+ });
362
+ }, /* @ts-ignore */
363
+ ...(ngDevMode ? [{ debugName: "selectedChips" }] : /* istanbul ignore next */ []));
364
+ visibleChips = computed(() => {
365
+ const cap = this.maxTagCount();
366
+ const chips = this.selectedChips();
367
+ return cap > 0 && chips.length > cap ? chips.slice(0, cap) : chips;
368
+ }, /* @ts-ignore */
369
+ ...(ngDevMode ? [{ debugName: "visibleChips" }] : /* istanbul ignore next */ []));
370
+ hiddenChipCount = computed(() => {
371
+ const cap = this.maxTagCount();
372
+ const total = this.selectedChips().length;
373
+ return cap > 0 && total > cap ? total - cap : 0;
374
+ }, /* @ts-ignore */
375
+ ...(ngDevMode ? [{ debugName: "hiddenChipCount" }] : /* istanbul ignore next */ []));
376
+ hasSelection = computed(() => {
377
+ if (this.hasChips())
378
+ return this.selectedChips().length > 0;
379
+ return this.value() != null;
380
+ }, /* @ts-ignore */
381
+ ...(ngDevMode ? [{ debugName: "hasSelection" }] : /* istanbul ignore next */ []));
382
+ disabledFromCva = signal(false, /* @ts-ignore */
383
+ ...(ngDevMode ? [{ debugName: "disabledFromCva" }] : /* istanbul ignore next */ []));
384
+ /** Effective disabled state — input wins, CVA second. */
385
+ isDisabled = computed(() => this.disabled() || this.disabledFromCva(), /* @ts-ignore */
386
+ ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
387
+ classes = computed(() => {
388
+ const parts = ['wr-select'];
389
+ if (this.open())
390
+ parts.push('wr-select--open');
391
+ if (this.rounded())
392
+ parts.push('wr-select--rounded');
393
+ if (this.isMulti())
394
+ parts.push('wr-select--multi');
395
+ if (this.isTag())
396
+ parts.push('wr-select--tag');
397
+ if (this.isSearch())
398
+ parts.push('wr-select--search');
399
+ if (this.isDisabled())
400
+ parts.push('wr-select--disabled');
401
+ return parts.join(' ');
402
+ }, /* @ts-ignore */
403
+ ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
404
+ /**
405
+ * Search-mode dynamic options. Loader results win when the loader is
406
+ * set; otherwise the static `[options]` array. Each item is rendered
407
+ * as an internal `<wr-option>` in the panel.
408
+ */
409
+ dynamicOptions = computed(() => this.loader() ? this.loadedOptions() : this.options(), /* @ts-ignore */
410
+ ...(ngDevMode ? [{ debugName: "dynamicOptions" }] : /* istanbul ignore next */ []));
411
+ /**
412
+ * Count of registered options that pass the current search filter.
413
+ * Hides the noResults state when the panel has at least one visible row.
414
+ */
415
+ visibleCount = computed(() => {
416
+ const list = this.registry();
417
+ if (!this.isSearch())
418
+ return list.length;
419
+ const q = this.searchQuery().trim().toLowerCase();
420
+ if (!q)
421
+ return list.length;
422
+ let count = 0;
423
+ for (const o of list) {
424
+ if (o.getLabel().toLowerCase().includes(q))
425
+ count++;
426
+ }
427
+ return count;
428
+ }, /* @ts-ignore */
429
+ ...(ngDevMode ? [{ debugName: "visibleCount" }] : /* istanbul ignore next */ []));
430
+ /** Search mode: show "no results" when the panel has nothing to offer. */
431
+ hasNoResults = computed(() => {
432
+ if (!this.isSearch() || !this.open() || this.loading())
433
+ return false;
434
+ if (this.searchQuery().trim().length < this.minChars())
435
+ return false;
436
+ return this.visibleCount() === 0;
437
+ }, /* @ts-ignore */
438
+ ...(ngDevMode ? [{ debugName: "hasNoResults" }] : /* istanbul ignore next */ []));
439
+ /** Search mode: is this option hidden by the current query? @internal */
440
+ isOptionHidden(label) {
441
+ if (!this.isSearch())
442
+ return false;
443
+ const q = this.searchQuery().trim().toLowerCase();
444
+ if (!q)
445
+ return false;
446
+ return !label.toLowerCase().includes(q);
447
+ }
448
+ panelTpl = viewChild.required('panelTpl', { read: TemplateRef });
449
+ /** Tag-mode inline input. Present only when `mode="tag"`. @internal */
450
+ tagInputEl = viewChild('tagInput', /* @ts-ignore */
451
+ ...(ngDevMode ? [{ debugName: "tagInputEl" }] : /* istanbul ignore next */ []));
452
+ /** Search-mode inline input. Present only when `mode="search"`. @internal */
453
+ searchInputEl = viewChild('searchInput', /* @ts-ignore */
454
+ ...(ngDevMode ? [{ debugName: "searchInputEl" }] : /* istanbul ignore next */ []));
455
+ focusTagInput() {
456
+ this.tagInputEl()?.nativeElement.focus();
457
+ }
458
+ focusSearchInput() {
459
+ this.searchInputEl()?.nativeElement.focus();
460
+ }
461
+ // Search-mode handlers
462
+ onSearchInput(event) {
463
+ if (this.isDisabled())
464
+ return;
465
+ const value = event.target.value;
466
+ this.searchQuery.set(value);
467
+ if (!this.open())
468
+ this.open.set(true);
469
+ }
470
+ onSearchFocus() {
471
+ if (this.isDisabled())
189
472
  return;
473
+ if (!this.open()) {
474
+ this.seedActiveIndex();
475
+ this.open.set(true);
190
476
  }
191
- const filteredOpts = this.filteredOptions();
192
- const currentIndex = this.highlightedOptionIndex();
193
- switch (event.key) {
194
- case 'ArrowDown':
195
- event.preventDefault();
196
- this.highlightedOptionIndex.set(currentIndex < filteredOpts.length - 1 ? currentIndex + 1 : 0);
197
- break;
198
- case 'ArrowUp':
199
- event.preventDefault();
200
- this.highlightedOptionIndex.set(currentIndex > 0 ? currentIndex - 1 : filteredOpts.length - 1);
201
- break;
202
- case 'Enter':
203
- event.preventDefault();
204
- if (currentIndex >= 0 && filteredOpts[currentIndex]) {
205
- this.onOptionSelect(filteredOpts[currentIndex]);
206
- }
207
- break;
208
- case 'Backspace':
209
- if (this.multiple && !this.searchValue() && this.selectedOptions().length) {
210
- const selectedOptionsArray = this.selectedOptions();
211
- const lastSelected = selectedOptionsArray[selectedOptionsArray.length - 1];
212
- this.onOptionSelect(lastSelected);
213
- }
214
- break;
215
- case 'Escape':
477
+ }
478
+ onSearchKey(event) {
479
+ // freeText: Enter on an unmatched query commits the typed string
480
+ // as the value. Skips when an active option is highlighted (the
481
+ // normal Enter-selects behaviour wins).
482
+ if (event.key === 'Enter' && this.freeText() && this.open()) {
483
+ const q = this.searchQuery().trim();
484
+ const idx = this.activeIndex();
485
+ const list = this.registry();
486
+ const hasActive = idx >= 0 && idx < list.length && !list[idx].disabled && !this.isOptionHidden(list[idx].getLabel());
487
+ if (!hasActive && q) {
216
488
  event.preventDefault();
217
- this.close();
218
- break;
489
+ this.value.set(q);
490
+ this.onChange(q);
491
+ this.onTouched();
492
+ this.open.set(false);
493
+ return;
494
+ }
219
495
  }
496
+ // Otherwise route through the same keyboard plumbing as the button trigger.
497
+ this.onTriggerKey(event);
220
498
  }
221
- onClickOutside(event) {
222
- if (!this.elementRef.nativeElement.contains(event.target)) {
223
- this.close();
224
- }
499
+ /**
500
+ * Display text for the search input. Shows the selected option's
501
+ * label when collapsed, or the live query while typing / panel open.
502
+ * @internal
503
+ */
504
+ searchDisplay = computed(() => {
505
+ if (this.open())
506
+ return this.searchQuery();
507
+ return this.selectedLabel() ?? '';
508
+ }, /* @ts-ignore */
509
+ ...(ngDevMode ? [{ debugName: "searchDisplay" }] : /* istanbul ignore next */ []));
510
+ host = inject(ElementRef);
511
+ overlay = inject(WR_OVERLAY);
512
+ vcr = inject(ViewContainerRef);
513
+ scrollStrategies = inject(ScrollStrategyOptions);
514
+ destroyRef = inject(DestroyRef);
515
+ overlayRef = null;
516
+ onChange = noop;
517
+ onTouched = noop;
518
+ constructor() {
519
+ effect(() => {
520
+ if (this.open()) {
521
+ this.openOverlay();
522
+ }
523
+ else {
524
+ this.closeOverlay();
525
+ // Reset the search query when the panel closes so a re-open
526
+ // doesn't carry a stale filter.
527
+ if (this.isSearch())
528
+ this.searchQuery.set('');
529
+ }
530
+ });
531
+ // Async loader pipeline (search mode only). `switchMap` cancels
532
+ // in-flight requests when a new keystroke arrives.
533
+ toObservable(this.searchQuery)
534
+ .pipe(debounceTime(this.debounceMs()), switchMap(query => {
535
+ const loader = this.loader();
536
+ if (!loader || !this.isSearch() || query.length < this.minChars()) {
537
+ this.loading.set(false);
538
+ return of([]);
539
+ }
540
+ this.loading.set(true);
541
+ const result = loader(query);
542
+ const source = isObservable(result) ? result : from(Promise.resolve(result));
543
+ return source.pipe(finalize(() => this.loading.set(false)));
544
+ }), takeUntilDestroyed(this.destroyRef))
545
+ .subscribe(items => this.loadedOptions.set(items));
546
+ // Resolve the single-mode trigger label whenever the value or
547
+ // options change. Multi mode reads `selectedChips` instead and
548
+ // doesn't need this.
549
+ effect(() => {
550
+ const v = this.value();
551
+ const list = this.registry();
552
+ if (this.isMulti()) {
553
+ this.selectedLabel.set(null);
554
+ return;
555
+ }
556
+ const match = list.find(o => o.value === v);
557
+ this.selectedLabel.set(match ? match.getLabel() : null);
558
+ });
225
559
  }
226
- get currentValue() {
227
- return this.value();
560
+ // WrSelectContext
561
+ isSelected(value) {
562
+ if (this.isMulti())
563
+ return this.asArray(this.value()).includes(value);
564
+ return this.value() === value;
228
565
  }
229
- get isMultiple() {
230
- return this.multiple;
566
+ selectOption(value) {
567
+ if (this.isMulti()) {
568
+ const current = this.asArray(this.value());
569
+ const idx = current.indexOf(value);
570
+ let next;
571
+ if (idx >= 0) {
572
+ next = [...current.slice(0, idx), ...current.slice(idx + 1)];
573
+ }
574
+ else {
575
+ const cap = this.maxItems();
576
+ if (cap > 0 && current.length >= cap)
577
+ return;
578
+ next = [...current, value];
579
+ }
580
+ this.value.set(next);
581
+ this.onChange(next);
582
+ this.onTouched();
583
+ // Keep the panel open in multi mode so the user can keep picking.
584
+ return;
585
+ }
586
+ this.value.set(value);
587
+ this.onChange(value);
588
+ this.onTouched();
589
+ this.open.set(false);
231
590
  }
232
- ngAfterContentInit() {
233
- this.options.changes.pipe(takeUntilDestroyed(this.destroyRef$)).subscribe(() => {
234
- this.cdr.markForCheck();
235
- });
591
+ registerOption(reg) {
592
+ this.registry.update(list => [...list, reg]);
593
+ return () => this.registry.update(list => list.filter(o => o.id !== reg.id));
594
+ }
595
+ // Chip handlers (multi + tag)
596
+ /** Remove one selection (chip × button). Works for multi and tag modes. */
597
+ removeChip(value, event) {
598
+ event.stopPropagation();
599
+ if (this.isDisabled() || !this.hasChips())
600
+ return;
601
+ const next = this.asArray(this.value()).filter(v => v !== value);
602
+ this.value.set(next);
603
+ this.onChange(next);
604
+ this.onTouched();
236
605
  }
237
- onOptionSelect(option) {
606
+ /** Clear every selection (× button). Multi/tag → empty array; search → null. */
607
+ clearAll(event) {
608
+ event.stopPropagation();
238
609
  if (this.isDisabled())
239
610
  return;
240
- if (this.multiple) {
241
- const currentValue = Array.isArray(this.value()) ? this.value() : [];
242
- if (this.maxMultipleCount && !this.isSelectedOption(option) && currentValue.length >= this.maxMultipleCount) {
243
- return;
244
- }
245
- const newValue = this.isSelectedOption(option)
246
- ? currentValue.filter((v) => v !== option.value)
247
- : [...currentValue, option.value];
248
- this.writeValue(newValue);
249
- this.searchValue.set('');
611
+ if (this.hasChips()) {
612
+ this.value.set([]);
613
+ this.onChange([]);
614
+ }
615
+ else if (this.isSearch()) {
616
+ this.value.set(null);
617
+ this.onChange(null);
618
+ this.searchQuery.set('');
250
619
  }
251
620
  else {
252
- this.writeValue(option.value);
253
- this.close();
621
+ return;
254
622
  }
623
+ this.onTouched();
255
624
  }
256
- onSearch(event) {
257
- const value = event.target.value;
258
- this.searchValue.set(value);
625
+ // Tag-mode draft handlers
626
+ /** Capacity check used by tag mode to refuse the next add. */
627
+ atCapacity = computed(() => {
628
+ const cap = this.maxItems();
629
+ return cap > 0 && this.asArray(this.value()).length >= cap;
630
+ }, /* @ts-ignore */
631
+ ...(ngDevMode ? [{ debugName: "atCapacity" }] : /* istanbul ignore next */ []));
632
+ onTagInput(event) {
633
+ if (this.isDisabled())
634
+ return;
635
+ this.draft.set(event.target.value);
259
636
  }
260
- isHighlightedOption(option) {
261
- if (this.highlightedOptionIndex() === -1) {
262
- return false;
637
+ onTagKeydown(event) {
638
+ if (this.isDisabled())
639
+ return;
640
+ const seps = this.separators();
641
+ if (seps.includes(event.key)) {
642
+ event.preventDefault();
643
+ this.commitDraft();
644
+ return;
263
645
  }
264
- const filteredOpts = this.filteredOptions();
265
- const optionIndex = filteredOpts.findIndex(item => item.value === option.value);
266
- return optionIndex !== -1 && optionIndex === this.highlightedOptionIndex();
267
- }
268
- isFilteredOption(option) {
269
- if (this.multiple) {
270
- const currentValue = this.value();
271
- if (Array.isArray(currentValue) && currentValue.includes(option.value)) {
272
- return true;
646
+ if (event.key === 'Backspace' && this.draft() === '') {
647
+ const chips = this.selectedChips();
648
+ if (chips.length > 0) {
649
+ event.preventDefault();
650
+ this.removeChip(chips[chips.length - 1].value, event);
273
651
  }
274
652
  }
275
- const search = this.searchValue().toLowerCase();
276
- if (search) {
277
- return !(option.label || option.value.toString()).toLowerCase().includes(search);
278
- }
279
- return false;
280
653
  }
654
+ onTagPaste(event) {
655
+ if (this.isDisabled())
656
+ return;
657
+ const text = event.clipboardData?.getData('text') ?? '';
658
+ if (!text)
659
+ return;
660
+ const seps = this.separators().filter(s => s !== 'Enter');
661
+ if (seps.length === 0)
662
+ return;
663
+ event.preventDefault();
664
+ const escaped = seps.map(s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('');
665
+ const splitRegex = new RegExp(`[${escaped}\\n]+`);
666
+ for (const part of text.split(splitRegex))
667
+ this.tryAddTag(part);
668
+ }
669
+ onTagBlur() {
670
+ if (this.draft().trim())
671
+ this.commitDraft();
672
+ this.onTouched();
673
+ }
674
+ commitDraft() {
675
+ const v = this.draft().trim();
676
+ this.draft.set('');
677
+ if (v)
678
+ this.tryAddTag(v);
679
+ }
680
+ tryAddTag(raw) {
681
+ if (!this.isTag() || this.atCapacity())
682
+ return;
683
+ const value = raw.trim();
684
+ if (!value)
685
+ return;
686
+ const existing = this.asArray(this.value()).map(String);
687
+ if (!this.allowDuplicates() && existing.includes(value))
688
+ return;
689
+ const validator = this.validate();
690
+ if (validator && !validator(value, existing))
691
+ return;
692
+ const next = [...existing, value];
693
+ this.value.set(next);
694
+ this.onChange(next);
695
+ this.onTouched();
696
+ }
697
+ // ControlValueAccessor
281
698
  writeValue(value) {
282
- this.value.set(value);
283
- this.onChange(value);
284
- this.cdr.markForCheck();
699
+ if (this.hasChips()) {
700
+ this.value.set(Array.isArray(value) ? value : []);
701
+ }
702
+ else {
703
+ this.value.set(value);
704
+ }
285
705
  }
286
706
  registerOnChange(fn) {
287
707
  this.onChange = fn;
288
708
  }
289
709
  registerOnTouched(fn) {
290
- this.onTouch = fn;
710
+ this.onTouched = fn;
291
711
  }
292
712
  setDisabledState(isDisabled) {
293
- this.isDisabled.set(isDisabled);
713
+ this.disabledFromCva.set(coerceBooleanProperty(isDisabled));
714
+ if (isDisabled)
715
+ this.open.set(false);
294
716
  }
295
- clear(event) {
296
- event.stopPropagation();
717
+ // Template handlers
718
+ onTriggerClick() {
297
719
  if (this.isDisabled())
298
720
  return;
299
- if (this.multiple) {
300
- this.writeValue([]);
721
+ // Tag mode has no panel — clicks should land on the inline input,
722
+ // not toggle an overlay that's never created.
723
+ if (this.isTag())
724
+ return;
725
+ this.open.update(v => !v);
726
+ }
727
+ onTriggerKey(event) {
728
+ if (this.isDisabled())
729
+ return;
730
+ if (!this.open()) {
731
+ if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown' || event.key === 'ArrowUp') {
732
+ event.preventDefault();
733
+ this.seedActiveIndex();
734
+ this.open.set(true);
735
+ return;
736
+ }
737
+ // Multi mode: Backspace on closed trigger removes last chip. Tag
738
+ // mode has its own input handler — never reaches this branch.
739
+ if (event.key === 'Backspace' && this.isMulti() && this.hasSelection()) {
740
+ event.preventDefault();
741
+ const chips = this.selectedChips();
742
+ const last = chips[chips.length - 1];
743
+ if (last)
744
+ this.removeChip(last.value, event);
745
+ }
746
+ return;
747
+ }
748
+ // Open — navigation/selection.
749
+ switch (event.key) {
750
+ case 'ArrowDown':
751
+ event.preventDefault();
752
+ this.moveActive(1);
753
+ break;
754
+ case 'ArrowUp':
755
+ event.preventDefault();
756
+ this.moveActive(-1);
757
+ break;
758
+ case 'Home':
759
+ event.preventDefault();
760
+ this.activeIndex.set(this.firstEnabled());
761
+ break;
762
+ case 'End':
763
+ event.preventDefault();
764
+ this.activeIndex.set(this.lastEnabled());
765
+ break;
766
+ case 'Enter':
767
+ case ' ': {
768
+ event.preventDefault();
769
+ const idx = this.activeIndex();
770
+ const list = this.registry();
771
+ if (idx >= 0 && idx < list.length && !list[idx].disabled) {
772
+ const id = list[idx].id;
773
+ const el = this.overlayRef?.overlayElement.querySelector(`#${CSS.escape(id)}`);
774
+ el?.click();
775
+ }
776
+ break;
777
+ }
778
+ default:
779
+ break;
780
+ }
781
+ }
782
+ seedActiveIndex() {
783
+ const list = this.registry();
784
+ let selectedIdx = -1;
785
+ if (this.isMulti()) {
786
+ const arr = this.asArray(this.value());
787
+ if (arr.length > 0)
788
+ selectedIdx = list.findIndex(o => o.value === arr[0]);
301
789
  }
302
790
  else {
303
- this.writeValue(null);
791
+ selectedIdx = list.findIndex(o => o.value === this.value());
304
792
  }
793
+ this.activeIndex.set(selectedIdx >= 0 ? selectedIdx : this.firstEnabled());
305
794
  }
306
- toggle() {
307
- if (this.isDisabled()) {
795
+ moveActive(delta) {
796
+ const list = this.registry();
797
+ if (list.length === 0)
308
798
  return;
309
- }
310
- this.isOpen.update(value => !value);
311
- this.isFocused.update(value => !value);
312
- if (this.isOpen() && this.searchable) {
313
- // TODO Разобраться с этим хаком
314
- setTimeout(() => {
315
- this.searchInput?.nativeElement.focus();
316
- }, 10);
799
+ let i = this.activeIndex();
800
+ let attempts = list.length;
801
+ while (attempts-- > 0) {
802
+ i = (i + delta + list.length) % list.length;
803
+ const o = list[i];
804
+ if (!o.disabled && !this.isOptionHidden(o.getLabel())) {
805
+ this.activeIndex.set(i);
806
+ return;
807
+ }
317
808
  }
318
809
  }
319
- close() {
320
- this.isOpen.set(false);
321
- this.isFocused.set(false);
322
- this.searchValue.set('');
323
- this.highlightedOptionIndex.set(-1);
810
+ firstEnabled() {
811
+ return this.registry().findIndex(o => !o.disabled && !this.isOptionHidden(o.getLabel()));
324
812
  }
325
- isSelectedOption(option) {
326
- const currentValue = this.value();
327
- if (this.multiple) {
328
- return Array.isArray(currentValue) && currentValue.includes(option.value);
329
- }
330
- return currentValue === option.value;
813
+ lastEnabled() {
814
+ const list = this.registry();
815
+ let found = -1;
816
+ list.forEach((o, i) => {
817
+ if (!o.disabled && !this.isOptionHidden(o.getLabel()))
818
+ found = i;
819
+ });
820
+ return found;
821
+ }
822
+ /** Coerce the multi-mode value signal into an array (handles writeValue from non-array). */
823
+ asArray(value) {
824
+ if (Array.isArray(value))
825
+ return value;
826
+ if (value == null)
827
+ return [];
828
+ return [value];
331
829
  }
332
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.10", ngImport: i0, type: WrSelectComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
333
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.10", type: WrSelectComponent, isStandalone: true, selector: "wr-select", inputs: { placeholder: "placeholder", noItemsLabel: "noItemsLabel", multiple: ["multiple", "multiple", booleanAttribute], searchable: ["searchable", "searchable", booleanAttribute], clearable: ["clearable", "clearable", booleanAttribute], maxMultipleCount: ["maxMultipleCount", "maxMultipleCount", numberAttribute], disabled: ["disabled", "disabled", booleanAttribute] }, host: { listeners: { "document:keydown": "onKeyDown($event)", "document:click": "onClickOutside($event)" }, properties: { "class": "this.hostClasses" } }, providers: [
334
- provideWrIcons([chevronDown, close]),
830
+ // Overlay
831
+ openOverlay() {
832
+ if (this.overlayRef)
833
+ return;
834
+ const positionStrategy = this.overlay
835
+ .position()
836
+ .flexibleConnectedTo(this.host)
837
+ .withPositions([
838
+ { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top' },
839
+ { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom' },
840
+ ])
841
+ .withPush(true);
842
+ this.overlayRef = this.overlay.create({
843
+ positionStrategy,
844
+ scrollStrategy: this.scrollStrategies.reposition(),
845
+ width: this.host.nativeElement.getBoundingClientRect().width,
846
+ panelClass: 'wr-select-overlay',
847
+ });
848
+ const portal = new TemplatePortal(this.panelTpl(), this.vcr);
849
+ this.overlayRef.attach(portal);
850
+ this.overlayRef
851
+ .outsidePointerEvents()
852
+ .pipe(takeUntilDestroyed(this.destroyRef))
853
+ .subscribe(event => {
854
+ if (this.host.nativeElement.contains(event.target))
855
+ return;
856
+ this.open.set(false);
857
+ });
858
+ this.overlayRef
859
+ .keydownEvents()
860
+ .pipe(takeUntilDestroyed(this.destroyRef))
861
+ .subscribe(event => {
862
+ if (event.key === 'Escape') {
863
+ event.preventDefault();
864
+ this.open.set(false);
865
+ }
866
+ });
867
+ }
868
+ closeOverlay() {
869
+ if (!this.overlayRef)
870
+ return;
871
+ this.overlayRef.dispose();
872
+ this.overlayRef = null;
873
+ }
874
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: WrSelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
875
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: WrSelect, isStandalone: true, selector: "wr-select", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, clearLabel: { classPropertyName: "clearLabel", publicName: "clearLabel", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, displayWith: { classPropertyName: "displayWith", publicName: "displayWith", isSignal: true, isRequired: false, transformFunction: null }, loader: { classPropertyName: "loader", publicName: "loader", isSignal: true, isRequired: false, transformFunction: null }, debounceMs: { classPropertyName: "debounceMs", publicName: "debounceMs", isSignal: true, isRequired: false, transformFunction: null }, minChars: { classPropertyName: "minChars", publicName: "minChars", isSignal: true, isRequired: false, transformFunction: null }, freeText: { classPropertyName: "freeText", publicName: "freeText", isSignal: true, isRequired: false, transformFunction: null }, noResultsText: { classPropertyName: "noResultsText", publicName: "noResultsText", isSignal: true, isRequired: false, transformFunction: null }, loadingText: { classPropertyName: "loadingText", publicName: "loadingText", isSignal: true, isRequired: false, transformFunction: null }, separators: { classPropertyName: "separators", publicName: "separators", isSignal: true, isRequired: false, transformFunction: null }, allowDuplicates: { classPropertyName: "allowDuplicates", publicName: "allowDuplicates", isSignal: true, isRequired: false, transformFunction: null }, validate: { classPropertyName: "validate", publicName: "validate", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, maxItems: { classPropertyName: "maxItems", publicName: "maxItems", isSignal: true, isRequired: false, transformFunction: null }, maxTagCount: { classPropertyName: "maxTagCount", publicName: "maxTagCount", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, providers: [
335
876
  {
336
877
  provide: NG_VALUE_ACCESSOR,
337
878
  // eslint-disable-next-line @angular-eslint/no-forward-ref
338
- useExisting: forwardRef(() => WrSelectComponent),
879
+ useExisting: forwardRef(() => WrSelect),
339
880
  multi: true,
340
881
  },
341
- ], queries: [{ propertyName: "options", predicate: WrOptionComponent, descendants: true }, { propertyName: "optionGroups", predicate: WrOptionGroupComponent }], viewQueries: [{ propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<ng-template #searchTemplate let-placeholder=\"placeholder\">\n <input\n #searchInput\n class=\"wr-select__search\"\n [value]=\"searchValue()\"\n (input)=\"onSearch($event)\"\n (click)=\"$event.stopPropagation()\"\n [placeholder]=\"placeholder || ''\"\n />\n</ng-template>\n\n<ng-template #selectedOptionsTemplate>\n @for (option of selectedOptions(); track option.value) {\n <wr-tag [hoverable]=\"true\" color=\"light\" (click)=\"onOptionSelect(option); $event.stopPropagation()\">\n {{ option.label || option.value }}\n <button class=\"wr-select__tag-remove\">\u00D7</button>\n </wr-tag>\n }\n</ng-template>\n\n<div class=\"wr-select__trigger\" (click)=\"toggle()\">\n <div class=\"wr-select__value\">\n @if (multiple) {\n <div class=\"wr-select__tags\">\n <div class=\"wr-select__tags-container\">\n <ng-container *ngTemplateOutlet=\"selectedOptionsTemplate\"></ng-container>\n </div>\n\n @if (searchable && isOpen()) {\n <ng-container *ngTemplateOutlet=\"searchTemplate\"></ng-container>\n }\n\n @if (maxMultipleCount) {\n <div class=\"wr-select__counter\">{{ selectedOptions().length }}/{{ maxMultipleCount }}</div>\n }\n </div>\n } @else {\n @if (searchable && isOpen()) {\n <ng-container\n *ngTemplateOutlet=\"searchTemplate; context: { placeholder: selectedLabel() || placeholder }\"\n ></ng-container>\n } @else {\n {{ selectedLabel() || placeholder }}\n }\n }\n </div>\n\n @if (clearable && selectedOptions().length) {\n <button class=\"wr-select__clear\" (click)=\"clear($event)\" [class.wr-select__clear--multiple]=\"multiple\">\n <wr-icon name=\"close\" />\n </button>\n }\n <wr-icon class=\"wr-select__arrow\" name=\"chevron-down\" />\n</div>\n\n<div class=\"wr-select__dropdown\" [class.wr-select__dropdown--open]=\"isOpen()\">\n <div class=\"wr-select__dropdown-content\">\n @if (!filteredOptions().length) {\n <div class=\"wr-select__no-data\">{{ noItemsLabel }}</div>\n } @else {\n <ng-content></ng-content>\n }\n </div>\n</div>\n", dependencies: [{ kind: "component", type: WrIconComponent, selector: "wr-icon", inputs: ["name"] }, { kind: "component", type: WrTagComponent, selector: "wr-tag", inputs: ["color", "icon", "iconPosition", "outlined", "rounded", "transparent", "hoverable", "loading"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
882
+ {
883
+ provide: WR_SELECT,
884
+ // eslint-disable-next-line @angular-eslint/no-forward-ref
885
+ useExisting: forwardRef(() => WrSelect),
886
+ },
887
+ ], viewQueries: [{ propertyName: "panelTpl", first: true, predicate: ["panelTpl"], descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "tagInputEl", first: true, predicate: ["tagInput"], descendants: true, isSignal: true }, { propertyName: "searchInputEl", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (isSearch()) {\n <!-- Search mode: a real text input drives a filtered overlay panel.\n The wrapper isn't itself focusable \u2014 the input is the tabstop. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <div class=\"wr-select__trigger wr-select__trigger--search\" (click)=\"focusSearchInput()\">\n <input\n #searchInput\n type=\"text\"\n role=\"combobox\"\n class=\"wr-select__search-input\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"open() ? activeOptionId() || null : null\"\n [placeholder]=\"resolvedPlaceholder()\"\n [value]=\"searchDisplay()\"\n (input)=\"onSearchInput($event)\"\n (focus)=\"onSearchFocus()\"\n (keydown)=\"onSearchKey($event)\"\n (blur)=\"onTouched()\"\n />\n @if (clearable() && hasSelection() && !isDisabled()) {\n <span\n class=\"wr-select__clear\"\n role=\"button\"\n [attr.aria-label]=\"resolvedClear()\"\n tabindex=\"-1\"\n (click)=\"clearAll($event)\"\n (keydown.enter)=\"clearAll($event)\"\n (keydown.space)=\"clearAll($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n <svg\n class=\"wr-icon__svg wr-select__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 </div>\n} @else if (isTag()) {\n <!-- Tag mode: a focusable input lives inside, so the wrapper itself\n only exists to host chips and forward whitespace clicks to the\n input. Keyboard users tab straight into the input \u2014 no need for a\n separate role. The click handler is a mouse-only convenience. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"wr-select__trigger wr-select__trigger--tag\"\n [attr.aria-disabled]=\"isDisabled() || null\"\n (click)=\"focusTagInput()\"\n >\n <span class=\"wr-select__chips\">\n @for (chip of visibleChips(); track $index) {\n <span class=\"wr-select__chip\">\n <span class=\"wr-select__chip-label\">{{ chip.label }}</span>\n <span\n class=\"wr-select__chip-remove\"\n role=\"button\"\n [attr.aria-label]=\"chipRemoveLabel({ label: chip.label })\"\n tabindex=\"-1\"\n (click)=\"removeChip(chip.value, $event)\"\n (keydown.enter)=\"removeChip(chip.value, $event)\"\n (keydown.space)=\"removeChip(chip.value, $event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n </span>\n }\n @if (hiddenChipCount(); as extra) {\n <span class=\"wr-select__chip wr-select__chip--more\">+{{ extra }} more</span>\n }\n <input\n #tagInput\n type=\"text\"\n class=\"wr-select__tag-input\"\n [value]=\"draft()\"\n [disabled]=\"isDisabled() || atCapacity()\"\n [placeholder]=\"hasSelection() ? '' : resolvedPlaceholder()\"\n (input)=\"onTagInput($event)\"\n (keydown)=\"onTagKeydown($event)\"\n (paste)=\"onTagPaste($event)\"\n (blur)=\"onTagBlur()\"\n />\n </span>\n\n @if (clearable() && hasSelection() && !isDisabled()) {\n <span\n class=\"wr-select__clear\"\n role=\"button\"\n [attr.aria-label]=\"resolvedClear()\"\n tabindex=\"-1\"\n (click)=\"clearAll($event)\"\n (keydown.enter)=\"clearAll($event)\"\n (keydown.space)=\"clearAll($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n </div>\n} @else {\n <button\n type=\"button\"\n class=\"wr-select__trigger\"\n role=\"combobox\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-multiselectable]=\"isMulti() ? 'true' : null\"\n [attr.aria-activedescendant]=\"open() ? activeOptionId() || null : null\"\n (click)=\"onTriggerClick()\"\n (keydown)=\"onTriggerKey($event)\"\n (blur)=\"onTouched()\"\n >\n @if (isMulti()) {\n @if (hasSelection()) {\n <span class=\"wr-select__chips\">\n @for (chip of visibleChips(); track $index) {\n <span class=\"wr-select__chip\">\n <span class=\"wr-select__chip-label\">{{ chip.label }}</span>\n <span\n class=\"wr-select__chip-remove\"\n role=\"button\"\n [attr.aria-label]=\"chipRemoveLabel({ label: chip.label })\"\n tabindex=\"-1\"\n (click)=\"removeChip(chip.value, $event)\"\n (keydown.enter)=\"removeChip(chip.value, $event)\"\n (keydown.space)=\"removeChip(chip.value, $event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n </span>\n }\n @if (hiddenChipCount(); as extra) {\n <span class=\"wr-select__chip wr-select__chip--more\">+{{ extra }} more</span>\n }\n </span>\n } @else {\n <span class=\"wr-select__placeholder\">{{ resolvedPlaceholder() }}</span>\n }\n } @else {\n @if (selectedLabel(); as label) {\n <span class=\"wr-select__value\">{{ label }}</span>\n } @else {\n <span class=\"wr-select__placeholder\">{{ resolvedPlaceholder() }}</span>\n }\n }\n\n @if (isMulti() && clearable() && hasSelection() && !isDisabled()) {\n <span\n class=\"wr-select__clear\"\n role=\"button\"\n [attr.aria-label]=\"resolvedClear()\"\n tabindex=\"-1\"\n (click)=\"clearAll($event)\"\n (keydown.enter)=\"clearAll($event)\"\n (keydown.space)=\"clearAll($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n\n <svg\n class=\"wr-icon__svg wr-select__chevron\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n}\n\n<ng-template #panelTpl>\n <div\n class=\"wr-select-panel\"\n role=\"listbox\"\n [attr.id]=\"listboxId\"\n [attr.aria-multiselectable]=\"isMulti() ? 'true' : null\"\n >\n @if (loading()) {\n <div class=\"wr-select-panel__loading\" aria-busy=\"true\">{{ resolvedLoading() }}</div>\n }\n\n <!-- Dynamic options from [options] or async [loader] (search mode).\n Rendered as wr-option so the standard registration / filter\n pipeline applies uniformly. -->\n @for (item of dynamicOptions(); track $index) {\n <wr-option [value]=\"item\">{{ displayWith()(item) }}</wr-option>\n }\n\n <!-- Projected static options. -->\n <ng-content />\n\n @if (hasNoResults()) {\n <div class=\"wr-select-panel__empty\">{{ resolvedNoResults() }}</div>\n }\n </div>\n</ng-template>\n", dependencies: [{ kind: "component", type: WrOption, selector: "wr-option", inputs: ["value", "disabled"] }], encapsulation: i0.ViewEncapsulation.None });
342
888
  }
343
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.10", ngImport: i0, type: WrSelectComponent, decorators: [{
889
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: WrSelect, decorators: [{
344
890
  type: Component,
345
- args: [{ selector: 'wr-select', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, providers: [
346
- provideWrIcons([chevronDown, close]),
891
+ args: [{ selector: 'wr-select', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, imports: [WrOption], providers: [
347
892
  {
348
893
  provide: NG_VALUE_ACCESSOR,
349
894
  // eslint-disable-next-line @angular-eslint/no-forward-ref
350
- useExisting: forwardRef(() => WrSelectComponent),
895
+ useExisting: forwardRef(() => WrSelect),
351
896
  multi: true,
352
897
  },
353
- ], imports: [WrIconComponent, WrTagComponent, NgTemplateOutlet], template: "<ng-template #searchTemplate let-placeholder=\"placeholder\">\n <input\n #searchInput\n class=\"wr-select__search\"\n [value]=\"searchValue()\"\n (input)=\"onSearch($event)\"\n (click)=\"$event.stopPropagation()\"\n [placeholder]=\"placeholder || ''\"\n />\n</ng-template>\n\n<ng-template #selectedOptionsTemplate>\n @for (option of selectedOptions(); track option.value) {\n <wr-tag [hoverable]=\"true\" color=\"light\" (click)=\"onOptionSelect(option); $event.stopPropagation()\">\n {{ option.label || option.value }}\n <button class=\"wr-select__tag-remove\">\u00D7</button>\n </wr-tag>\n }\n</ng-template>\n\n<div class=\"wr-select__trigger\" (click)=\"toggle()\">\n <div class=\"wr-select__value\">\n @if (multiple) {\n <div class=\"wr-select__tags\">\n <div class=\"wr-select__tags-container\">\n <ng-container *ngTemplateOutlet=\"selectedOptionsTemplate\"></ng-container>\n </div>\n\n @if (searchable && isOpen()) {\n <ng-container *ngTemplateOutlet=\"searchTemplate\"></ng-container>\n }\n\n @if (maxMultipleCount) {\n <div class=\"wr-select__counter\">{{ selectedOptions().length }}/{{ maxMultipleCount }}</div>\n }\n </div>\n } @else {\n @if (searchable && isOpen()) {\n <ng-container\n *ngTemplateOutlet=\"searchTemplate; context: { placeholder: selectedLabel() || placeholder }\"\n ></ng-container>\n } @else {\n {{ selectedLabel() || placeholder }}\n }\n }\n </div>\n\n @if (clearable && selectedOptions().length) {\n <button class=\"wr-select__clear\" (click)=\"clear($event)\" [class.wr-select__clear--multiple]=\"multiple\">\n <wr-icon name=\"close\" />\n </button>\n }\n <wr-icon class=\"wr-select__arrow\" name=\"chevron-down\" />\n</div>\n\n<div class=\"wr-select__dropdown\" [class.wr-select__dropdown--open]=\"isOpen()\">\n <div class=\"wr-select__dropdown-content\">\n @if (!filteredOptions().length) {\n <div class=\"wr-select__no-data\">{{ noItemsLabel }}</div>\n } @else {\n <ng-content></ng-content>\n }\n </div>\n</div>\n" }]
354
- }], propDecorators: { placeholder: [{
355
- type: Input
356
- }], noItemsLabel: [{
357
- type: Input
358
- }], multiple: [{
359
- type: Input,
360
- args: [{ transform: booleanAttribute }]
361
- }], searchable: [{
362
- type: Input,
363
- args: [{ transform: booleanAttribute }]
364
- }], clearable: [{
365
- type: Input,
366
- args: [{ transform: booleanAttribute }]
367
- }], maxMultipleCount: [{
368
- type: Input,
369
- args: [{ transform: numberAttribute }]
370
- }], disabled: [{
371
- type: Input,
372
- args: [{ transform: booleanAttribute }]
373
- }], searchInput: [{
374
- type: ViewChild,
375
- args: ['searchInput']
376
- }], options: [{
377
- type: ContentChildren,
378
- args: [WrOptionComponent, { descendants: true }]
379
- }], optionGroups: [{
380
- type: ContentChildren,
381
- args: [WrOptionGroupComponent]
382
- }], hostClasses: [{
383
- type: HostBinding,
384
- args: ['class']
385
- }], onKeyDown: [{
386
- type: HostListener,
387
- args: ['document:keydown', ['$event']]
388
- }], onClickOutside: [{
389
- type: HostListener,
390
- args: ['document:click', ['$event']]
391
- }] } });
898
+ {
899
+ provide: WR_SELECT,
900
+ // eslint-disable-next-line @angular-eslint/no-forward-ref
901
+ useExisting: forwardRef(() => WrSelect),
902
+ },
903
+ ], template: "@if (isSearch()) {\n <!-- Search mode: a real text input drives a filtered overlay panel.\n The wrapper isn't itself focusable \u2014 the input is the tabstop. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <div class=\"wr-select__trigger wr-select__trigger--search\" (click)=\"focusSearchInput()\">\n <input\n #searchInput\n type=\"text\"\n role=\"combobox\"\n class=\"wr-select__search-input\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"open() ? activeOptionId() || null : null\"\n [placeholder]=\"resolvedPlaceholder()\"\n [value]=\"searchDisplay()\"\n (input)=\"onSearchInput($event)\"\n (focus)=\"onSearchFocus()\"\n (keydown)=\"onSearchKey($event)\"\n (blur)=\"onTouched()\"\n />\n @if (clearable() && hasSelection() && !isDisabled()) {\n <span\n class=\"wr-select__clear\"\n role=\"button\"\n [attr.aria-label]=\"resolvedClear()\"\n tabindex=\"-1\"\n (click)=\"clearAll($event)\"\n (keydown.enter)=\"clearAll($event)\"\n (keydown.space)=\"clearAll($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n <svg\n class=\"wr-icon__svg wr-select__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 </div>\n} @else if (isTag()) {\n <!-- Tag mode: a focusable input lives inside, so the wrapper itself\n only exists to host chips and forward whitespace clicks to the\n input. Keyboard users tab straight into the input \u2014 no need for a\n separate role. The click handler is a mouse-only convenience. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"wr-select__trigger wr-select__trigger--tag\"\n [attr.aria-disabled]=\"isDisabled() || null\"\n (click)=\"focusTagInput()\"\n >\n <span class=\"wr-select__chips\">\n @for (chip of visibleChips(); track $index) {\n <span class=\"wr-select__chip\">\n <span class=\"wr-select__chip-label\">{{ chip.label }}</span>\n <span\n class=\"wr-select__chip-remove\"\n role=\"button\"\n [attr.aria-label]=\"chipRemoveLabel({ label: chip.label })\"\n tabindex=\"-1\"\n (click)=\"removeChip(chip.value, $event)\"\n (keydown.enter)=\"removeChip(chip.value, $event)\"\n (keydown.space)=\"removeChip(chip.value, $event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n </span>\n }\n @if (hiddenChipCount(); as extra) {\n <span class=\"wr-select__chip wr-select__chip--more\">+{{ extra }} more</span>\n }\n <input\n #tagInput\n type=\"text\"\n class=\"wr-select__tag-input\"\n [value]=\"draft()\"\n [disabled]=\"isDisabled() || atCapacity()\"\n [placeholder]=\"hasSelection() ? '' : resolvedPlaceholder()\"\n (input)=\"onTagInput($event)\"\n (keydown)=\"onTagKeydown($event)\"\n (paste)=\"onTagPaste($event)\"\n (blur)=\"onTagBlur()\"\n />\n </span>\n\n @if (clearable() && hasSelection() && !isDisabled()) {\n <span\n class=\"wr-select__clear\"\n role=\"button\"\n [attr.aria-label]=\"resolvedClear()\"\n tabindex=\"-1\"\n (click)=\"clearAll($event)\"\n (keydown.enter)=\"clearAll($event)\"\n (keydown.space)=\"clearAll($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n </div>\n} @else {\n <button\n type=\"button\"\n class=\"wr-select__trigger\"\n role=\"combobox\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-multiselectable]=\"isMulti() ? 'true' : null\"\n [attr.aria-activedescendant]=\"open() ? activeOptionId() || null : null\"\n (click)=\"onTriggerClick()\"\n (keydown)=\"onTriggerKey($event)\"\n (blur)=\"onTouched()\"\n >\n @if (isMulti()) {\n @if (hasSelection()) {\n <span class=\"wr-select__chips\">\n @for (chip of visibleChips(); track $index) {\n <span class=\"wr-select__chip\">\n <span class=\"wr-select__chip-label\">{{ chip.label }}</span>\n <span\n class=\"wr-select__chip-remove\"\n role=\"button\"\n [attr.aria-label]=\"chipRemoveLabel({ label: chip.label })\"\n tabindex=\"-1\"\n (click)=\"removeChip(chip.value, $event)\"\n (keydown.enter)=\"removeChip(chip.value, $event)\"\n (keydown.space)=\"removeChip(chip.value, $event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n </span>\n }\n @if (hiddenChipCount(); as extra) {\n <span class=\"wr-select__chip wr-select__chip--more\">+{{ extra }} more</span>\n }\n </span>\n } @else {\n <span class=\"wr-select__placeholder\">{{ resolvedPlaceholder() }}</span>\n }\n } @else {\n @if (selectedLabel(); as label) {\n <span class=\"wr-select__value\">{{ label }}</span>\n } @else {\n <span class=\"wr-select__placeholder\">{{ resolvedPlaceholder() }}</span>\n }\n }\n\n @if (isMulti() && clearable() && hasSelection() && !isDisabled()) {\n <span\n class=\"wr-select__clear\"\n role=\"button\"\n [attr.aria-label]=\"resolvedClear()\"\n tabindex=\"-1\"\n (click)=\"clearAll($event)\"\n (keydown.enter)=\"clearAll($event)\"\n (keydown.space)=\"clearAll($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n\n <svg\n class=\"wr-icon__svg wr-select__chevron\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n}\n\n<ng-template #panelTpl>\n <div\n class=\"wr-select-panel\"\n role=\"listbox\"\n [attr.id]=\"listboxId\"\n [attr.aria-multiselectable]=\"isMulti() ? 'true' : null\"\n >\n @if (loading()) {\n <div class=\"wr-select-panel__loading\" aria-busy=\"true\">{{ resolvedLoading() }}</div>\n }\n\n <!-- Dynamic options from [options] or async [loader] (search mode).\n Rendered as wr-option so the standard registration / filter\n pipeline applies uniformly. -->\n @for (item of dynamicOptions(); track $index) {\n <wr-option [value]=\"item\">{{ displayWith()(item) }}</wr-option>\n }\n\n <!-- Projected static options. -->\n <ng-content />\n\n @if (hasNoResults()) {\n <div class=\"wr-select-panel__empty\">{{ resolvedNoResults() }}</div>\n }\n </div>\n</ng-template>\n" }]
904
+ }], ctorParameters: () => [], propDecorators: { placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], clearLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearLabel", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], displayWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayWith", required: false }] }], loader: [{ type: i0.Input, args: [{ isSignal: true, alias: "loader", required: false }] }], debounceMs: [{ type: i0.Input, args: [{ isSignal: true, alias: "debounceMs", required: false }] }], minChars: [{ type: i0.Input, args: [{ isSignal: true, alias: "minChars", required: false }] }], freeText: [{ type: i0.Input, args: [{ isSignal: true, alias: "freeText", required: false }] }], noResultsText: [{ type: i0.Input, args: [{ isSignal: true, alias: "noResultsText", required: false }] }], loadingText: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingText", required: false }] }], separators: [{ type: i0.Input, args: [{ isSignal: true, alias: "separators", required: false }] }], allowDuplicates: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowDuplicates", required: false }] }], validate: [{ type: i0.Input, args: [{ isSignal: true, alias: "validate", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], maxItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxItems", required: false }] }], maxTagCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxTagCount", required: false }] }], panelTpl: [{ type: i0.ViewChild, args: ['panelTpl', { ...{ read: TemplateRef }, isSignal: true }] }], tagInputEl: [{ type: i0.ViewChild, args: ['tagInput', { isSignal: true }] }], searchInputEl: [{ type: i0.ViewChild, args: ['searchInput', { isSignal: true }] }] } });
392
905
 
393
906
  /**
394
907
  * @license
@@ -396,10 +909,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.10", ngImpo
396
909
  * Use of this source code is governed by an MIT-style license that can be
397
910
  * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
398
911
  */
912
+ /**
913
+ * Visually groups options under a label inside a `<wr-select>`.
914
+ *
915
+ * @example
916
+ * ```html
917
+ * <wr-option-group label="Sizes">
918
+ * <wr-option value="sm">Small</wr-option>
919
+ * <wr-option value="md">Medium</wr-option>
920
+ * </wr-option-group>
921
+ * ```
922
+ */
923
+ class WrOptionGroup {
924
+ /** Section heading shown above the options. */
925
+ label = input.required(/* @ts-ignore */
926
+ ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
927
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: WrOptionGroup, deps: [], target: i0.ɵɵFactoryTarget.Component });
928
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.2", type: WrOptionGroup, isStandalone: true, selector: "wr-option-group", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "role": "group" }, classAttribute: "wr-option-group" }, ngImport: i0, template: "<div class=\"wr-option-group__label\">{{ label() }}</div>\n<ng-content />\n", encapsulation: i0.ViewEncapsulation.None });
929
+ }
930
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: WrOptionGroup, decorators: [{
931
+ type: Component,
932
+ args: [{ selector: 'wr-option-group', encapsulation: ViewEncapsulation.None, host: { class: 'wr-option-group', role: 'group' }, template: "<div class=\"wr-option-group__label\">{{ label() }}</div>\n<ng-content />\n" }]
933
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }] } });
399
934
 
400
935
  /**
401
936
  * Generated bundle index. Do not edit.
402
937
  */
403
938
 
404
- export { WrOptionComponent, WrOptionGroupComponent, WrSelectComponent };
939
+ export { WR_SELECT, WrOption, WrOptionGroup, WrSelect };
405
940
  //# sourceMappingURL=ngwr-select.mjs.map