ngwr 13.0.1 → 14.0.1

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 (309) hide show
  1. package/README.md +69 -13
  2. package/aurora/aurora.scss +1 -1
  3. package/aurora/styles/_dark.scss +21 -0
  4. package/aurora/styles/_index.scss +11 -30
  5. package/aurora/styles/_rules.scss +25 -0
  6. package/border-glow/styles/_dark.scss +46 -0
  7. package/border-glow/styles/_index.scss +5 -0
  8. package/border-glow/styles/_rules.scss +0 -31
  9. package/button/styles/_index.scss +16 -0
  10. package/color-picker/styles/_index.scss +43 -5
  11. package/event-calendar/styles/_index.scss +15 -0
  12. package/fesm2022/ngwr-action-sheet.mjs +3 -3
  13. package/fesm2022/ngwr-affix.mjs +3 -3
  14. package/fesm2022/ngwr-alert.mjs +7 -7
  15. package/fesm2022/ngwr-alert.mjs.map +1 -1
  16. package/fesm2022/ngwr-anchor.mjs +3 -3
  17. package/fesm2022/ngwr-aurora.mjs +4 -4
  18. package/fesm2022/ngwr-aurora.mjs.map +1 -1
  19. package/fesm2022/ngwr-avatar.mjs +3 -3
  20. package/fesm2022/ngwr-back-top.mjs +14 -4
  21. package/fesm2022/ngwr-back-top.mjs.map +1 -1
  22. package/fesm2022/ngwr-badge.mjs +6 -6
  23. package/fesm2022/ngwr-bar-chart.mjs +3 -3
  24. package/fesm2022/ngwr-blur-text.mjs +16 -5
  25. package/fesm2022/ngwr-blur-text.mjs.map +1 -1
  26. package/fesm2022/ngwr-border-glow.mjs +4 -4
  27. package/fesm2022/ngwr-border-glow.mjs.map +1 -1
  28. package/fesm2022/ngwr-breadcrumbs.mjs +7 -7
  29. package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
  30. package/fesm2022/ngwr-burger.mjs +3 -3
  31. package/fesm2022/ngwr-button.mjs +88 -10
  32. package/fesm2022/ngwr-button.mjs.map +1 -1
  33. package/fesm2022/ngwr-calendar-heatmap.mjs +3 -3
  34. package/fesm2022/ngwr-calendar-testing.mjs +15 -0
  35. package/fesm2022/ngwr-calendar-testing.mjs.map +1 -1
  36. package/fesm2022/ngwr-calendar.mjs +53 -7
  37. package/fesm2022/ngwr-calendar.mjs.map +1 -1
  38. package/fesm2022/ngwr-card.mjs +9 -9
  39. package/fesm2022/ngwr-carousel.mjs +6 -6
  40. package/fesm2022/ngwr-cascader.mjs +4 -4
  41. package/fesm2022/ngwr-cascader.mjs.map +1 -1
  42. package/fesm2022/ngwr-checkbox.mjs +6 -6
  43. package/fesm2022/ngwr-circular-text.mjs +23 -4
  44. package/fesm2022/ngwr-circular-text.mjs.map +1 -1
  45. package/fesm2022/ngwr-click-spark.mjs +3 -3
  46. package/fesm2022/ngwr-clipboard.mjs +3 -3
  47. package/fesm2022/ngwr-collapse.mjs +9 -9
  48. package/fesm2022/ngwr-collapse.mjs.map +1 -1
  49. package/fesm2022/ngwr-color-picker-testing.mjs +23 -7
  50. package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -1
  51. package/fesm2022/ngwr-color-picker.mjs +93 -14
  52. package/fesm2022/ngwr-color-picker.mjs.map +1 -1
  53. package/fesm2022/ngwr-command-palette.mjs +29 -5
  54. package/fesm2022/ngwr-command-palette.mjs.map +1 -1
  55. package/fesm2022/ngwr-compare.mjs +3 -3
  56. package/fesm2022/ngwr-confetti.mjs +3 -3
  57. package/fesm2022/ngwr-context-menu.mjs +18 -12
  58. package/fesm2022/ngwr-context-menu.mjs.map +1 -1
  59. package/fesm2022/ngwr-cookie.mjs +3 -3
  60. package/fesm2022/ngwr-counter.mjs +6 -6
  61. package/fesm2022/ngwr-date-adapters-fns.mjs +104 -36
  62. package/fesm2022/ngwr-date-adapters-fns.mjs.map +1 -1
  63. package/fesm2022/ngwr-date-adapters-luxon.mjs +3 -3
  64. package/fesm2022/ngwr-date-picker-testing.mjs +10 -2
  65. package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
  66. package/fesm2022/ngwr-date-picker.mjs +118 -28
  67. package/fesm2022/ngwr-date-picker.mjs.map +1 -1
  68. package/fesm2022/ngwr-date.mjs +483 -82
  69. package/fesm2022/ngwr-date.mjs.map +1 -1
  70. package/fesm2022/ngwr-decrypt-text.mjs +3 -3
  71. package/fesm2022/ngwr-density.mjs +6 -6
  72. package/fesm2022/ngwr-descriptions.mjs +6 -6
  73. package/fesm2022/ngwr-dialog.mjs +15 -15
  74. package/fesm2022/ngwr-directives.mjs +12 -12
  75. package/fesm2022/ngwr-divider.mjs +3 -3
  76. package/fesm2022/ngwr-donut-chart.mjs +3 -3
  77. package/fesm2022/ngwr-drag-drop.mjs +37 -9
  78. package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
  79. package/fesm2022/ngwr-drawer.mjs +18 -18
  80. package/fesm2022/ngwr-dropdown.mjs +29 -12
  81. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  82. package/fesm2022/ngwr-empty.mjs +3 -3
  83. package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -1
  84. package/fesm2022/ngwr-event-calendar.mjs +6 -6
  85. package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
  86. package/fesm2022/ngwr-falling-text.mjs +3 -3
  87. package/fesm2022/ngwr-file-upload.mjs +55 -15
  88. package/fesm2022/ngwr-file-upload.mjs.map +1 -1
  89. package/fesm2022/ngwr-form-testing.mjs +13 -7
  90. package/fesm2022/ngwr-form-testing.mjs.map +1 -1
  91. package/fesm2022/ngwr-form.mjs +44 -25
  92. package/fesm2022/ngwr-form.mjs.map +1 -1
  93. package/fesm2022/ngwr-fuzzy-text.mjs +3 -3
  94. package/fesm2022/ngwr-gauge.mjs +3 -3
  95. package/fesm2022/ngwr-glitch-text.mjs +3 -3
  96. package/fesm2022/ngwr-gradient-text.mjs +3 -3
  97. package/fesm2022/ngwr-hotkey.mjs +6 -6
  98. package/fesm2022/ngwr-i18n-en.mjs +63 -1
  99. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  100. package/fesm2022/ngwr-i18n-ru.mjs +28 -1
  101. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  102. package/fesm2022/ngwr-i18n.mjs +159 -31
  103. package/fesm2022/ngwr-i18n.mjs.map +1 -1
  104. package/fesm2022/ngwr-icon.mjs +6 -6
  105. package/fesm2022/ngwr-image-cropper.mjs +3 -3
  106. package/fesm2022/ngwr-input-number.mjs +5 -5
  107. package/fesm2022/ngwr-input-number.mjs.map +1 -1
  108. package/fesm2022/ngwr-input-otp.mjs +23 -5
  109. package/fesm2022/ngwr-input-otp.mjs.map +1 -1
  110. package/fesm2022/ngwr-input.mjs +37 -26
  111. package/fesm2022/ngwr-input.mjs.map +1 -1
  112. package/fesm2022/ngwr-keyboard.mjs +3 -3
  113. package/fesm2022/ngwr-knob-testing.mjs.map +1 -1
  114. package/fesm2022/ngwr-knob.mjs +22 -4
  115. package/fesm2022/ngwr-knob.mjs.map +1 -1
  116. package/fesm2022/ngwr-layout.mjs +15 -15
  117. package/fesm2022/ngwr-lightbox.mjs +4 -4
  118. package/fesm2022/ngwr-lightbox.mjs.map +1 -1
  119. package/fesm2022/ngwr-line-chart.mjs +3 -3
  120. package/fesm2022/ngwr-list.mjs +7 -7
  121. package/fesm2022/ngwr-list.mjs.map +1 -1
  122. package/fesm2022/ngwr-loading-bar-router.mjs +73 -0
  123. package/fesm2022/ngwr-loading-bar-router.mjs.map +1 -0
  124. package/fesm2022/ngwr-loading-bar.mjs +27 -38
  125. package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
  126. package/fesm2022/ngwr-markdown.mjs +6 -6
  127. package/fesm2022/ngwr-marquee.mjs +3 -3
  128. package/fesm2022/ngwr-media.mjs +3 -3
  129. package/fesm2022/ngwr-mention.mjs +109 -19
  130. package/fesm2022/ngwr-mention.mjs.map +1 -1
  131. package/fesm2022/ngwr-meta.mjs +6 -6
  132. package/fesm2022/ngwr-meter-group.mjs +3 -3
  133. package/fesm2022/ngwr-overlay.mjs +6 -6
  134. package/fesm2022/ngwr-page-header.mjs +3 -3
  135. package/fesm2022/ngwr-pagination-testing.mjs +11 -10
  136. package/fesm2022/ngwr-pagination-testing.mjs.map +1 -1
  137. package/fesm2022/ngwr-pagination.mjs +67 -32
  138. package/fesm2022/ngwr-pagination.mjs.map +1 -1
  139. package/fesm2022/ngwr-pipes.mjs +21 -21
  140. package/fesm2022/ngwr-platform.mjs +9 -9
  141. package/fesm2022/ngwr-popconfirm.mjs +7 -6
  142. package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
  143. package/fesm2022/ngwr-popover.mjs +7 -6
  144. package/fesm2022/ngwr-popover.mjs.map +1 -1
  145. package/fesm2022/ngwr-progress.mjs +3 -3
  146. package/fesm2022/ngwr-pull-to-refresh.mjs +3 -3
  147. package/fesm2022/ngwr-qr.mjs +3 -3
  148. package/fesm2022/ngwr-radio.mjs +6 -6
  149. package/fesm2022/ngwr-rating.mjs +48 -22
  150. package/fesm2022/ngwr-rating.mjs.map +1 -1
  151. package/fesm2022/ngwr-result.mjs +12 -12
  152. package/fesm2022/ngwr-rotating-text.mjs +3 -3
  153. package/fesm2022/ngwr-scroll.mjs +3 -3
  154. package/fesm2022/ngwr-segmented.mjs +12 -8
  155. package/fesm2022/ngwr-segmented.mjs.map +1 -1
  156. package/fesm2022/ngwr-select.mjs +217 -59
  157. package/fesm2022/ngwr-select.mjs.map +1 -1
  158. package/fesm2022/ngwr-shiny-text.mjs +7 -7
  159. package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
  160. package/fesm2022/ngwr-sidebar.mjs +3 -3
  161. package/fesm2022/ngwr-skeleton.mjs +3 -3
  162. package/fesm2022/ngwr-slider.mjs +12 -8
  163. package/fesm2022/ngwr-slider.mjs.map +1 -1
  164. package/fesm2022/ngwr-sparkline.mjs +3 -3
  165. package/fesm2022/ngwr-speed-dial.mjs +3 -3
  166. package/fesm2022/ngwr-spinner.mjs +3 -3
  167. package/fesm2022/ngwr-splash-cursor.mjs +3 -3
  168. package/fesm2022/ngwr-split-text.mjs +3 -3
  169. package/fesm2022/ngwr-splitter-testing.mjs.map +1 -1
  170. package/fesm2022/ngwr-splitter.mjs +3 -3
  171. package/fesm2022/ngwr-spotlight-card.mjs +7 -7
  172. package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
  173. package/fesm2022/ngwr-squircle.mjs +7 -6
  174. package/fesm2022/ngwr-squircle.mjs.map +1 -1
  175. package/fesm2022/ngwr-star-border.mjs +4 -4
  176. package/fesm2022/ngwr-star-border.mjs.map +1 -1
  177. package/fesm2022/ngwr-statistic.mjs +44 -13
  178. package/fesm2022/ngwr-statistic.mjs.map +1 -1
  179. package/fesm2022/ngwr-stepper.mjs +6 -6
  180. package/fesm2022/ngwr-storage.mjs +11 -3
  181. package/fesm2022/ngwr-storage.mjs.map +1 -1
  182. package/fesm2022/ngwr-switch.mjs +3 -3
  183. package/fesm2022/ngwr-table.mjs +51 -28
  184. package/fesm2022/ngwr-table.mjs.map +1 -1
  185. package/fesm2022/ngwr-tabs-router.mjs +158 -0
  186. package/fesm2022/ngwr-tabs-router.mjs.map +1 -0
  187. package/fesm2022/ngwr-tabs-testing.mjs +13 -11
  188. package/fesm2022/ngwr-tabs-testing.mjs.map +1 -1
  189. package/fesm2022/ngwr-tabs.mjs +104 -16
  190. package/fesm2022/ngwr-tabs.mjs.map +1 -1
  191. package/fesm2022/ngwr-textarea.mjs +46 -6
  192. package/fesm2022/ngwr-textarea.mjs.map +1 -1
  193. package/fesm2022/ngwr-theme.mjs +90 -4
  194. package/fesm2022/ngwr-theme.mjs.map +1 -1
  195. package/fesm2022/ngwr-tilt-card.mjs +7 -7
  196. package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
  197. package/fesm2022/ngwr-timeline.mjs +6 -6
  198. package/fesm2022/ngwr-toast.mjs +116 -11
  199. package/fesm2022/ngwr-toast.mjs.map +1 -1
  200. package/fesm2022/ngwr-toolbar.mjs +3 -3
  201. package/fesm2022/ngwr-tour.mjs +6 -6
  202. package/fesm2022/ngwr-tour.mjs.map +1 -1
  203. package/fesm2022/ngwr-transfer.mjs +4 -4
  204. package/fesm2022/ngwr-transfer.mjs.map +1 -1
  205. package/fesm2022/ngwr-tree.mjs +35 -12
  206. package/fesm2022/ngwr-tree.mjs.map +1 -1
  207. package/fesm2022/ngwr-typewriter.mjs +3 -3
  208. package/fesm2022/ngwr-typography.mjs +3 -3
  209. package/fesm2022/ngwr-utils.mjs +37 -1
  210. package/fesm2022/ngwr-utils.mjs.map +1 -1
  211. package/fesm2022/ngwr-version.mjs +1 -1
  212. package/fesm2022/ngwr-version.mjs.map +1 -1
  213. package/fesm2022/ngwr-virtual-scroll.mjs +3 -3
  214. package/fesm2022/ngwr-waves.mjs +4 -4
  215. package/fesm2022/ngwr-waves.mjs.map +1 -1
  216. package/fesm2022/ngwr-window-testing.mjs +10 -2
  217. package/fesm2022/ngwr-window-testing.mjs.map +1 -1
  218. package/fesm2022/ngwr-window.mjs +15 -15
  219. package/fesm2022/ngwr-window.mjs.map +1 -1
  220. package/i18n/en.json +33 -6
  221. package/i18n/ru.json +33 -6
  222. package/llms-full.txt +29 -20
  223. package/llms.txt +127 -25
  224. package/mcp/catalog.js +3 -3
  225. package/mcp/providers.js +11 -0
  226. package/package.json +9 -1
  227. package/schematics/migrations/v13/index.js +101 -0
  228. package/schematics/migrations/v14/index.js +422 -0
  229. package/schematics/migrations.json +10 -0
  230. package/schematics/page/templates.js +1 -1
  231. package/schematics/use/symbol-map.json +1 -0
  232. package/shiny-text/styles/_dark.scss +20 -0
  233. package/shiny-text/styles/_index.scss +5 -0
  234. package/shiny-text/styles/_rules.scss +0 -4
  235. package/skills/ngwr/SKILL.md +4 -1
  236. package/skills/ngwr/references/catalog.md +2 -0
  237. package/skills/ngwr/references/setup.md +4 -0
  238. package/spotlight-card/styles/_dark.scss +21 -0
  239. package/spotlight-card/styles/_index.scss +5 -0
  240. package/spotlight-card/styles/_rules.scss +0 -6
  241. package/star-border/styles/_dark.scss +21 -0
  242. package/star-border/styles/_index.scss +5 -0
  243. package/star-border/styles/_rules.scss +0 -5
  244. package/stepper/styles/_index.scss +19 -1
  245. package/textarea/styles/_index.scss +14 -6
  246. package/theme/styles/_colors.scss +102 -0
  247. package/theme/styles/_focus.scss +31 -1
  248. package/tilt-card/styles/_dark.scss +21 -0
  249. package/tilt-card/styles/_index.scss +5 -0
  250. package/tilt-card/styles/_rules.scss +0 -5
  251. package/toast/styles/_index.scss +12 -4
  252. package/types/ngwr-alert.d.ts +3 -3
  253. package/types/ngwr-blur-text.d.ts +8 -0
  254. package/types/ngwr-button.d.ts +45 -2
  255. package/types/ngwr-calendar-testing.d.ts +21 -4
  256. package/types/ngwr-calendar.d.ts +37 -0
  257. package/types/ngwr-circular-text.d.ts +9 -0
  258. package/types/ngwr-collapse.d.ts +19 -15
  259. package/types/ngwr-color-picker-testing.d.ts +10 -4
  260. package/types/ngwr-color-picker.d.ts +70 -2
  261. package/types/ngwr-command-palette.d.ts +14 -0
  262. package/types/ngwr-context-menu.d.ts +1 -1
  263. package/types/ngwr-date-adapters-fns.d.ts +71 -29
  264. package/types/ngwr-date-picker-testing.d.ts +206 -185
  265. package/types/ngwr-date-picker.d.ts +7 -0
  266. package/types/ngwr-date.d.ts +143 -13
  267. package/types/ngwr-drag-drop.d.ts +19 -0
  268. package/types/ngwr-dropdown.d.ts +18 -2
  269. package/types/ngwr-event-calendar-testing.d.ts +12 -9
  270. package/types/ngwr-file-upload.d.ts +31 -3
  271. package/types/ngwr-form-testing.d.ts +13 -7
  272. package/types/ngwr-form.d.ts +47 -12
  273. package/types/ngwr-i18n.d.ts +89 -7
  274. package/types/ngwr-input-number.d.ts +1 -1
  275. package/types/ngwr-input-otp.d.ts +3 -0
  276. package/types/ngwr-input.d.ts +13 -5
  277. package/types/ngwr-knob-testing.d.ts +8 -4
  278. package/types/ngwr-knob.d.ts +18 -0
  279. package/types/ngwr-loading-bar-router.d.ts +35 -0
  280. package/types/ngwr-loading-bar.d.ts +19 -9
  281. package/types/ngwr-mention.d.ts +13 -0
  282. package/types/ngwr-pagination-testing.d.ts +11 -10
  283. package/types/ngwr-pagination.d.ts +39 -10
  284. package/types/ngwr-popconfirm.d.ts +1 -1
  285. package/types/ngwr-popover.d.ts +1 -1
  286. package/types/ngwr-rating.d.ts +25 -4
  287. package/types/ngwr-segmented.d.ts +1 -0
  288. package/types/ngwr-select.d.ts +110 -19
  289. package/types/ngwr-slider.d.ts +1 -0
  290. package/types/ngwr-splitter-testing.d.ts +9 -4
  291. package/types/ngwr-squircle.d.ts +1 -1
  292. package/types/ngwr-statistic.d.ts +22 -2
  293. package/types/ngwr-storage.d.ts +39 -1
  294. package/types/ngwr-table.d.ts +24 -5
  295. package/types/ngwr-tabs-router.d.ts +67 -0
  296. package/types/ngwr-tabs-testing.d.ts +12 -10
  297. package/types/ngwr-tabs.d.ts +103 -6
  298. package/types/ngwr-textarea.d.ts +29 -0
  299. package/types/ngwr-theme.d.ts +92 -2
  300. package/types/ngwr-toast.d.ts +53 -0
  301. package/types/ngwr-tree.d.ts +10 -2
  302. package/types/ngwr-utils.d.ts +33 -1
  303. package/types/ngwr-version.d.ts +1 -1
  304. package/types/ngwr-window-testing.d.ts +11 -3
  305. package/types/ngwr-window.d.ts +11 -4
  306. package/waves/styles/_dark.scss +19 -0
  307. package/waves/styles/_index.scss +5 -0
  308. package/waves/styles/_rules.scss +0 -3
  309. package/window/styles/_index.scss +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-color-picker-testing.mjs","sources":["../../../projects/lib/color-picker/testing/wr-color-picker-harness.ts","../../../projects/lib/color-picker/testing/wr-color-picker-trigger-harness.ts","../../../projects/lib/color-picker/testing/ngwr-color-picker-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, type TestElement } from '@angular/cdk/testing';\n\nimport { WrSegmentedHarness } from 'ngwr/segmented/testing';\n\nimport type { WrColorPickerHarnessFilters, WrColorPickerTab, WrColorPickerThumbs } from './interfaces';\n\n/**\n * One labelled numeric field — internal. A `TestElement` cannot be queried into, so\n * pairing an input with the label printed under it means a harness anchored on the\n * `<label>` that holds both.\n */\nclass FieldHarness extends ComponentHarness {\n static hostSelector = '.wr-color-picker__field';\n\n async getLabel(): Promise<string> {\n return (await this.locatorFor('.wr-color-picker__field-label')()).text();\n }\n\n async getInput(): Promise<TestElement> {\n return this.locatorFor('input')();\n }\n\n async getValue(): Promise<string> {\n return (await this.getInput()).getProperty<string>('value');\n }\n}\n\n/** The `left: <n>%` / `top: <n>%` a thumb carries, out of its inline `style`. */\nconst THUMB_POS = {\n left: /(?:^|;)\\s*left\\s*:\\s*([\\d.]+)%/,\n top: /(?:^|;)\\s*top\\s*:\\s*([\\d.]+)%/,\n} as const;\n\n/** One thumb's declared offset. See {@link WrColorPickerHarness.getThumbs} for why this is not `getCssValue()`. */\nasync function percent(thumb: TestElement, side: 'left' | 'top', surface: string): Promise<number> {\n const style = await thumb.getAttribute('style');\n const match = THUMB_POS[side].exec(style ?? '');\n\n if (!match) {\n throw new Error(\n `WrColorPickerHarness.getThumbs(): the ${surface} thumb carries no inline ${side} percentage ` +\n `(style: \"${style ?? ''}\"). The component writes one on every render, so its absence is the bug — ` +\n `and the stylesheet declares \\`${side}: 0\\`, so reporting 0 would read as a thumb parked at the origin.`\n );\n }\n return Number.parseFloat(match[1]);\n}\n\n/**\n * Test harness for `<wr-color-picker>` — the inline picker, and the one the\n * `[wrColorPickerTrigger]` directive mounts in an overlay.\n *\n * **What this harness will not do: drag.** The SV canvas and the hue / alpha\n * sliders are pointer surfaces that read `getBoundingClientRect()` and divide by\n * its width. A unit test has no layout, so every box is 0×0 and the division is\n * `NaN` — a `setHue()` built on a synthetic pointer event would report success and\n * write nothing, or write garbage. The numeric fields reach the same state and are\n * also the only keyboard path the component has, so they are the whole write API\n * here. {@link getThumbs} reads where the thumbs ARE, from the percentages the\n * component writes inline, which is how a spec checks a surface followed a value.\n *\n * **And it will not read the preview swatch.** The preview paints the current\n * colour as a CSS background, and jsdom normalises that to `rgb(…)` — dropping the\n * alpha channel entirely, so `#ff880080` and `#ff8800ff` come back identical. A\n * `getColor()` reading it would answer one thing in a unit test and another in a\n * browser. {@link getHex} is the colour: the field holds the canonical 8-digit\n * string, alpha included, whatever `format` writes into `value`.\n *\n * @example\n * ```ts\n * const picker = await loader.getHarness(WrColorPickerHarness);\n *\n * await picker.setHex('#3969e2');\n * expect(await picker.getHex()).toBe('#3969e2ff');\n *\n * await picker.setTab('rgb');\n * expect(await picker.getRgb()).toEqual({ r: 57, g: 105, b: 226 });\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrColorPickerHarness extends ComponentHarness {\n static hostSelector = 'wr-color-picker';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrColorPickerHarnessFilters = {}): HarnessPredicate<WrColorPickerHarness> {\n return new HarnessPredicate(WrColorPickerHarness, options)\n .addOption('color', options.color, (harness, color) => HarnessPredicate.stringMatches(harness.readHex(), color))\n .addOption('tab', options.tab, async (harness, tab) => (await harness.getTab()) === tab)\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n private readonly hexField = this.locatorForOptional('.wr-color-picker__inputs--hex input');\n private readonly tabs = this.locatorFor(WrSegmentedHarness.with({ selector: '.wr-color-picker__tabs' }));\n\n /**\n * The tab switcher, as a {@link WrSegmentedHarness}.\n *\n * Handed back rather than re-queried: the switcher IS a `<wr-segmented>`, which\n * already knows that the roving tab stop is not the selection and how the sliding\n * thumb reports itself. Re-reading its buttons here would be a second, worse copy.\n */\n async getTabs(): Promise<WrSegmentedHarness> {\n return this.tabs();\n }\n\n /** Which set of numeric fields is showing. */\n async getTab(): Promise<WrColorPickerTab> {\n const label = await (await this.tabs()).getSelectedLabel();\n return (label ?? 'HEX').toLowerCase() as WrColorPickerTab;\n }\n\n /** Show a different set of numeric fields. An already-active tab is left alone. */\n async setTab(tab: WrColorPickerTab): Promise<void> {\n if ((await this.getTab()) === tab) return;\n await (await this.tabs()).select({ label: tab.toUpperCase() });\n }\n\n /**\n * The canonical hex string, as the HEX field shows it — 8 digits while `alpha` is\n * on, 6 while it is off.\n *\n * This is the picker's colour, and deliberately not the same thing as its `value`:\n * `format` decides whether `value` is written as hex, `rgb()` or `hsl()`, while the\n * field always shows hex. A spec that cares about the emitted string should read\n * the bound signal; one that cares about the COLOUR should read this.\n *\n * Throws off the HEX tab, where the field does not exist — see {@link setTab}.\n */\n async getHex(): Promise<string> {\n const field = await this.hexField();\n if (!field) {\n throw new Error(\n `WrColorPickerHarness.getHex(): the HEX field is not rendered — the picker is showing the ` +\n `${(await this.getTab()).toUpperCase()} tab. Call setTab('hex') first.`\n );\n }\n return field.getProperty<string>('value');\n }\n\n /**\n * Type a colour into the HEX field.\n *\n * Typed rather than assigned, because that is the path the component is built\n * around: it re-parses on every keystroke and applies the first spelling that\n * parses, so a partial `#ff8` commits a colour on its way to `#ff8800`. An\n * unparseable string leaves the committed colour ALONE and lives in the field\n * until {@link blurHex} snaps it back — assert that rather than expecting a throw.\n */\n async setHex(value: string): Promise<void> {\n const field = await this.requireHexField('setHex');\n await field.clear();\n await field.sendKeys(value);\n }\n\n /**\n * Blur the HEX field — re-syncing it with the committed colour and emitting\n * `touch`.\n *\n * The re-sync is the visible half: a field left holding text that never parsed\n * goes back to the canonical hex here, and nowhere else.\n */\n async blurHex(): Promise<void> {\n await (await this.requireHexField('blurHex')).blur();\n }\n\n /** Whether the HEX field currently holds focus. */\n async isHexFocused(): Promise<boolean> {\n return (await this.requireHexField('isHexFocused')).isFocused();\n }\n\n /** The three RGB channels as the RGB tab shows them. Throws off that tab. */\n async getRgb(): Promise<{ r: number; g: number; b: number }> {\n const [r, g, b] = await this.channelValues('rgb', ['R', 'G', 'B']);\n return { r, g, b };\n }\n\n /** Write one RGB channel, 0–255. Out-of-range numbers are clamped by the component. */\n async setRgbChannel(channel: 'r' | 'g' | 'b', value: number): Promise<void> {\n await this.writeChannel('rgb', channel.toUpperCase(), value);\n }\n\n /**\n * Hue, saturation and lightness as the HSL tab shows them — degrees and whole\n * percents, already rounded for display. Throws off that tab.\n */\n async getHsl(): Promise<{ h: number; s: number; l: number }> {\n const [h, s, l] = await this.channelValues('hsl', ['H', 'S%', 'L%']);\n return { h, s, l };\n }\n\n /** Write one HSL channel — `h` in degrees, `s` / `l` in percent. */\n async setHslChannel(channel: 'h' | 's' | 'l', value: number): Promise<void> {\n await this.writeChannel('hsl', channel === 'h' ? 'H' : `${channel.toUpperCase()}%`, value);\n }\n\n /**\n * Alpha as the whole percent the numeric tabs show, or `null` when the picker was\n * built without alpha.\n *\n * Only the RGB and HSL tabs carry the field — the HEX tab spells alpha into the\n * hex string instead — so this throws on the HEX tab rather than answering `null`,\n * which would read as \"this picker has no alpha\".\n */\n async getAlphaPercent(): Promise<number | null> {\n const tab = await this.getTab();\n if (tab === 'hex') {\n throw new Error(\n 'WrColorPickerHarness.getAlphaPercent(): the HEX tab has no alpha field — the alpha is the last two ' +\n \"digits of getHex(). Call setTab('rgb') or setTab('hsl') to read it as a percent.\"\n );\n }\n if (!(await this.hasAlpha())) return null;\n const [alpha] = await this.channelValues(tab, ['A%']);\n return alpha;\n }\n\n /** Write alpha as a whole percent, 0–100. */\n async setAlphaPercent(value: number): Promise<void> {\n const tab = await this.getTab();\n if (tab === 'hex') {\n throw new Error(\n \"WrColorPickerHarness.setAlphaPercent(): the HEX tab has no alpha field. Call setTab('rgb') first, \" +\n 'or write the two alpha digits through setHex().'\n );\n }\n await this.writeChannel(tab, 'A%', value);\n }\n\n /**\n * Whether the picker offers alpha at all.\n *\n * From the host modifier, which is the one answer that holds on every tab: the\n * alpha SLIDER is always rendered when it is on, while the `A%` field only exists\n * on the two numeric tabs.\n */\n async hasAlpha(): Promise<boolean> {\n return (await this.host()).hasClass('wr-color-picker--alpha');\n }\n\n /**\n * Whether the picker refuses interaction.\n *\n * Read from the modifier the host paints rather than from a field's `disabled`\n * property, because the fields come and go with the tab while the state does not —\n * and the drag surfaces, which have no `disabled` of their own, are gated by the\n * same signal.\n */\n async isDisabled(): Promise<boolean> {\n return (await this.host()).hasClass('wr-color-picker--disabled');\n }\n\n /** The preset colours offered under the fields, as the strings the consumer passed. */\n async getSwatches(): Promise<string[]> {\n const swatches = await this.locatorForAll('.wr-color-picker__swatch')();\n return Promise.all(swatches.map(async swatch => (await swatch.getAttribute('aria-label')) ?? ''));\n }\n\n /**\n * Click a preset.\n *\n * Matched on the string the consumer passed, which is what the button publishes as\n * its accessible name — the rendered background is a CSS colour and would come back\n * normalised and alpha-stripped. Throws when nothing matches, naming what is offered.\n */\n async pickSwatch(color: string): Promise<void> {\n const swatches = await this.locatorForAll('.wr-color-picker__swatch')();\n for (const swatch of swatches) {\n if ((await swatch.getAttribute('aria-label')) === color) {\n await swatch.click();\n return;\n }\n }\n throw new Error(\n `WrColorPickerHarness.pickSwatch(): no swatch for \"${color}\". The picker offers: ` +\n `${(await this.getSwatches()).join(', ')}.`\n );\n }\n\n /**\n * Where the three thumbs sit, in percent along their surfaces.\n *\n * The component writes these as inline `left` / `top` percentages, so they are\n * readable with no layout at all — which makes them the only evidence a spec has\n * that the surfaces followed a colour set through the fields. A measured position\n * would be zero for all three.\n *\n * Read off the `style` ATTRIBUTE rather than through `getCssValue()`, and the\n * difference is not cosmetic: `getCssValue()` is `getComputedStyle()`, and `left` /\n * `top` on a positioned element resolve to the USED value — so the declared `40%`\n * comes straight back in jsdom and arrives as pixels in a real browser, and the same\n * harness answered 40 here and 96 under a browser runner with nothing to say which\n * one you got. The stylesheet's own `top: 0; left: 0` is a second way that read lies,\n * turning a thumb whose binding is gone into one parked at the origin.\n */\n async getThumbs(): Promise<WrColorPickerThumbs> {\n const sv = await this.locatorFor('.wr-color-picker__sv .wr-color-picker__thumb')();\n const hue = await this.locatorFor('.wr-color-picker__slider--hue .wr-color-picker__thumb')();\n const alpha = await this.locatorForOptional('.wr-color-picker__slider--alpha .wr-color-picker__thumb')();\n\n return {\n sv: { x: await percent(sv, 'left', 'SV'), y: await percent(sv, 'top', 'SV') },\n hue: await percent(hue, 'left', 'hue'),\n alpha: alpha ? await percent(alpha, 'left', 'alpha') : null,\n };\n }\n\n /** The HEX field's value without the tab check — for filters, which must not throw. */\n private async readHex(): Promise<string> {\n const field = await this.hexField();\n return field ? field.getProperty<string>('value') : '';\n }\n\n private async requireHexField(method: string): Promise<TestElement> {\n const field = await this.hexField();\n if (!field) {\n throw new Error(\n `WrColorPickerHarness.${method}(): the HEX field is not rendered — the picker is showing the ` +\n `${(await this.getTab()).toUpperCase()} tab. Call setTab('hex') first.`\n );\n }\n return field;\n }\n\n private async channelValues(tab: WrColorPickerTab, labels: readonly string[]): Promise<number[]> {\n await this.requireTab(tab, 'read');\n const inputs = await this.inputsByLabel();\n\n return labels.map(label => {\n const input = inputs.get(label);\n if (!input) throw new Error(`WrColorPickerHarness: the ${tab.toUpperCase()} tab has no \"${label}\" field.`);\n return Number(input.value);\n });\n }\n\n private async writeChannel(tab: WrColorPickerTab, label: string, value: number): Promise<void> {\n await this.requireTab(tab, 'write');\n const inputs = await this.inputsByLabel();\n const found = inputs.get(label);\n if (!found) {\n throw new Error(\n `WrColorPickerHarness: the ${tab.toUpperCase()} tab has no \"${label}\" field. ${\n label === 'A%' ? 'This picker was built with `[alpha]=\"false\"`.' : ''\n }`\n );\n }\n\n // Set and dispatch once, rather than clearing and typing. Each of these inputs\n // is bound to `[value]`, and the component commits on every `input` event — so\n // typing 128 would land 1, then 12, then 128, and CLEARING would land 0 first,\n // because `Number('')` is 0 rather than NaN. A spec that asked for one colour\n // would get four, the first of them black.\n await found.element.setInputValue(String(value));\n await found.element.dispatchEvent('input');\n }\n\n private async requireTab(tab: WrColorPickerTab, verb: string): Promise<void> {\n const current = await this.getTab();\n if (current !== tab) {\n throw new Error(\n `WrColorPickerHarness: cannot ${verb} the ${tab.toUpperCase()} channels while the picker is showing ` +\n `the ${current.toUpperCase()} tab — those fields are not rendered. Call setTab('${tab}') first.`\n );\n }\n }\n\n /** Every numeric input of the current tab, keyed by the label printed under it. */\n private async inputsByLabel(): Promise<Map<string, { element: TestElement; value: string }>> {\n const found = new Map<string, { element: TestElement; value: string }>();\n\n for (const field of await this.locatorForAll(FieldHarness)()) {\n const label = await field.getLabel();\n found.set(label, { element: await field.getInput(), value: await field.getValue() });\n }\n return found;\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, TestKey } from '@angular/cdk/testing';\n\nimport type { WrColorPickerTriggerHarnessFilters } from './interfaces';\nimport { WrColorPickerHarness } from './wr-color-picker-harness';\n\n/**\n * Test harness for `[wrColorPickerTrigger]` — the element that opens a\n * `<wr-color-picker>` in an overlay.\n *\n * The trigger stays in your fixture; the picker it opens does not. So this harness\n * is loaded from the fixture loader like any element, and {@link getPicker} reaches\n * ACROSS into the overlay container for you — scoped by the panel id the trigger\n * publishes through `aria-controls`, never by `.wr-color-picker-overlay`, which\n * answers with whichever trigger opened first.\n *\n * @example\n * ```ts\n * const trigger = await loader.getHarness(WrColorPickerTriggerHarness);\n * const picker = await trigger.open();\n *\n * await picker.setHex('#3969e2');\n * await trigger.sendEscape();\n * expect(await trigger.isOpen()).toBe(false);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrColorPickerTriggerHarness extends ComponentHarness {\n static hostSelector = '[wrColorPickerTrigger]';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrColorPickerTriggerHarnessFilters = {}): HarnessPredicate<WrColorPickerTriggerHarness> {\n return new HarnessPredicate(WrColorPickerTriggerHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getText(), text))\n .addOption('open', options.open, async (harness, open) => (await harness.isOpen()) === open);\n }\n\n /** The trigger's own text, trimmed. */\n async getText(): Promise<string> {\n return (await this.host()).text();\n }\n\n /** Whether the picker is showing, from the trigger's `aria-expanded`. */\n async isOpen(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-expanded')) === 'true';\n }\n\n /**\n * Whether the trigger refuses to open.\n *\n * From `aria-disabled`, which the directive writes for its own `disabled` input.\n * The host is usually a `<button>`, but the directive does not touch its `disabled`\n * PROPERTY — a consumer who binds that as well turns the element off at the DOM\n * level, which is a stronger thing and reads here too.\n */\n async isDisabled(): Promise<boolean> {\n const host = await this.host();\n return (await host.getAttribute('aria-disabled')) === 'true' || (await host.getProperty<boolean>('disabled'));\n }\n\n /**\n * The picker this trigger opened, or `null` while it is closed.\n *\n * Scoped by the id in `aria-controls`, so two triggers open at once — which\n * nothing in the component prevents, since only an outside click closes one —\n * answer with their own panels rather than with whichever came first.\n */\n async getPicker(): Promise<WrColorPickerHarness | null> {\n const id = await (await this.host()).getAttribute('aria-controls');\n if (!id) return null;\n\n return this.documentRootLocatorFactory().locatorForOptional(WrColorPickerHarness.with({ ancestor: `#${id}` }))();\n }\n\n /**\n * Open the picker and hand it back. An already-open trigger is left alone.\n *\n * Throws on a disabled trigger rather than answering `null` three lines later:\n * `toggle()` returns early while `disabled` is set, so the click lands and nothing\n * happens at all.\n */\n async open(): Promise<WrColorPickerHarness> {\n if (!(await this.isOpen())) {\n if (await this.isDisabled()) {\n throw new Error(\n `WrColorPickerTriggerHarness.open(): \"${await this.getText()}\" is disabled — the directive turns its ` +\n 'own toggle away, so the click would land and change nothing.'\n );\n }\n await (await this.host()).click();\n }\n\n const picker = await this.getPicker();\n if (!picker) {\n throw new Error(\n 'WrColorPickerTriggerHarness.open(): the trigger reports itself open but published no panel to find. ' +\n 'That is a broken `aria-controls` pairing, not a timing problem.'\n );\n }\n return picker;\n }\n\n /** Close the picker. An already-closed trigger is left alone. */\n async close(): Promise<void> {\n if (!(await this.isOpen())) return;\n await (await this.host()).click();\n }\n\n /** Click the trigger once, whichever way that takes it. */\n async toggle(): Promise<void> {\n await (await this.host()).click();\n }\n\n /**\n * Press Escape.\n *\n * Sent to the TRIGGER, and it still reaches the picker: the overlay listens through\n * the CDK's keyboard dispatcher, which keeps one document listener and routes to\n * the top-most overlay — so this does not depend on where focus happens to be.\n */\n async sendEscape(): Promise<void> {\n await (await this.host()).sendKeys(TestKey.ESCAPE);\n }\n\n /** The role the trigger claims to open — `dialog`. */\n async getHasPopup(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-haspopup');\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ngwr-color-picker-testing.mjs","sources":["../../../projects/lib/color-picker/testing/wr-color-picker-harness.ts","../../../projects/lib/color-picker/testing/wr-color-picker-trigger-harness.ts","../../../projects/lib/color-picker/testing/ngwr-color-picker-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, type TestElement } from '@angular/cdk/testing';\n\nimport { WrSegmentedHarness } from 'ngwr/segmented/testing';\n\nimport type { WrColorPickerHarnessFilters, WrColorPickerTab, WrColorPickerThumbs } from './interfaces';\n\n/**\n * One labelled numeric field — internal. A `TestElement` cannot be queried into, so\n * pairing an input with the label printed under it means a harness anchored on the\n * `<label>` that holds both.\n */\nclass FieldHarness extends ComponentHarness {\n static hostSelector = '.wr-color-picker__field';\n\n async getLabel(): Promise<string> {\n return (await this.locatorFor('.wr-color-picker__field-label')()).text();\n }\n\n async getInput(): Promise<TestElement> {\n return this.locatorFor('input')();\n }\n\n async getValue(): Promise<string> {\n return (await this.getInput()).getProperty<string>('value');\n }\n}\n\n/**\n * The percentage a thumb carries, out of its inline `style`.\n *\n * Three properties and not two, because the surfaces do not agree on the inline\n * axis and that disagreement is the contract: the SV canvas is a picture and\n * keeps a physical `left`, while the hue and alpha tracks are one-value sliders\n * that mirror under `dir=\"rtl\"` and so are placed with `inset-inline-start`.\n * Reading `left` off a slider thumb therefore finds nothing, which is exactly\n * what should happen if one of them ever regresses to the physical property.\n */\nconst THUMB_POS = {\n left: /(?:^|;)\\s*left\\s*:\\s*([\\d.]+)%/,\n top: /(?:^|;)\\s*top\\s*:\\s*([\\d.]+)%/,\n 'inset-inline-start': /(?:^|;)\\s*inset-inline-start\\s*:\\s*([\\d.]+)%/,\n} as const;\n\n/** One thumb's declared offset. See {@link WrColorPickerHarness.getThumbs} for why this is not `getCssValue()`. */\nasync function percent(thumb: TestElement, side: keyof typeof THUMB_POS, surface: string): Promise<number> {\n const style = await thumb.getAttribute('style');\n const match = THUMB_POS[side].exec(style ?? '');\n\n if (!match) {\n throw new Error(\n `WrColorPickerHarness.getThumbs(): the ${surface} thumb carries no inline ${side} percentage ` +\n `(style: \"${style ?? ''}\"). The component writes one on every render, so its absence is the bug — ` +\n `and the stylesheet declares \\`${side}: 0\\`, so reporting 0 would read as a thumb parked at the origin.`\n );\n }\n return Number.parseFloat(match[1]);\n}\n\n/**\n * Test harness for `<wr-color-picker>` — the inline picker, and the one the\n * `[wrColorPickerTrigger]` directive mounts in an overlay.\n *\n * **What this harness will not do: drag.** The SV canvas and the hue / alpha\n * sliders are pointer surfaces that read `getBoundingClientRect()` and divide by\n * its width. A unit test has no layout, so every box is 0×0 and the division is\n * `NaN` — a `setHue()` built on a synthetic pointer event would report success and\n * write nothing, or write garbage. The numeric fields reach the same state and are\n * also the only keyboard path the component has, so they are the whole write API\n * here. {@link getThumbs} reads where the thumbs ARE, from the percentages the\n * component writes inline, which is how a spec checks a surface followed a value.\n *\n * **And it will not read the preview swatch.** The preview paints the current\n * colour as a CSS background, and jsdom normalises that to `rgb(…)` — dropping the\n * alpha channel entirely, so `#ff880080` and `#ff8800ff` come back identical. A\n * `getColor()` reading it would answer one thing in a unit test and another in a\n * browser. {@link getHex} is the colour: the field holds the canonical 8-digit\n * string, alpha included, whatever `format` writes into `value`.\n *\n * @example\n * ```ts\n * const picker = await loader.getHarness(WrColorPickerHarness);\n *\n * await picker.setHex('#3969e2');\n * expect(await picker.getHex()).toBe('#3969e2ff');\n *\n * await picker.setTab('rgb');\n * expect(await picker.getRgb()).toEqual({ r: 57, g: 105, b: 226 });\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrColorPickerHarness extends ComponentHarness {\n static hostSelector = 'wr-color-picker';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrColorPickerHarnessFilters = {}): HarnessPredicate<WrColorPickerHarness> {\n return new HarnessPredicate(WrColorPickerHarness, options)\n .addOption('color', options.color, (harness, color) => HarnessPredicate.stringMatches(harness.readHex(), color))\n .addOption('tab', options.tab, async (harness, tab) => (await harness.getTab()) === tab)\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n private readonly hexField = this.locatorForOptional('.wr-color-picker__inputs--hex input');\n private readonly tabs = this.locatorFor(WrSegmentedHarness.with({ selector: '.wr-color-picker__tabs' }));\n\n /**\n * The tab switcher, as a {@link WrSegmentedHarness}.\n *\n * Handed back rather than re-queried: the switcher IS a `<wr-segmented>`, which\n * already knows that the roving tab stop is not the selection and how the sliding\n * thumb reports itself. Re-reading its buttons here would be a second, worse copy.\n */\n async getTabs(): Promise<WrSegmentedHarness> {\n return this.tabs();\n }\n\n /** Which set of numeric fields is showing. */\n async getTab(): Promise<WrColorPickerTab> {\n const label = await (await this.tabs()).getSelectedLabel();\n return (label ?? 'HEX').toLowerCase() as WrColorPickerTab;\n }\n\n /** Show a different set of numeric fields. An already-active tab is left alone. */\n async setTab(tab: WrColorPickerTab): Promise<void> {\n if ((await this.getTab()) === tab) return;\n await (await this.tabs()).select({ label: tab.toUpperCase() });\n }\n\n /**\n * The canonical hex string, as the HEX field shows it — 8 digits while `alpha` is\n * on, 6 while it is off.\n *\n * This is the picker's colour, and deliberately not the same thing as its `value`:\n * `format` decides whether `value` is written as hex, `rgb()` or `hsl()`, while the\n * field always shows hex. A spec that cares about the emitted string should read\n * the bound signal; one that cares about the COLOUR should read this.\n *\n * Throws off the HEX tab, where the field does not exist — see {@link setTab}.\n */\n async getHex(): Promise<string> {\n const field = await this.hexField();\n if (!field) {\n throw new Error(\n `WrColorPickerHarness.getHex(): the HEX field is not rendered — the picker is showing the ` +\n `${(await this.getTab()).toUpperCase()} tab. Call setTab('hex') first.`\n );\n }\n return field.getProperty<string>('value');\n }\n\n /**\n * Type a colour into the HEX field.\n *\n * Typed rather than assigned, because that is the path the component is built\n * around: it re-parses on every keystroke and applies the first spelling that\n * parses, so a partial `#ff8` commits a colour on its way to `#ff8800`. An\n * unparseable string leaves the committed colour ALONE and lives in the field\n * until {@link blurHex} snaps it back — assert that rather than expecting a throw.\n */\n async setHex(value: string): Promise<void> {\n const field = await this.requireHexField('setHex');\n await field.clear();\n await field.sendKeys(value);\n }\n\n /**\n * Blur the HEX field — re-syncing it with the committed colour and emitting\n * `touch`.\n *\n * The re-sync is the visible half: a field left holding text that never parsed\n * goes back to the canonical hex here, and nowhere else.\n */\n async blurHex(): Promise<void> {\n await (await this.requireHexField('blurHex')).blur();\n }\n\n /** Whether the HEX field currently holds focus. */\n async isHexFocused(): Promise<boolean> {\n return (await this.requireHexField('isHexFocused')).isFocused();\n }\n\n /** The three RGB channels as the RGB tab shows them. Throws off that tab. */\n async getRgb(): Promise<{ r: number; g: number; b: number }> {\n const [r, g, b] = await this.channelValues('rgb', ['R', 'G', 'B']);\n return { r, g, b };\n }\n\n /** Write one RGB channel, 0–255. Out-of-range numbers are clamped by the component. */\n async setRgbChannel(channel: 'r' | 'g' | 'b', value: number): Promise<void> {\n await this.writeChannel('rgb', channel.toUpperCase(), value);\n }\n\n /**\n * Hue, saturation and lightness as the HSL tab shows them — degrees and whole\n * percents, already rounded for display. Throws off that tab.\n */\n async getHsl(): Promise<{ h: number; s: number; l: number }> {\n const [h, s, l] = await this.channelValues('hsl', ['H', 'S%', 'L%']);\n return { h, s, l };\n }\n\n /** Write one HSL channel — `h` in degrees, `s` / `l` in percent. */\n async setHslChannel(channel: 'h' | 's' | 'l', value: number): Promise<void> {\n await this.writeChannel('hsl', channel === 'h' ? 'H' : `${channel.toUpperCase()}%`, value);\n }\n\n /**\n * Alpha as the whole percent the numeric tabs show, or `null` when the picker was\n * built without alpha.\n *\n * Only the RGB and HSL tabs carry the field — the HEX tab spells alpha into the\n * hex string instead — so this throws on the HEX tab rather than answering `null`,\n * which would read as \"this picker has no alpha\".\n */\n async getAlphaPercent(): Promise<number | null> {\n const tab = await this.getTab();\n if (tab === 'hex') {\n throw new Error(\n 'WrColorPickerHarness.getAlphaPercent(): the HEX tab has no alpha field — the alpha is the last two ' +\n \"digits of getHex(). Call setTab('rgb') or setTab('hsl') to read it as a percent.\"\n );\n }\n if (!(await this.hasAlpha())) return null;\n const [alpha] = await this.channelValues(tab, ['A%']);\n return alpha;\n }\n\n /** Write alpha as a whole percent, 0–100. */\n async setAlphaPercent(value: number): Promise<void> {\n const tab = await this.getTab();\n if (tab === 'hex') {\n throw new Error(\n \"WrColorPickerHarness.setAlphaPercent(): the HEX tab has no alpha field. Call setTab('rgb') first, \" +\n 'or write the two alpha digits through setHex().'\n );\n }\n await this.writeChannel(tab, 'A%', value);\n }\n\n /**\n * Whether the picker offers alpha at all.\n *\n * From the host modifier, which is the one answer that holds on every tab: the\n * alpha SLIDER is always rendered when it is on, while the `A%` field only exists\n * on the two numeric tabs.\n */\n async hasAlpha(): Promise<boolean> {\n return (await this.host()).hasClass('wr-color-picker--alpha');\n }\n\n /**\n * Whether the picker refuses interaction.\n *\n * Read from the modifier the host paints rather than from a field's `disabled`\n * property, because the fields come and go with the tab while the state does not —\n * and the drag surfaces, which have no `disabled` of their own, are gated by the\n * same signal.\n */\n async isDisabled(): Promise<boolean> {\n return (await this.host()).hasClass('wr-color-picker--disabled');\n }\n\n /** The preset colours offered under the fields, as the strings the consumer passed. */\n async getSwatches(): Promise<string[]> {\n const swatches = await this.locatorForAll('.wr-color-picker__swatch')();\n return Promise.all(swatches.map(async swatch => (await swatch.getAttribute('aria-label')) ?? ''));\n }\n\n /**\n * Click a preset.\n *\n * Matched on the string the consumer passed, which is what the button publishes as\n * its accessible name — the rendered background is a CSS colour and would come back\n * normalised and alpha-stripped. Throws when nothing matches, naming what is offered.\n */\n async pickSwatch(color: string): Promise<void> {\n const swatches = await this.locatorForAll('.wr-color-picker__swatch')();\n for (const swatch of swatches) {\n if ((await swatch.getAttribute('aria-label')) === color) {\n await swatch.click();\n return;\n }\n }\n throw new Error(\n `WrColorPickerHarness.pickSwatch(): no swatch for \"${color}\". The picker offers: ` +\n `${(await this.getSwatches()).join(', ')}.`\n );\n }\n\n /**\n * Where the three thumbs sit, in percent along their surfaces.\n *\n * The component writes these as inline percentages, so they are readable with no\n * layout at all — which makes them the only evidence a spec has that the surfaces\n * followed a colour set through the fields. A measured position would be zero for\n * all three.\n *\n * The number is always \"along the surface from its own start\", so it is direction\n * free: the SV canvas reports a physical `left` because it does not mirror, and\n * the two tracks report `inset-inline-start` because they do. An RTL spec reads\n * the same value as an LTR one, which is the point — what mirrors is where that\n * fraction lands on screen, not the fraction.\n *\n * Read off the `style` ATTRIBUTE rather than through `getCssValue()`, and the\n * difference is not cosmetic: `getCssValue()` is `getComputedStyle()`, and `left` /\n * `top` on a positioned element resolve to the USED value — so the declared `40%`\n * comes straight back in jsdom and arrives as pixels in a real browser, and the same\n * harness answered 40 here and 96 under a browser runner with nothing to say which\n * one you got. The stylesheet's own `top: 0; left: 0` is a second way that read lies,\n * turning a thumb whose binding is gone into one parked at the origin.\n */\n async getThumbs(): Promise<WrColorPickerThumbs> {\n const sv = await this.locatorFor('.wr-color-picker__sv .wr-color-picker__thumb')();\n const hue = await this.locatorFor('.wr-color-picker__slider--hue .wr-color-picker__thumb')();\n const alpha = await this.locatorForOptional('.wr-color-picker__slider--alpha .wr-color-picker__thumb')();\n\n return {\n sv: { x: await percent(sv, 'left', 'SV'), y: await percent(sv, 'top', 'SV') },\n hue: await percent(hue, 'inset-inline-start', 'hue'),\n alpha: alpha ? await percent(alpha, 'inset-inline-start', 'alpha') : null,\n };\n }\n\n /** The HEX field's value without the tab check — for filters, which must not throw. */\n private async readHex(): Promise<string> {\n const field = await this.hexField();\n return field ? field.getProperty<string>('value') : '';\n }\n\n private async requireHexField(method: string): Promise<TestElement> {\n const field = await this.hexField();\n if (!field) {\n throw new Error(\n `WrColorPickerHarness.${method}(): the HEX field is not rendered — the picker is showing the ` +\n `${(await this.getTab()).toUpperCase()} tab. Call setTab('hex') first.`\n );\n }\n return field;\n }\n\n private async channelValues(tab: WrColorPickerTab, labels: readonly string[]): Promise<number[]> {\n await this.requireTab(tab, 'read');\n const inputs = await this.inputsByLabel();\n\n return labels.map(label => {\n const input = inputs.get(label);\n if (!input) throw new Error(`WrColorPickerHarness: the ${tab.toUpperCase()} tab has no \"${label}\" field.`);\n return Number(input.value);\n });\n }\n\n private async writeChannel(tab: WrColorPickerTab, label: string, value: number): Promise<void> {\n await this.requireTab(tab, 'write');\n const inputs = await this.inputsByLabel();\n const found = inputs.get(label);\n if (!found) {\n throw new Error(\n `WrColorPickerHarness: the ${tab.toUpperCase()} tab has no \"${label}\" field. ${\n label === 'A%' ? 'This picker was built with `[alpha]=\"false\"`.' : ''\n }`\n );\n }\n\n // Set and dispatch once, rather than clearing and typing. Each of these inputs\n // is bound to `[value]`, and the component commits on every `input` event — so\n // typing 128 would land 1, then 12, then 128, and CLEARING would land 0 first,\n // because `Number('')` is 0 rather than NaN. A spec that asked for one colour\n // would get four, the first of them black.\n await found.element.setInputValue(String(value));\n await found.element.dispatchEvent('input');\n }\n\n private async requireTab(tab: WrColorPickerTab, verb: string): Promise<void> {\n const current = await this.getTab();\n if (current !== tab) {\n throw new Error(\n `WrColorPickerHarness: cannot ${verb} the ${tab.toUpperCase()} channels while the picker is showing ` +\n `the ${current.toUpperCase()} tab — those fields are not rendered. Call setTab('${tab}') first.`\n );\n }\n }\n\n /** Every numeric input of the current tab, keyed by the label printed under it. */\n private async inputsByLabel(): Promise<Map<string, { element: TestElement; value: string }>> {\n const found = new Map<string, { element: TestElement; value: string }>();\n\n for (const field of await this.locatorForAll(FieldHarness)()) {\n const label = await field.getLabel();\n found.set(label, { element: await field.getInput(), value: await field.getValue() });\n }\n return found;\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, TestKey } from '@angular/cdk/testing';\n\nimport type { WrColorPickerTriggerHarnessFilters } from './interfaces';\nimport { WrColorPickerHarness } from './wr-color-picker-harness';\n\n/**\n * Test harness for `[wrColorPickerTrigger]` — the element that opens a\n * `<wr-color-picker>` in an overlay.\n *\n * The trigger stays in your fixture; the picker it opens does not. So this harness\n * is loaded from the fixture loader like any element, and {@link getPicker} reaches\n * ACROSS into the overlay container for you — scoped by the panel id the trigger\n * publishes through `aria-controls`, never by `.wr-color-picker-overlay`, which\n * answers with whichever trigger opened first.\n *\n * @example\n * ```ts\n * const trigger = await loader.getHarness(WrColorPickerTriggerHarness);\n * const picker = await trigger.open();\n *\n * await picker.setHex('#3969e2');\n * await trigger.sendEscape();\n * expect(await trigger.isOpen()).toBe(false);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrColorPickerTriggerHarness extends ComponentHarness {\n static hostSelector = '[wrColorPickerTrigger]';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrColorPickerTriggerHarnessFilters = {}): HarnessPredicate<WrColorPickerTriggerHarness> {\n return new HarnessPredicate(WrColorPickerTriggerHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getText(), text))\n .addOption('open', options.open, async (harness, open) => (await harness.isOpen()) === open);\n }\n\n /** The trigger's own text, trimmed. */\n async getText(): Promise<string> {\n return (await this.host()).text();\n }\n\n /** Whether the picker is showing, from the trigger's `aria-expanded`. */\n async isOpen(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-expanded')) === 'true';\n }\n\n /**\n * Whether the trigger refuses to open.\n *\n * From `aria-disabled`, which the directive writes for its own `disabled` input.\n * The host is usually a `<button>`, but the directive does not touch its `disabled`\n * PROPERTY — a consumer who binds that as well turns the element off at the DOM\n * level, which is a stronger thing and reads here too.\n */\n async isDisabled(): Promise<boolean> {\n const host = await this.host();\n return (await host.getAttribute('aria-disabled')) === 'true' || (await host.getProperty<boolean>('disabled'));\n }\n\n /**\n * The picker this trigger opened, or `null` while it is closed.\n *\n * Scoped by the id in `aria-controls`, so two triggers open at once — which\n * nothing in the component prevents, since only an outside click closes one —\n * answer with their own panels rather than with whichever came first.\n */\n async getPicker(): Promise<WrColorPickerHarness | null> {\n const id = await (await this.host()).getAttribute('aria-controls');\n if (!id) return null;\n\n return this.documentRootLocatorFactory().locatorForOptional(WrColorPickerHarness.with({ ancestor: `#${id}` }))();\n }\n\n /**\n * Open the picker and hand it back. An already-open trigger is left alone.\n *\n * Throws on a disabled trigger rather than answering `null` three lines later:\n * `toggle()` returns early while `disabled` is set, so the click lands and nothing\n * happens at all.\n */\n async open(): Promise<WrColorPickerHarness> {\n if (!(await this.isOpen())) {\n if (await this.isDisabled()) {\n throw new Error(\n `WrColorPickerTriggerHarness.open(): \"${await this.getText()}\" is disabled — the directive turns its ` +\n 'own toggle away, so the click would land and change nothing.'\n );\n }\n await (await this.host()).click();\n }\n\n const picker = await this.getPicker();\n if (!picker) {\n throw new Error(\n 'WrColorPickerTriggerHarness.open(): the trigger reports itself open but published no panel to find. ' +\n 'That is a broken `aria-controls` pairing, not a timing problem.'\n );\n }\n return picker;\n }\n\n /** Close the picker. An already-closed trigger is left alone. */\n async close(): Promise<void> {\n if (!(await this.isOpen())) return;\n await (await this.host()).click();\n }\n\n /** Click the trigger once, whichever way that takes it. */\n async toggle(): Promise<void> {\n await (await this.host()).click();\n }\n\n /**\n * Press Escape.\n *\n * Sent to the TRIGGER, and it still reaches the picker: the overlay listens through\n * the CDK's keyboard dispatcher, which keeps one document listener and routes to\n * the top-most overlay — so this does not depend on where focus happens to be.\n */\n async sendEscape(): Promise<void> {\n await (await this.host()).sendKeys(TestKey.ESCAPE);\n }\n\n /** The role the trigger claims to open — `dialog`. */\n async getHasPopup(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-haspopup');\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1,7 +1,8 @@
1
+ import { Directionality } from '@angular/cdk/bidi';
1
2
  import { coerceBooleanProperty } from '@angular/cdk/coercion';
2
3
  import { DecimalPipe } from '@angular/common';
3
4
  import * as i0 from '@angular/core';
4
- import { input, model, output, signal, computed, effect, ViewEncapsulation, Component, inject, ElementRef, ViewContainerRef, DestroyRef, Directive } from '@angular/core';
5
+ import { input, model, output, signal, computed, inject, effect, ViewEncapsulation, Component, ElementRef, ViewContainerRef, DestroyRef, Directive } from '@angular/core';
5
6
  import { useFormFieldAria, WR_FORM_FIELD } from 'ngwr/form';
6
7
  import { useI18nFormatter, readI18nText } from 'ngwr/i18n';
7
8
  import { WrSegmented } from 'ngwr/segmented';
@@ -436,6 +437,18 @@ class WrColorPicker {
436
437
  return `${r}, ${g}, ${b}`;
437
438
  }, /* @ts-ignore */
438
439
  ...(ngDevMode ? [{ debugName: "rgbCss" }] : /* istanbul ignore next */ []));
440
+ /**
441
+ * Ambient reading direction, for the two SLIDERS only — see `updateFromEvent`
442
+ * for why the SV canvas is not in that set.
443
+ *
444
+ * Optional so a bare `TestBed` — or any consumer that never set a direction —
445
+ * needs no provider; `Directionality` is root-provided, so `null` only ever
446
+ * means "nobody asked", which is LTR. Read inside the handlers rather than
447
+ * cached: the thumbs are placed with `inset-inline-start`, so the tracks mirror
448
+ * from CSS alone and a runtime flip needs no subscription to
449
+ * `Directionality.change`.
450
+ */
451
+ dir = inject(Directionality, { optional: true });
439
452
  /** Thumb positions in % for each surface. */
440
453
  svThumbX = computed(() => this.s() * 100, /* @ts-ignore */
441
454
  ...(ngDevMode ? [{ debugName: "svThumbX" }] : /* istanbul ignore next */ []));
@@ -466,11 +479,38 @@ class WrColorPicker {
466
479
  ...(ngDevMode ? [{ debugName: "resolvedAreaLabel" }] : /* istanbul ignore next */ []));
467
480
  resolvedHueLabel = readI18nText('colorPicker.hue', 'Hue');
468
481
  resolvedAlphaLabel = readI18nText('colorPicker.alpha', 'Opacity');
469
- tabOptions = [
470
- { value: 'hex', label: 'HEX' },
471
- { value: 'rgb', label: 'RGB' },
472
- { value: 'hsl', label: 'HSL' },
473
- ];
482
+ /**
483
+ * Every visible word in this component that is not a slider name.
484
+ *
485
+ * The format strip and the nine channel captions were literals — three in
486
+ * TypeScript, the rest in the template — beside a `colorPicker` catalog
487
+ * section that already existed and held the slider names. So the section was
488
+ * there and these had simply not been put in it, which is why a fully Russian
489
+ * picker still read `HEX RGB HSL` over `R G B`. Each caption is one string
490
+ * INCLUDING its `%`, because a locale that spaces the sign differently has to
491
+ * move the letter and the sign together.
492
+ *
493
+ * Signals, not strings: they follow a locale switch, and a catalog that lands
494
+ * a microtask after the first change-detection pass.
495
+ */
496
+ channelHexLabel = readI18nText('colorPicker.channelHex', 'HEX');
497
+ channelRedLabel = readI18nText('colorPicker.channelRed', 'R');
498
+ channelGreenLabel = readI18nText('colorPicker.channelGreen', 'G');
499
+ channelBlueLabel = readI18nText('colorPicker.channelBlue', 'B');
500
+ channelHueLabel = readI18nText('colorPicker.channelHue', 'H');
501
+ channelSaturationLabel = readI18nText('colorPicker.channelSaturation', 'S%');
502
+ channelLightnessLabel = readI18nText('colorPicker.channelLightness', 'L%');
503
+ channelAlphaLabel = readI18nText('colorPicker.channelAlpha', 'A%');
504
+ formatHexLabel = readI18nText('colorPicker.formatHex', 'HEX');
505
+ formatRgbLabel = readI18nText('colorPicker.formatRgb', 'RGB');
506
+ formatHslLabel = readI18nText('colorPicker.formatHsl', 'HSL');
507
+ /** A `computed`, because `WrSegmentedOption.label` is a plain string. */
508
+ tabOptions = computed(() => [
509
+ { value: 'hex', label: this.formatHexLabel() },
510
+ { value: 'rgb', label: this.formatRgbLabel() },
511
+ { value: 'hsl', label: this.formatHslLabel() },
512
+ ], /* @ts-ignore */
513
+ ...(ngDevMode ? [{ debugName: "tabOptions" }] : /* istanbul ignore next */ []));
474
514
  classes = computed(() => {
475
515
  const parts = ['wr-color-picker'];
476
516
  if (this.alpha())
@@ -553,20 +593,29 @@ class WrColorPicker {
553
593
  /**
554
594
  * Hue and alpha are real one-value sliders, so they take the APG slider keys —
555
595
  * the same set `wr-knob` and `wr-slider` implement, including Home / End.
596
+ *
597
+ * The horizontal arrows name a SIDE of the track and so follow the reading
598
+ * direction, exactly as `wr-slider`'s do; the vertical pair names a value's
599
+ * direction and never mirrors.
556
600
  */
557
601
  onSliderKeydown(event, surface) {
558
602
  if (this.disabled() || this.readonly())
559
603
  return;
560
604
  const step = event.shiftKey ? KEY_STEP_COARSE : KEY_STEP;
605
+ const inline = this.dir?.value === 'rtl' ? -step : step;
561
606
  const max = surface === 'hue' ? 360 : 100;
562
607
  const current = surface === 'hue' ? this.h() : this.a() * 100;
563
608
  let next;
564
609
  switch (event.key) {
565
610
  case 'ArrowRight':
611
+ next = current + inline;
612
+ break;
566
613
  case 'ArrowUp':
567
614
  next = current + step;
568
615
  break;
569
616
  case 'ArrowLeft':
617
+ next = current - inline;
618
+ break;
570
619
  case 'ArrowDown':
571
620
  next = current - step;
572
621
  break;
@@ -591,6 +640,10 @@ class WrColorPicker {
591
640
  * The SV canvas is two axes at once, so the keys follow the picture: left and
592
641
  * right run along saturation, up and down along brightness — the directions
593
642
  * the thumb visibly moves. Shift takes ten percent at a time.
643
+ *
644
+ * Not mirrored under `dir="rtl"`, unlike the two sliders above, and for the
645
+ * reason given on `fraction()`: the canvas is a picture rather than a track, so
646
+ * its gradient does not flip and neither may the keys that walk it.
594
647
  */
595
648
  onAreaKeydown(event) {
596
649
  if (this.disabled() || this.readonly())
@@ -619,9 +672,34 @@ class WrColorPicker {
619
672
  onSurfaceBlur() {
620
673
  this.touch.emit();
621
674
  }
675
+ /**
676
+ * The pointer's position along a surface, as a 0–1 fraction.
677
+ *
678
+ * Measured from whichever edge the surface STARTS at, and the two surfaces do
679
+ * not agree on what that means — deliberately.
680
+ *
681
+ * The hue and alpha bars are one-value tracks with `role="slider"`, the same
682
+ * widget `wr-slider` is, so they mirror: their thumbs are placed with
683
+ * `inset-inline-start` and their gradients are flipped by a `[dir='rtl']` rule,
684
+ * which makes the reader's start the physical right edge under `dir="rtl"`.
685
+ * Opacity 100% used to sit at the physical right in both directions, i.e. at
686
+ * the reader's START in RTL, while the format strip in the same panel mirrored
687
+ * correctly.
688
+ *
689
+ * The SV canvas does NOT mirror, and stays measured from `rect.left`. It is a
690
+ * two-axis colour FIELD, not a track — the component says so itself by giving
691
+ * it `role="group"` rather than `role="slider"`, because it carries two values
692
+ * and a slider has one. A picture with no start and no end has nothing to
693
+ * mirror, and flipping it would only move white to the other side of an image
694
+ * every colour picker in the world draws the same way.
695
+ */
696
+ fraction(event, rect, surface) {
697
+ const offset = surface !== 'sv' && this.dir?.value === 'rtl' ? rect.right - event.clientX : event.clientX - rect.left;
698
+ return clamp(offset / rect.width, 0, 1);
699
+ }
622
700
  updateFromEvent(event, target, surface) {
623
701
  const rect = target.getBoundingClientRect();
624
- const x = clamp((event.clientX - rect.left) / rect.width, 0, 1);
702
+ const x = this.fraction(event, rect, surface);
625
703
  if (surface === 'sv') {
626
704
  const y = clamp((event.clientY - rect.top) / rect.height, 0, 1);
627
705
  this.s.set(x);
@@ -720,16 +798,16 @@ class WrColorPicker {
720
798
  this.a.set(hsv.a);
721
799
  this.emit();
722
800
  }
723
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrColorPicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
724
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: WrColorPicker, isStandalone: true, selector: "wr-color-picker", inputs: { alpha: { classPropertyName: "alpha", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, swatches: { classPropertyName: "swatches", publicName: "swatches", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch" }, host: { properties: { "class": "classes()", "style.--wr-color-picker-hue": "hueCss()", "style.--wr-color-picker-rgb": "rgbCss()" } }, providers: [{ provide: WR_FORM_FIELD, useValue: null }], ngImport: i0, template: "<!--\n `role=\"group\"` rather than `role=\"slider\"`, because this surface holds TWO\n values and a slider has one `aria-valuenow`; and rather than\n `role=\"application\"`, which would switch the reader out of its own browse mode\n for the whole subtree \u2014 the call `wr-image-cropper` already made. A group has\n no value of its own, so the two percentages ride in its accessible name, which\n is where a reader querying the focused element finds them.\n-->\n<div\n class=\"wr-color-picker__sv\"\n role=\"group\"\n [attr.aria-label]=\"resolvedAreaLabel()\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n (pointerdown)=\"onPointerDown($event, 'sv')\"\n (keydown)=\"onAreaKeydown($event)\"\n (blur)=\"onSurfaceBlur()\"\n>\n <span class=\"wr-color-picker__thumb\" [style.left.%]=\"svThumbX()\" [style.top.%]=\"svThumbY()\"></span>\n</div>\n\n<div\n class=\"wr-color-picker__slider wr-color-picker__slider--hue\"\n role=\"slider\"\n aria-valuemin=\"0\"\n aria-valuemax=\"360\"\n [attr.aria-label]=\"resolvedHueLabel()\"\n [attr.aria-valuenow]=\"hueValue()\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n (pointerdown)=\"onPointerDown($event, 'hue')\"\n (keydown)=\"onSliderKeydown($event, 'hue')\"\n (blur)=\"onSurfaceBlur()\"\n>\n <span class=\"wr-color-picker__thumb wr-color-picker__thumb--slider\" [style.left.%]=\"hueThumbX()\"></span>\n</div>\n\n@if (alpha()) {\n <div\n class=\"wr-color-picker__slider wr-color-picker__slider--alpha\"\n role=\"slider\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n [attr.aria-label]=\"resolvedAlphaLabel()\"\n [attr.aria-valuenow]=\"alphaPercent()\"\n [attr.aria-valuetext]=\"`${alphaPercent()}%`\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n (pointerdown)=\"onPointerDown($event, 'alpha')\"\n (keydown)=\"onSliderKeydown($event, 'alpha')\"\n (blur)=\"onSurfaceBlur()\"\n >\n <span class=\"wr-color-picker__thumb wr-color-picker__thumb--slider\" [style.left.%]=\"alphaThumbX()\"></span>\n </div>\n}\n\n<div class=\"wr-color-picker__chrome\">\n <span class=\"wr-color-picker__preview\" [style.background]=\"hex()\"></span>\n <wr-segmented\n class=\"wr-color-picker__tabs\"\n [options]=\"tabOptions\"\n [value]=\"activeTab()\"\n (valueChange)=\"onTabChange($event)\"\n />\n</div>\n\n@switch (activeTab()) {\n @case ('hex') {\n <div class=\"wr-color-picker__inputs wr-color-picker__inputs--hex\">\n <label class=\"wr-color-picker__field wr-color-picker__field--wide\">\n <input\n type=\"text\"\n spellcheck=\"false\"\n autocomplete=\"off\"\n [value]=\"hexInput()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onHexInput($event)\"\n (blur)=\"onHexBlur()\"\n />\n <span class=\"wr-color-picker__field-label\">HEX</span>\n </label>\n </div>\n }\n @case ('rgb') {\n <div class=\"wr-color-picker__inputs\">\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"255\"\n [value]=\"rgb().r\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onRgbChannel('r', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">R</span>\n </label>\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"255\"\n [value]=\"rgb().g\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onRgbChannel('g', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">G</span>\n </label>\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"255\"\n [value]=\"rgb().b\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onRgbChannel('b', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">B</span>\n </label>\n @if (alpha()) {\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"100\"\n [value]=\"alphaPercent()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onAlphaPercent($event)\"\n />\n <span class=\"wr-color-picker__field-label\">A%</span>\n </label>\n }\n </div>\n }\n @case ('hsl') {\n <div class=\"wr-color-picker__inputs\">\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"360\"\n [value]=\"hslView().h | number: '1.0-0'\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onHslChannel('h', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">H</span>\n </label>\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"100\"\n [value]=\"hslView().s * 100 | number: '1.0-0'\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onHslChannel('s', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">S%</span>\n </label>\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"100\"\n [value]=\"hslView().l * 100 | number: '1.0-0'\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onHslChannel('l', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">L%</span>\n </label>\n @if (alpha()) {\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"100\"\n [value]=\"alphaPercent()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onAlphaPercent($event)\"\n />\n <span class=\"wr-color-picker__field-label\">A%</span>\n </label>\n }\n </div>\n }\n}\n\n@if (swatches().length > 0) {\n <div class=\"wr-color-picker__swatches\">\n @for (color of swatches(); track color) {\n <button\n type=\"button\"\n class=\"wr-color-picker__swatch\"\n [style.background]=\"color\"\n [attr.aria-label]=\"color\"\n [disabled]=\"disabled() || readonly()\"\n (click)=\"onSwatchClick(color)\"\n ></button>\n }\n </div>\n}\n", dependencies: [{ kind: "component", type: WrSegmented, selector: "wr-segmented", inputs: ["options", "value", "disabled", "readonly", "size"], outputs: ["valueChange", "touch"] }, { kind: "pipe", type: DecimalPipe, name: "number" }], encapsulation: i0.ViewEncapsulation.None });
801
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: WrColorPicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
802
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: WrColorPicker, isStandalone: true, selector: "wr-color-picker", inputs: { alpha: { classPropertyName: "alpha", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, swatches: { classPropertyName: "swatches", publicName: "swatches", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch" }, host: { properties: { "class": "classes()", "style.--wr-color-picker-hue": "hueCss()", "style.--wr-color-picker-rgb": "rgbCss()" } }, providers: [{ provide: WR_FORM_FIELD, useValue: null }], ngImport: i0, template: "<!--\n `role=\"group\"` rather than `role=\"slider\"`, because this surface holds TWO\n values and a slider has one `aria-valuenow`; and rather than\n `role=\"application\"`, which would switch the reader out of its own browse mode\n for the whole subtree \u2014 the call `wr-image-cropper` already made. A group has\n no value of its own, so the two percentages ride in its accessible name, which\n is where a reader querying the focused element finds them.\n-->\n<div\n class=\"wr-color-picker__sv\"\n role=\"group\"\n [attr.aria-label]=\"resolvedAreaLabel()\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n (pointerdown)=\"onPointerDown($event, 'sv')\"\n (keydown)=\"onAreaKeydown($event)\"\n (blur)=\"onSurfaceBlur()\"\n>\n <span class=\"wr-color-picker__thumb\" [style.left.%]=\"svThumbX()\" [style.top.%]=\"svThumbY()\"></span>\n</div>\n\n<div\n class=\"wr-color-picker__slider wr-color-picker__slider--hue\"\n role=\"slider\"\n aria-valuemin=\"0\"\n aria-valuemax=\"360\"\n [attr.aria-label]=\"resolvedHueLabel()\"\n [attr.aria-valuenow]=\"hueValue()\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n (pointerdown)=\"onPointerDown($event, 'hue')\"\n (keydown)=\"onSliderKeydown($event, 'hue')\"\n (blur)=\"onSurfaceBlur()\"\n>\n <!--\n `inset-inline-start`, not `left`: this is a one-value track, so it mirrors\n under `dir=\"rtl\"` the way `wr-slider` does. The SV thumb above keeps `left` \u2014\n the canvas is a picture and does not flip.\n -->\n <span class=\"wr-color-picker__thumb wr-color-picker__thumb--slider\" [style.inset-inline-start.%]=\"hueThumbX()\"></span>\n</div>\n\n@if (alpha()) {\n <div\n class=\"wr-color-picker__slider wr-color-picker__slider--alpha\"\n role=\"slider\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n [attr.aria-label]=\"resolvedAlphaLabel()\"\n [attr.aria-valuenow]=\"alphaPercent()\"\n [attr.aria-valuetext]=\"`${alphaPercent()}%`\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n (pointerdown)=\"onPointerDown($event, 'alpha')\"\n (keydown)=\"onSliderKeydown($event, 'alpha')\"\n (blur)=\"onSurfaceBlur()\"\n >\n <span\n class=\"wr-color-picker__thumb wr-color-picker__thumb--slider\"\n [style.inset-inline-start.%]=\"alphaThumbX()\"\n ></span>\n </div>\n}\n\n<div class=\"wr-color-picker__chrome\">\n <span class=\"wr-color-picker__preview\" [style.background]=\"hex()\"></span>\n <wr-segmented\n class=\"wr-color-picker__tabs\"\n [options]=\"tabOptions()\"\n [value]=\"activeTab()\"\n (valueChange)=\"onTabChange($event)\"\n />\n</div>\n\n@switch (activeTab()) {\n @case ('hex') {\n <div class=\"wr-color-picker__inputs wr-color-picker__inputs--hex\">\n <label class=\"wr-color-picker__field wr-color-picker__field--wide\">\n <input\n type=\"text\"\n spellcheck=\"false\"\n autocomplete=\"off\"\n [value]=\"hexInput()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onHexInput($event)\"\n (blur)=\"onHexBlur()\"\n />\n <span class=\"wr-color-picker__field-label\">{{ channelHexLabel() }}</span>\n </label>\n </div>\n }\n @case ('rgb') {\n <div class=\"wr-color-picker__inputs\">\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"255\"\n [value]=\"rgb().r\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onRgbChannel('r', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">{{ channelRedLabel() }}</span>\n </label>\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"255\"\n [value]=\"rgb().g\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onRgbChannel('g', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">{{ channelGreenLabel() }}</span>\n </label>\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"255\"\n [value]=\"rgb().b\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onRgbChannel('b', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">{{ channelBlueLabel() }}</span>\n </label>\n @if (alpha()) {\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"100\"\n [value]=\"alphaPercent()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onAlphaPercent($event)\"\n />\n <span class=\"wr-color-picker__field-label\">{{ channelAlphaLabel() }}</span>\n </label>\n }\n </div>\n }\n @case ('hsl') {\n <div class=\"wr-color-picker__inputs\">\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"360\"\n [value]=\"hslView().h | number: '1.0-0'\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onHslChannel('h', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">{{ channelHueLabel() }}</span>\n </label>\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"100\"\n [value]=\"hslView().s * 100 | number: '1.0-0'\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onHslChannel('s', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">{{ channelSaturationLabel() }}</span>\n </label>\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"100\"\n [value]=\"hslView().l * 100 | number: '1.0-0'\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onHslChannel('l', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">{{ channelLightnessLabel() }}</span>\n </label>\n @if (alpha()) {\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"100\"\n [value]=\"alphaPercent()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onAlphaPercent($event)\"\n />\n <span class=\"wr-color-picker__field-label\">{{ channelAlphaLabel() }}</span>\n </label>\n }\n </div>\n }\n}\n\n@if (swatches().length > 0) {\n <div class=\"wr-color-picker__swatches\">\n @for (color of swatches(); track color) {\n <button\n type=\"button\"\n class=\"wr-color-picker__swatch\"\n [style.background]=\"color\"\n [attr.aria-label]=\"color\"\n [disabled]=\"disabled() || readonly()\"\n (click)=\"onSwatchClick(color)\"\n ></button>\n }\n </div>\n}\n", dependencies: [{ kind: "component", type: WrSegmented, selector: "wr-segmented", inputs: ["options", "value", "disabled", "readonly", "size"], outputs: ["valueChange", "touch"] }, { kind: "pipe", type: DecimalPipe, name: "number" }], encapsulation: i0.ViewEncapsulation.None });
725
803
  }
726
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrColorPicker, decorators: [{
804
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: WrColorPicker, decorators: [{
727
805
  type: Component,
728
806
  args: [{ selector: 'wr-color-picker', encapsulation: ViewEncapsulation.None, host: {
729
807
  '[class]': 'classes()',
730
808
  '[style.--wr-color-picker-hue]': 'hueCss()',
731
809
  '[style.--wr-color-picker-rgb]': 'rgbCss()',
732
- }, providers: [{ provide: WR_FORM_FIELD, useValue: null }], imports: [DecimalPipe, WrSegmented], template: "<!--\n `role=\"group\"` rather than `role=\"slider\"`, because this surface holds TWO\n values and a slider has one `aria-valuenow`; and rather than\n `role=\"application\"`, which would switch the reader out of its own browse mode\n for the whole subtree \u2014 the call `wr-image-cropper` already made. A group has\n no value of its own, so the two percentages ride in its accessible name, which\n is where a reader querying the focused element finds them.\n-->\n<div\n class=\"wr-color-picker__sv\"\n role=\"group\"\n [attr.aria-label]=\"resolvedAreaLabel()\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n (pointerdown)=\"onPointerDown($event, 'sv')\"\n (keydown)=\"onAreaKeydown($event)\"\n (blur)=\"onSurfaceBlur()\"\n>\n <span class=\"wr-color-picker__thumb\" [style.left.%]=\"svThumbX()\" [style.top.%]=\"svThumbY()\"></span>\n</div>\n\n<div\n class=\"wr-color-picker__slider wr-color-picker__slider--hue\"\n role=\"slider\"\n aria-valuemin=\"0\"\n aria-valuemax=\"360\"\n [attr.aria-label]=\"resolvedHueLabel()\"\n [attr.aria-valuenow]=\"hueValue()\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n (pointerdown)=\"onPointerDown($event, 'hue')\"\n (keydown)=\"onSliderKeydown($event, 'hue')\"\n (blur)=\"onSurfaceBlur()\"\n>\n <span class=\"wr-color-picker__thumb wr-color-picker__thumb--slider\" [style.left.%]=\"hueThumbX()\"></span>\n</div>\n\n@if (alpha()) {\n <div\n class=\"wr-color-picker__slider wr-color-picker__slider--alpha\"\n role=\"slider\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n [attr.aria-label]=\"resolvedAlphaLabel()\"\n [attr.aria-valuenow]=\"alphaPercent()\"\n [attr.aria-valuetext]=\"`${alphaPercent()}%`\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n (pointerdown)=\"onPointerDown($event, 'alpha')\"\n (keydown)=\"onSliderKeydown($event, 'alpha')\"\n (blur)=\"onSurfaceBlur()\"\n >\n <span class=\"wr-color-picker__thumb wr-color-picker__thumb--slider\" [style.left.%]=\"alphaThumbX()\"></span>\n </div>\n}\n\n<div class=\"wr-color-picker__chrome\">\n <span class=\"wr-color-picker__preview\" [style.background]=\"hex()\"></span>\n <wr-segmented\n class=\"wr-color-picker__tabs\"\n [options]=\"tabOptions\"\n [value]=\"activeTab()\"\n (valueChange)=\"onTabChange($event)\"\n />\n</div>\n\n@switch (activeTab()) {\n @case ('hex') {\n <div class=\"wr-color-picker__inputs wr-color-picker__inputs--hex\">\n <label class=\"wr-color-picker__field wr-color-picker__field--wide\">\n <input\n type=\"text\"\n spellcheck=\"false\"\n autocomplete=\"off\"\n [value]=\"hexInput()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onHexInput($event)\"\n (blur)=\"onHexBlur()\"\n />\n <span class=\"wr-color-picker__field-label\">HEX</span>\n </label>\n </div>\n }\n @case ('rgb') {\n <div class=\"wr-color-picker__inputs\">\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"255\"\n [value]=\"rgb().r\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onRgbChannel('r', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">R</span>\n </label>\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"255\"\n [value]=\"rgb().g\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onRgbChannel('g', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">G</span>\n </label>\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"255\"\n [value]=\"rgb().b\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onRgbChannel('b', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">B</span>\n </label>\n @if (alpha()) {\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"100\"\n [value]=\"alphaPercent()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onAlphaPercent($event)\"\n />\n <span class=\"wr-color-picker__field-label\">A%</span>\n </label>\n }\n </div>\n }\n @case ('hsl') {\n <div class=\"wr-color-picker__inputs\">\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"360\"\n [value]=\"hslView().h | number: '1.0-0'\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onHslChannel('h', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">H</span>\n </label>\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"100\"\n [value]=\"hslView().s * 100 | number: '1.0-0'\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onHslChannel('s', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">S%</span>\n </label>\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"100\"\n [value]=\"hslView().l * 100 | number: '1.0-0'\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onHslChannel('l', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">L%</span>\n </label>\n @if (alpha()) {\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"100\"\n [value]=\"alphaPercent()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onAlphaPercent($event)\"\n />\n <span class=\"wr-color-picker__field-label\">A%</span>\n </label>\n }\n </div>\n }\n}\n\n@if (swatches().length > 0) {\n <div class=\"wr-color-picker__swatches\">\n @for (color of swatches(); track color) {\n <button\n type=\"button\"\n class=\"wr-color-picker__swatch\"\n [style.background]=\"color\"\n [attr.aria-label]=\"color\"\n [disabled]=\"disabled() || readonly()\"\n (click)=\"onSwatchClick(color)\"\n ></button>\n }\n </div>\n}\n" }]
810
+ }, providers: [{ provide: WR_FORM_FIELD, useValue: null }], imports: [DecimalPipe, WrSegmented], template: "<!--\n `role=\"group\"` rather than `role=\"slider\"`, because this surface holds TWO\n values and a slider has one `aria-valuenow`; and rather than\n `role=\"application\"`, which would switch the reader out of its own browse mode\n for the whole subtree \u2014 the call `wr-image-cropper` already made. A group has\n no value of its own, so the two percentages ride in its accessible name, which\n is where a reader querying the focused element finds them.\n-->\n<div\n class=\"wr-color-picker__sv\"\n role=\"group\"\n [attr.aria-label]=\"resolvedAreaLabel()\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n (pointerdown)=\"onPointerDown($event, 'sv')\"\n (keydown)=\"onAreaKeydown($event)\"\n (blur)=\"onSurfaceBlur()\"\n>\n <span class=\"wr-color-picker__thumb\" [style.left.%]=\"svThumbX()\" [style.top.%]=\"svThumbY()\"></span>\n</div>\n\n<div\n class=\"wr-color-picker__slider wr-color-picker__slider--hue\"\n role=\"slider\"\n aria-valuemin=\"0\"\n aria-valuemax=\"360\"\n [attr.aria-label]=\"resolvedHueLabel()\"\n [attr.aria-valuenow]=\"hueValue()\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n (pointerdown)=\"onPointerDown($event, 'hue')\"\n (keydown)=\"onSliderKeydown($event, 'hue')\"\n (blur)=\"onSurfaceBlur()\"\n>\n <!--\n `inset-inline-start`, not `left`: this is a one-value track, so it mirrors\n under `dir=\"rtl\"` the way `wr-slider` does. The SV thumb above keeps `left` \u2014\n the canvas is a picture and does not flip.\n -->\n <span class=\"wr-color-picker__thumb wr-color-picker__thumb--slider\" [style.inset-inline-start.%]=\"hueThumbX()\"></span>\n</div>\n\n@if (alpha()) {\n <div\n class=\"wr-color-picker__slider wr-color-picker__slider--alpha\"\n role=\"slider\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n [attr.aria-label]=\"resolvedAlphaLabel()\"\n [attr.aria-valuenow]=\"alphaPercent()\"\n [attr.aria-valuetext]=\"`${alphaPercent()}%`\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n (pointerdown)=\"onPointerDown($event, 'alpha')\"\n (keydown)=\"onSliderKeydown($event, 'alpha')\"\n (blur)=\"onSurfaceBlur()\"\n >\n <span\n class=\"wr-color-picker__thumb wr-color-picker__thumb--slider\"\n [style.inset-inline-start.%]=\"alphaThumbX()\"\n ></span>\n </div>\n}\n\n<div class=\"wr-color-picker__chrome\">\n <span class=\"wr-color-picker__preview\" [style.background]=\"hex()\"></span>\n <wr-segmented\n class=\"wr-color-picker__tabs\"\n [options]=\"tabOptions()\"\n [value]=\"activeTab()\"\n (valueChange)=\"onTabChange($event)\"\n />\n</div>\n\n@switch (activeTab()) {\n @case ('hex') {\n <div class=\"wr-color-picker__inputs wr-color-picker__inputs--hex\">\n <label class=\"wr-color-picker__field wr-color-picker__field--wide\">\n <input\n type=\"text\"\n spellcheck=\"false\"\n autocomplete=\"off\"\n [value]=\"hexInput()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onHexInput($event)\"\n (blur)=\"onHexBlur()\"\n />\n <span class=\"wr-color-picker__field-label\">{{ channelHexLabel() }}</span>\n </label>\n </div>\n }\n @case ('rgb') {\n <div class=\"wr-color-picker__inputs\">\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"255\"\n [value]=\"rgb().r\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onRgbChannel('r', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">{{ channelRedLabel() }}</span>\n </label>\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"255\"\n [value]=\"rgb().g\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onRgbChannel('g', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">{{ channelGreenLabel() }}</span>\n </label>\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"255\"\n [value]=\"rgb().b\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onRgbChannel('b', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">{{ channelBlueLabel() }}</span>\n </label>\n @if (alpha()) {\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"100\"\n [value]=\"alphaPercent()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onAlphaPercent($event)\"\n />\n <span class=\"wr-color-picker__field-label\">{{ channelAlphaLabel() }}</span>\n </label>\n }\n </div>\n }\n @case ('hsl') {\n <div class=\"wr-color-picker__inputs\">\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"360\"\n [value]=\"hslView().h | number: '1.0-0'\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onHslChannel('h', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">{{ channelHueLabel() }}</span>\n </label>\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"100\"\n [value]=\"hslView().s * 100 | number: '1.0-0'\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onHslChannel('s', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">{{ channelSaturationLabel() }}</span>\n </label>\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"100\"\n [value]=\"hslView().l * 100 | number: '1.0-0'\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onHslChannel('l', $event)\"\n />\n <span class=\"wr-color-picker__field-label\">{{ channelLightnessLabel() }}</span>\n </label>\n @if (alpha()) {\n <label class=\"wr-color-picker__field\">\n <input\n type=\"number\"\n min=\"0\"\n max=\"100\"\n [value]=\"alphaPercent()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onAlphaPercent($event)\"\n />\n <span class=\"wr-color-picker__field-label\">{{ channelAlphaLabel() }}</span>\n </label>\n }\n </div>\n }\n}\n\n@if (swatches().length > 0) {\n <div class=\"wr-color-picker__swatches\">\n @for (color of swatches(); track color) {\n <button\n type=\"button\"\n class=\"wr-color-picker__swatch\"\n [style.background]=\"color\"\n [attr.aria-label]=\"color\"\n [disabled]=\"disabled() || readonly()\"\n (click)=\"onSwatchClick(color)\"\n ></button>\n }\n </div>\n}\n" }]
733
811
  }], ctorParameters: () => [], propDecorators: { alpha: [{ type: i0.Input, args: [{ isSignal: true, alias: "alpha", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], swatches: [{ type: i0.Input, args: [{ isSignal: true, alias: "swatches", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touch: [{ type: i0.Output, args: ["touch"] }] } });
734
812
 
735
813
  /**
@@ -892,13 +970,14 @@ class WrColorPickerTrigger {
892
970
  this.overlayRef = null;
893
971
  }
894
972
  }
895
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrColorPickerTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive });
896
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.4", type: WrColorPickerTrigger, isStandalone: true, selector: "[wrColorPickerTrigger]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, alpha: { classPropertyName: "alpha", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, swatches: { classPropertyName: "swatches", publicName: "swatches", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", opened: "opened", closed: "closed" }, host: { listeners: { "click": "toggle()" }, properties: { "attr.aria-haspopup": "\"dialog\"", "attr.aria-expanded": "isOpen()", "attr.aria-controls": "isOpen() ? panelId : null", "attr.aria-disabled": "disabled() ? true : null" } }, ngImport: i0 });
973
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: WrColorPickerTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive });
974
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.5", type: WrColorPickerTrigger, isStandalone: true, selector: "[wrColorPickerTrigger]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, alpha: { classPropertyName: "alpha", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, swatches: { classPropertyName: "swatches", publicName: "swatches", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", opened: "opened", closed: "closed" }, host: { listeners: { "click": "toggle()" }, properties: { "attr.aria-haspopup": "\"dialog\"", "attr.aria-expanded": "isOpen()", "attr.aria-controls": "isOpen() ? panelId : null", "attr.aria-disabled": "disabled() ? true : null" } }, exportAs: ["wrColorPickerTrigger"], ngImport: i0 });
897
975
  }
898
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrColorPickerTrigger, decorators: [{
976
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: WrColorPickerTrigger, decorators: [{
899
977
  type: Directive,
900
978
  args: [{
901
979
  selector: '[wrColorPickerTrigger]',
980
+ exportAs: 'wrColorPickerTrigger',
902
981
  host: {
903
982
  '(click)': 'toggle()',
904
983
  // The same two attributes `<wr-popover>`'s trigger carries: without them the