@kubex/zinc 1.1.185 → 2.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 (358) hide show
  1. package/.browserslistrc +2 -0
  2. package/.github/workflows/js_build_and_deploy.yaml +54 -1
  3. package/.github/workflows/test.yaml +27 -0
  4. package/dist/chunks/zn.UWJLIXDT.js +1 -0
  5. package/dist/custom-elements.json +10319 -2666
  6. package/dist/vscode.html-custom-data.json +1012 -57
  7. package/dist/web-types.json +2097 -145
  8. package/dist/zn.d.ts +2294 -416
  9. package/dist/zn.min.css +1 -1
  10. package/dist/zn.min.js +2310 -1637
  11. package/docs/_includes/default.njk +4 -1
  12. package/docs/assets/scripts/docs.js +22 -6
  13. package/docs/assets/styles/docs.css +9 -2
  14. package/docs/data/report-table-day-compare.json +380 -0
  15. package/docs/data/report-table.json +314 -0
  16. package/docs/pages/components/avatar-group.md +57 -0
  17. package/docs/pages/components/badge.md +67 -0
  18. package/docs/pages/components/button.md +41 -1
  19. package/docs/pages/components/chat-message.md +16 -0
  20. package/docs/pages/components/checkbox.md +1 -1
  21. package/docs/pages/components/chip.md +25 -13
  22. package/docs/pages/components/color-picker.md +37 -0
  23. package/docs/pages/components/copy-button.md +2 -2
  24. package/docs/pages/components/data-table-filter.md +33 -50
  25. package/docs/pages/components/data-table-search.md +4 -4
  26. package/docs/pages/components/data-table.md +27 -3
  27. package/docs/pages/components/date-range.md +116 -0
  28. package/docs/pages/components/dialog.md +29 -37
  29. package/docs/pages/components/diff-row.md +28 -0
  30. package/docs/pages/components/diff.md +46 -0
  31. package/docs/pages/components/editor.md +55 -0
  32. package/docs/pages/components/icon.md +46 -1
  33. package/docs/pages/components/input.md +30 -11
  34. package/docs/pages/components/item.md +35 -0
  35. package/docs/pages/components/key-value.md +25 -0
  36. package/docs/pages/components/menu.md +74 -7
  37. package/docs/pages/components/nav-item.md +15 -0
  38. package/docs/pages/components/nav-list.md +132 -0
  39. package/docs/pages/components/navbar.md +100 -1
  40. package/docs/pages/components/page.md +242 -74
  41. package/docs/pages/components/priority-list.md +43 -5
  42. package/docs/pages/components/query-builder.md +23 -0
  43. package/docs/pages/components/radio-group.md +33 -1
  44. package/docs/pages/components/radio.md +1 -1
  45. package/docs/pages/components/report-table.md +202 -0
  46. package/docs/pages/components/stat.md +30 -0
  47. package/docs/pages/components/tile.md +11 -0
  48. package/docs/pages/components/tooltip.md +1 -1
  49. package/docs/pages/components/translation-grid.md +86 -0
  50. package/docs/pages/components/vertical-stepper.md +108 -64
  51. package/package.json +2 -1
  52. package/scss/_global-spacing.scss +2 -2
  53. package/scss/_root.scss +11 -171
  54. package/scss/boot.scss +14 -0
  55. package/scss/mixins/_helper-mixins.scss +24 -2
  56. package/scss/mixins/_motion-mixins.scss +126 -0
  57. package/scss/mixins/_spacing-mixins.scss +4 -3
  58. package/scss/mixins/_tab-indicator-mixins.scss +12 -0
  59. package/scss/mixins/_typography-mixins.scss +1 -1
  60. package/scss/mixins/index.scss +14 -0
  61. package/scss/shared/_base.scss +32 -1
  62. package/scss/shared/_button-mixin.scss +7 -5
  63. package/scss/shared/_table.scss +35 -4
  64. package/scss/shared/nav.scss +4 -0
  65. package/scss/shared/shimmer.scss +1 -1
  66. package/scss/table-controls.scss +206 -0
  67. package/scss/themes/_dark.scss +1 -3
  68. package/scss/themes/_index.scss +1 -1
  69. package/scss/themes/_light.scss +46 -6
  70. package/scss/themes/_modes.scss +11 -11
  71. package/scss/themes/_openbyte.scss +944 -0
  72. package/scss/themes/_typography.scss +4 -2
  73. package/scss/variables/index.scss +0 -9
  74. package/src/components/alert/alert.component.ts +14 -4
  75. package/src/components/alert/alert.scss +55 -69
  76. package/src/components/alert/alert.test.ts +8 -0
  77. package/src/components/animated-button/animated-button.component.ts +3 -2
  78. package/src/components/animated-button/animated-button.scss +6 -23
  79. package/src/components/animated-button/animated-button.test.ts +4 -3
  80. package/src/components/avatar-group/avatar-group.component.ts +84 -0
  81. package/src/components/avatar-group/avatar-group.scss +54 -0
  82. package/src/components/avatar-group/avatar-group.test.ts +59 -0
  83. package/src/components/avatar-group/index.ts +12 -0
  84. package/src/components/badge/badge.component.ts +49 -0
  85. package/src/components/badge/badge.scss +80 -0
  86. package/src/components/badge/badge.test.ts +34 -0
  87. package/src/components/badge/index.ts +12 -0
  88. package/src/components/bulk-actions/bulk-actions.component.ts +14 -2
  89. package/src/components/button/button.component.ts +228 -8
  90. package/src/components/button/button.scss +115 -32
  91. package/src/components/button/button.test.ts +122 -0
  92. package/src/components/button-menu/button-menu.component.ts +32 -11
  93. package/src/components/button-menu/button-menu.scss +5 -1
  94. package/src/components/channel-tile/channel-tile.component.ts +34 -27
  95. package/src/components/channel-tile/channel-tile.scss +76 -43
  96. package/src/components/chart/builders.test.ts +46 -0
  97. package/src/components/chart/builders.ts +27 -3
  98. package/src/components/chart/chart-live.test.ts +4 -6
  99. package/src/components/chart/chart.component.ts +125 -5
  100. package/src/components/chart/chart.scss +121 -0
  101. package/src/components/chart/chart.test.ts +81 -0
  102. package/src/components/chat-message/chat-message.component.ts +21 -1
  103. package/src/components/chat-message/chat-message.scss +13 -2
  104. package/src/components/chat-message/chat-message.test.ts +9 -0
  105. package/src/components/checkbox/checkbox.component.ts +61 -27
  106. package/src/components/checkbox/checkbox.scss +58 -25
  107. package/src/components/checkbox-group/checkbox-group.component.ts +7 -1
  108. package/src/components/chip/chip.component.ts +11 -1
  109. package/src/components/chip/chip.scss +91 -26
  110. package/src/components/chip/chip.test.ts +17 -0
  111. package/src/components/collapsible/collapsible.component.ts +17 -3
  112. package/src/components/collapsible/collapsible.scss +19 -10
  113. package/src/components/color-picker/color-picker.component.ts +277 -0
  114. package/src/components/color-picker/color-picker.scss +141 -0
  115. package/src/components/color-picker/color-picker.test.ts +57 -0
  116. package/src/components/color-picker/index.ts +12 -0
  117. package/src/components/cols/cols.component.ts +10 -0
  118. package/src/components/cols/cols.test.ts +17 -0
  119. package/src/components/confirm/confirm.component.ts +20 -6
  120. package/src/components/confirm/confirm.test.ts +52 -0
  121. package/src/components/content-block/content-block.component.ts +38 -9
  122. package/src/components/content-block/content-block.scss +11 -5
  123. package/src/components/content-block/content-block.test.ts +26 -0
  124. package/src/components/copy-button/copy-button.component.ts +2 -2
  125. package/src/components/copy-button/copy-button.scss +17 -4
  126. package/src/components/data-select/data-select.component.ts +3 -1
  127. package/src/components/data-table/data-table.component.ts +362 -140
  128. package/src/components/data-table/data-table.scss +136 -60
  129. package/src/components/data-table/data-table.test.ts +356 -4
  130. package/src/components/data-table-filter/data-table-filter.component.ts +426 -406
  131. package/src/components/data-table-filter/data-table-filter.scss +176 -148
  132. package/src/components/data-table-filter/data-table-filter.test.ts +271 -273
  133. package/src/components/data-table-search/data-table-search.component.ts +4 -2
  134. package/src/components/data-table-search/data-table-search.scss +8 -1
  135. package/src/components/data-table-search/data-table-search.test.ts +29 -0
  136. package/src/components/date-range/date-range-math.test.ts +78 -0
  137. package/src/components/date-range/date-range-math.ts +105 -0
  138. package/src/components/date-range/date-range.component.ts +375 -0
  139. package/src/components/date-range/date-range.scss +272 -0
  140. package/src/components/date-range/date-range.test.ts +247 -0
  141. package/src/components/date-range/index.ts +12 -0
  142. package/src/components/datepicker/datepicker.component.ts +44 -7
  143. package/src/components/datepicker/datepicker.scss +9 -0
  144. package/src/components/datepicker/datepicker.test.ts +2 -2
  145. package/src/components/dialog/dialog.component.ts +38 -19
  146. package/src/components/dialog/dialog.scss +65 -36
  147. package/src/components/dialog/dialog.test.ts +14 -0
  148. package/src/components/diff/diff.component.ts +55 -0
  149. package/src/components/diff/diff.scss +46 -0
  150. package/src/components/diff/diff.test.ts +15 -0
  151. package/src/components/diff/index.ts +12 -0
  152. package/src/components/diff-row/diff-row.component.ts +75 -0
  153. package/src/components/diff-row/diff-row.scss +70 -0
  154. package/src/components/diff-row/diff-row.test.ts +37 -0
  155. package/src/components/diff-row/index.ts +12 -0
  156. package/src/components/dropdown/dropdown.component.ts +12 -10
  157. package/src/components/dropdown/dropdown.scss +2 -0
  158. package/src/components/editor/editor.component.ts +159 -11
  159. package/src/components/editor/editor.scss +250 -1
  160. package/src/components/editor/editor.test.ts +31 -3
  161. package/src/components/editor/modules/ai/panel/ai-panel.scss +0 -1
  162. package/src/components/editor/modules/attachment/attachment.ts +53 -31
  163. package/src/components/editor/modules/context-menu/context-menu.ts +17 -0
  164. package/src/components/editor/modules/dialog/dialog.component.ts +23 -1
  165. package/src/components/editor/modules/dialog/dialog.test.ts +28 -0
  166. package/src/components/editor/modules/emoji/emoji.ts +4 -5
  167. package/src/components/editor/modules/merge-field/merge-field.ts +83 -0
  168. package/src/components/editor/modules/toolbar/toolbar.component.ts +121 -68
  169. package/src/components/editor/modules/toolbar/toolbar.scss +38 -0
  170. package/src/components/editor/modules/toolbar/toolbar.ts +25 -1
  171. package/src/components/empty-state/empty-state.component.ts +6 -1
  172. package/src/components/empty-state/empty-state.scss +21 -4
  173. package/src/components/expanding-action/expanding-action.component.ts +22 -34
  174. package/src/components/expanding-action/expanding-action.scss +3 -45
  175. package/src/components/expanding-action/expanding-action.test.ts +2 -1
  176. package/src/components/file/file.component.ts +30 -2
  177. package/src/components/file/file.scss +6 -0
  178. package/src/components/flow-builder/flow-builder.component.ts +36 -3
  179. package/src/components/flow-builder/flow-builder.scss +0 -6
  180. package/src/components/flow-builder/modules/flow-canvas/flow-canvas.component.ts +8 -0
  181. package/src/components/flow-builder/modules/flow-canvas/flow-canvas.scss +10 -5
  182. package/src/components/flow-builder/modules/flow-node/flow-node.component.ts +17 -0
  183. package/src/components/flow-builder/modules/flow-node/flow-node.scss +10 -0
  184. package/src/components/form-actions/form-actions.component.ts +48 -2
  185. package/src/components/form-actions/form-actions.scss +1 -1
  186. package/src/components/form-group/form-group.component.ts +10 -3
  187. package/src/components/form-group/form-group.scss +37 -4
  188. package/src/components/form-group/form-group.test.ts +10 -0
  189. package/src/components/header/header.component.ts +11 -4
  190. package/src/components/header/header.scss +47 -5
  191. package/src/components/hover-container/hover-container.component.ts +4 -5
  192. package/src/components/icon/icon.component.ts +95 -9
  193. package/src/components/icon/icon.scss +70 -6
  194. package/src/components/icon/icon.test.ts +55 -0
  195. package/src/components/icon-picker/icon-picker.component.ts +35 -1
  196. package/src/components/inline-edit/inline-edit.component.ts +38 -8
  197. package/src/components/inline-edit/inline-edit.scss +128 -19
  198. package/src/components/input/input.component.ts +173 -297
  199. package/src/components/input/input.scss +65 -43
  200. package/src/components/input/input.test.ts +86 -4
  201. package/src/components/item/item.component.ts +12 -0
  202. package/src/components/item/item.scss +102 -3
  203. package/src/components/item/item.test.ts +23 -0
  204. package/src/components/key/key.component.ts +2 -2
  205. package/src/components/key/key.scss +11 -0
  206. package/src/components/key-value/index.ts +12 -0
  207. package/src/components/key-value/key-value.component.ts +160 -0
  208. package/src/components/key-value/key-value.scss +20 -0
  209. package/src/components/key-value/key-value.test.ts +22 -0
  210. package/src/components/markdown-editor/markdown-editor.component.ts +8 -1
  211. package/src/components/menu/menu.component.ts +21 -2
  212. package/src/components/menu/menu.scss +43 -23
  213. package/src/components/menu/menu.test.ts +33 -0
  214. package/src/components/menu-item/menu-item.component.ts +13 -2
  215. package/src/components/menu-item/menu-item.scss +56 -23
  216. package/src/components/menu-item/menu-item.test.ts +31 -0
  217. package/src/components/menu-item/submenu-controller.ts +25 -15
  218. package/src/components/menu-label/index.ts +12 -0
  219. package/src/components/menu-label/menu-label.component.ts +30 -0
  220. package/src/components/menu-label/menu-label.scss +18 -0
  221. package/src/components/nav-item/index.ts +12 -0
  222. package/src/components/nav-item/nav-item.component.ts +83 -0
  223. package/src/components/nav-item/nav-item.scss +128 -0
  224. package/src/components/nav-list/index.ts +12 -0
  225. package/src/components/nav-list/nav-list.component.ts +313 -0
  226. package/src/components/nav-list/nav-list.scss +87 -0
  227. package/src/components/nav-list/nav-list.test.ts +250 -0
  228. package/src/components/navbar/navbar.component.ts +235 -171
  229. package/src/components/navbar/navbar.scss +333 -169
  230. package/src/components/navbar/navbar.test.ts +63 -143
  231. package/src/components/page/page.component.ts +114 -29
  232. package/src/components/page/page.scss +223 -95
  233. package/src/components/page/page.test.ts +214 -3
  234. package/src/components/page-builder/page-builder.component.ts +51 -5
  235. package/src/components/page-builder/page-builder.scss +6 -1
  236. package/src/components/page-nav/page-nav.component.ts +25 -3
  237. package/src/components/page-nav/page-nav.scss +10 -2
  238. package/src/components/pagination/pagination.scss +1 -1
  239. package/src/components/panel/panel.component.ts +14 -13
  240. package/src/components/panel/panel.scss +14 -10
  241. package/src/components/panel/panel.test.ts +42 -0
  242. package/src/components/popup/popup.component.ts +20 -13
  243. package/src/components/popup/popup.test.ts +21 -1
  244. package/src/components/priority-list/priority-list.component.ts +157 -172
  245. package/src/components/priority-list/priority-list.scss +113 -17
  246. package/src/components/priority-list/priority-list.test.ts +301 -3
  247. package/src/components/progress-bar/progress-bar.component.ts +1 -1
  248. package/src/components/progress-bar/progress-bar.scss +16 -4
  249. package/src/components/progress-tile/progress-tile.scss +1 -1
  250. package/src/components/query-builder/query-builder.component.ts +85 -10
  251. package/src/components/query-builder/query-builder.scss +7 -1
  252. package/src/components/query-builder/query-builder.test.ts +40 -0
  253. package/src/components/radio/radio.component.ts +57 -18
  254. package/src/components/radio/radio.scss +101 -26
  255. package/src/components/radio-group/radio-group.component.ts +45 -7
  256. package/src/components/radio-group/radio-group.scss +17 -0
  257. package/src/components/radio-group/radio-group.test.ts +63 -2
  258. package/src/components/rating/rating.component.ts +23 -0
  259. package/src/components/rating/rating.scss +1 -1
  260. package/src/components/remarkd-editor/remarkd-editor.component.ts +60 -5
  261. package/src/components/remarkd-editor/remarkd-editor.scss +12 -4
  262. package/src/components/report-table/index.ts +12 -0
  263. package/src/components/report-table/report-format.test.ts +86 -0
  264. package/src/components/report-table/report-format.ts +72 -0
  265. package/src/components/report-table/report-math.test.ts +74 -0
  266. package/src/components/report-table/report-math.ts +252 -0
  267. package/src/components/report-table/report-table.component.ts +681 -0
  268. package/src/components/report-table/report-table.scss +328 -0
  269. package/src/components/report-table/report-table.test.ts +702 -0
  270. package/src/components/reveal/reveal.component.ts +15 -1
  271. package/src/components/reveal/reveal.scss +5 -0
  272. package/src/components/schedule-builder/schedule-builder.component.ts +23 -8
  273. package/src/components/scroll-container/scroll-container.component.ts +19 -1
  274. package/src/components/scroll-container/scroll-container.scss +6 -1
  275. package/src/components/select/select.component.ts +52 -36
  276. package/src/components/select/select.scss +42 -25
  277. package/src/components/select/select.test.ts +14 -2
  278. package/src/components/settings-container/settings-container.scss +18 -5
  279. package/src/components/sidebar/sidebar.component.ts +8 -1
  280. package/src/components/sidebar/sidebar.scss +6 -1
  281. package/src/components/skeleton/skeleton.component.ts +3 -3
  282. package/src/components/skeleton/skeleton.scss +6 -6
  283. package/src/components/skeleton/skeleton.test.ts +9 -0
  284. package/src/components/slash-menu/slash-menu.scss +1 -1
  285. package/src/components/slideout/slideout.component.ts +20 -3
  286. package/src/components/slideout/slideout.scss +13 -6
  287. package/src/components/slideout/slideout.test.ts +19 -0
  288. package/src/components/split-button/split-button.component.ts +1 -1
  289. package/src/components/split-pane/split-pane.component.ts +71 -8
  290. package/src/components/split-pane/split-pane.scss +145 -41
  291. package/src/components/split-pane/split-pane.test.ts +23 -3
  292. package/src/components/stat/stat.component.ts +209 -14
  293. package/src/components/stat/stat.scss +305 -5
  294. package/src/components/stat/stat.test.ts +143 -2
  295. package/src/components/status-indicator/status-indicator.scss +22 -21
  296. package/src/components/stepper/stepper.scss +3 -3
  297. package/src/components/tab/tab.component.ts +1 -0
  298. package/src/components/table/table.component.ts +12 -1
  299. package/src/components/table/table.scss +21 -13
  300. package/src/components/table/table.test.ts +17 -0
  301. package/src/components/tabs/tabs.component.ts +150 -37
  302. package/src/components/tabs/tabs.scss +48 -7
  303. package/src/components/tabs/tabs.test.ts +169 -0
  304. package/src/components/textarea/textarea.component.ts +25 -3
  305. package/src/components/textarea/textarea.scss +10 -0
  306. package/src/components/textarea/textarea.test.ts +8 -4
  307. package/src/components/theme-editor/theme-editor.scss +0 -6
  308. package/src/components/tile/tile.component.ts +32 -2
  309. package/src/components/tile/tile.scss +40 -7
  310. package/src/components/tile/tile.test.ts +10 -0
  311. package/src/components/toggle/toggle.component.ts +23 -7
  312. package/src/components/toggle/toggle.scss +36 -29
  313. package/src/components/toggle/toggle.test.ts +17 -0
  314. package/src/components/tooltip/tooltip.component.ts +3 -0
  315. package/src/components/tooltip/tooltip.scss +11 -6
  316. package/src/components/tooltip/tooltip.test.ts +12 -0
  317. package/src/components/translation-field/index.ts +12 -0
  318. package/src/components/translation-field/translation-field.component.ts +55 -0
  319. package/src/components/translation-field/translation-field.scss +101 -0
  320. package/src/components/translation-fields/index.ts +12 -0
  321. package/src/components/translation-fields/translation-fields.component.ts +80 -0
  322. package/src/components/translation-fields/translation-fields.scss +60 -0
  323. package/src/components/translation-grid/index.ts +12 -0
  324. package/src/components/translation-grid/translation-grid.component.ts +157 -0
  325. package/src/components/translation-grid/translation-grid.scss +138 -0
  326. package/src/components/translation-grid/translation-grid.test.ts +28 -0
  327. package/src/components/vertical-stepper/vertical-stepper.component.ts +35 -9
  328. package/src/components/vertical-stepper/vertical-stepper.scss +110 -39
  329. package/src/components/vertical-stepper/vertical-stepper.test.ts +35 -0
  330. package/src/components/well/well.scss +2 -2
  331. package/src/events/events.ts +2 -0
  332. package/src/events/zn-chart-click.ts +13 -0
  333. package/src/events/zn-report-change.ts +7 -0
  334. package/src/form-control.scss +36 -3
  335. package/src/internal/anchored-popover.ts +175 -0
  336. package/src/internal/animate.ts +19 -5
  337. package/src/internal/cosmic-border.ts +15 -0
  338. package/src/internal/form.ts +1 -1
  339. package/src/internal/hover-intent.ts +15 -0
  340. package/src/internal/input-modality.ts +15 -0
  341. package/src/internal/pointer-intent.ts +37 -0
  342. package/src/internal/reorder.ts +632 -0
  343. package/src/internal/scroll.ts +51 -3
  344. package/src/internal/selection-card.ts +1 -1
  345. package/src/internal/tab-indicator.ts +92 -0
  346. package/src/internal/table-sort.ts +25 -0
  347. package/src/internal/table-tabs.ts +108 -0
  348. package/src/internal/test-drag.ts +35 -0
  349. package/src/internal/theme-openbyte.test.ts +126 -0
  350. package/src/internal/theme.ts +4 -4
  351. package/src/internal/zinc-element.ts +2 -2
  352. package/src/selection-card.scss +25 -0
  353. package/src/types/web-test-runner-commands.d.ts +1 -0
  354. package/src/utilities/color.test.ts +43 -0
  355. package/src/utilities/color.ts +189 -0
  356. package/src/zinc.ts +14 -0
  357. package/.github/workflows/eleventy_build.yml +0 -42
  358. package/scss/themes/_aura.scss +0 -114
@@ -81,6 +81,9 @@ export default class ZnCheckboxGroup extends ZincElement implements ZincFormCont
81
81
  /** Squares off the corners of every contained checkbox in the group, which are rounded by default. */
82
82
  @property({type: Boolean, reflect: true}) square = false;
83
83
 
84
+ /** Centres the text of every contained checkbox in the group. */
85
+ @property({type: Boolean, reflect: true}) centered = false;
86
+
84
87
  /**
85
88
  * Lays horizontal contained checkboxes out as a wrapping row instead of an equal-width grid, so each one sizes from
86
89
  * `--zn-checkbox-group-column-width` (set it to `auto` to size from content) and wraps onto a new line when needed.
@@ -174,6 +177,10 @@ export default class ZnCheckboxGroup extends ZincElement implements ZincFormCont
174
177
  checkbox.square = true;
175
178
  }
176
179
 
180
+ if (this.centered) {
181
+ checkbox.centered = true;
182
+ }
183
+
177
184
  // Add class to checkboxes in a Checkbox Group so that we can style them without using :slotted
178
185
  checkbox.classList.add('groupedCheckbox');
179
186
 
@@ -317,7 +324,6 @@ export default class ZnCheckboxGroup extends ZincElement implements ZincFormCont
317
324
  'form-control--has-label-tooltip': hasLabelTooltip,
318
325
  'form-control--has-help-text': hasHelpText
319
326
  })}
320
- role="listbox"
321
327
  aria-labelledby="label"
322
328
  aria-describedby="help-text"
323
329
  aria-errormessage="error-message">
@@ -31,9 +31,16 @@ export default class ZnChip extends ZincElement {
31
31
 
32
32
  @property({attribute: 'icon-size', type: Number}) iconSize: number = 18;
33
33
 
34
- @property() type: 'info' | 'success' | 'warning' | 'error' | 'primary' |
34
+ /** `ai` marks AI-generated content only. */
35
+ @property() type: 'info' | 'success' | 'warning' | 'error' | 'primary' | 'ai' |
35
36
  'transparent' | 'custom' | 'neutral' = 'neutral';
36
37
 
38
+ /** Fills the chip with its type colour. */
39
+ @property({type: Boolean}) solid = false;
40
+
41
+ /** Leads the label with a dot in the type colour. */
42
+ @property({type: Boolean}) dot = false;
43
+
37
44
  @property({attribute: 'flush', type: Boolean, reflect: true}) flush: boolean = false;
38
45
  @property({attribute: 'flush-x', type: Boolean, reflect: true}) flushX: boolean = false;
39
46
  @property({attribute: 'flush-y', type: Boolean, reflect: true}) flushY: boolean = false;
@@ -59,6 +66,9 @@ export default class ZnChip extends ZincElement {
59
66
  'chip--transparent': this.type === 'transparent',
60
67
  'chip--custom': this.type === 'custom',
61
68
  'chip--neutral': this.type === 'neutral',
69
+ 'chip--ai': this.type === 'ai',
70
+ 'chip--solid': this.solid,
71
+ 'chip--dot': this.dot,
62
72
  })}">
63
73
  ${this.icon ? html`
64
74
  <zn-icon src="${this.icon}" size="${this.iconSize}"></zn-icon>` : ''}
@@ -6,21 +6,33 @@
6
6
  }
7
7
 
8
8
  .chip {
9
+ // fg = text/icon, bg = tint, line = hairline (drawn only when a theme sets
10
+ // --zn-chip-border-width), solid = the `solid` fill and the `dot`.
11
+ --chip-fg: rgb(var(--zn-text));
12
+ --chip-bg: rgba(var(--zn-border-color), .10);
13
+ --chip-line: rgba(var(--zn-border-color), .50);
14
+ --chip-solid: rgb(var(--zn-color-muted-text));
15
+ --chip-ink: 255, 255, 255;
16
+
17
+ box-sizing: border-box;
9
18
  display: inline-flex;
10
19
  font-size: var(--zn-text-chip-font-size);
11
20
  font-family: var(--zn-font-family-sans);
12
21
  font-weight: var(--zn-text-chip-font-weight);
13
- border-radius: var(--zn-border-radius-x-large);
14
- padding-inline: var(--zn-spacing-x-small);
22
+ border-radius: var(--zn-chip-border-radius, var(--zn-border-radius-x-large));
23
+ padding-inline: var(--zn-chip-padding-inline, var(--zn-spacing-x-small));
15
24
  padding-block: 0;
16
- height: var(--zn-spacing-medium);
25
+ height: var(--zn-chip-height, var(--zn-spacing-medium));
26
+ color: var(--chip-fg);
27
+ background: var(--chip-bg);
28
+ border: var(--zn-chip-border-width, 0px) solid var(--chip-line);
17
29
  text-align: center;
18
- gap: var(--zn-spacing-x-small);
30
+ gap: var(--zn-chip-gap, var(--zn-spacing-x-small));
19
31
  width: fit-content;
20
32
  justify-content: center;
21
33
  align-items: center;
22
34
  line-height: var(--zn-text-chip-line-height);
23
- letter-spacing: var(--zn-letter-spacing-loose);
35
+ letter-spacing: var(--zn-chip-letter-spacing, var(--zn-letter-spacing-loose));
24
36
  white-space: nowrap;
25
37
 
26
38
  &--flush {
@@ -39,43 +51,66 @@
39
51
  }
40
52
 
41
53
  .chip--primary {
42
- color: rgba(var(--zn-primary), 0.9);
43
- background: rgba(var(--zn-primary), .10);
54
+ --chip-fg: var(--zn-chip-primary-fg, rgba(var(--zn-primary), 0.9));
55
+ --chip-bg: var(--zn-chip-primary-bg, rgba(var(--zn-primary), .10));
56
+ --chip-line: var(--zn-chip-primary-line, rgba(var(--zn-primary), .30));
57
+ --chip-solid: rgb(var(--zn-primary));
44
58
  }
45
59
 
46
- .chip--error {
47
- color: rgb(var(--zn-color-error));
48
- background: rgba(var(--zn-color-error), .10);
49
- }
50
-
51
- .chip--info {
52
- color: rgb(var(--zn-color-info));
53
- background: rgba(var(--zn-color-info), .10);
60
+ @each $type, $role in (error: danger, info: info, warning: warning, success: success, ai: ai) {
61
+ .chip--#{$type} {
62
+ --chip-fg: var(--zn-color-#{$role}-fg);
63
+ --chip-bg: var(--zn-color-#{$role}-bg);
64
+ --chip-line: var(--zn-color-#{$role}-line);
65
+ --chip-solid: var(--zn-color-#{$role}-solid);
66
+ }
54
67
  }
55
68
 
56
69
  .chip--warning {
57
- color: rgb(var(--zn-color-warning));
58
- background: rgba(var(--zn-color-warning), .10);
70
+ --chip-ink: var(--zn-color-on-warning, 255, 255, 255);
59
71
  }
60
72
 
61
- .chip--success {
62
- color: rgb(var(--zn-color-success));
63
- background: rgba(var(--zn-color-success), .10);
73
+ .chip--ai {
74
+ --chip-ink: var(--zn-color-on-ai, 255, 255, 255);
64
75
  }
65
76
 
66
77
  .chip--neutral {
67
- color: rgb(var(--zn-text));
68
- background: rgba(var(--zn-border-color), .10);
78
+ --chip-fg: var(--zn-chip-neutral-fg, rgb(var(--zn-text)));
79
+ --chip-bg: var(--zn-chip-neutral-bg, rgba(var(--zn-border-color), .10));
80
+ --chip-line: var(--zn-chip-neutral-line, rgba(var(--zn-border-color), .50));
81
+ --chip-solid: var(--zn-color-neutral-solid);
69
82
  }
70
83
 
71
84
  .chip--transparent {
72
- color: rgb(var(--zn-text));
73
- background: transparent;
85
+ --chip-fg: rgb(var(--zn-text));
86
+ --chip-bg: transparent;
87
+ --chip-line: transparent;
74
88
  }
75
89
 
76
90
  .chip--custom {
77
- color: var(--chip-color-override, rgb(var(--zn-text)));
78
- background-color: hsl(from var(--chip-color-override, inherit) h s l / 0.1);
91
+ --chip-fg: var(--chip-color-override, rgb(var(--zn-text)));
92
+ --chip-bg: hsl(from var(--chip-color-override, inherit) h s l / 0.1);
93
+ --chip-line: hsl(from var(--chip-color-override, inherit) h s l / 0.3);
94
+ --chip-solid: var(--chip-color-override, rgb(var(--zn-text)));
95
+ }
96
+
97
+ .chip--solid {
98
+ color: rgb(var(--chip-ink));
99
+ background: var(--chip-solid);
100
+ border-color: var(--chip-solid);
101
+ }
102
+
103
+ .chip--dot::before {
104
+ content: "";
105
+ flex: none;
106
+ width: 6px;
107
+ height: 6px;
108
+ border-radius: 50%;
109
+ background: var(--chip-solid);
110
+ }
111
+
112
+ .chip--solid.chip--dot::before {
113
+ background: currentColor;
79
114
  }
80
115
 
81
116
  .chip__value {
@@ -101,3 +136,33 @@
101
136
  .chip--with-icon {
102
137
  padding-left: var(--zn-spacing-2x-small);
103
138
  }
139
+
140
+ .chip--with-icon.chip--with-content {
141
+ padding-left: var(--zn-chip-icon-padding-start, var(--zn-spacing-2x-small));
142
+ }
143
+
144
+ .chip__action {
145
+ // Slotted zn-buttons inherit these, shrinking them to fit the circle.
146
+ --zn-button-height: 100%;
147
+ --zn-button-icon-width: 100%;
148
+
149
+ display: inline-grid;
150
+ place-content: center;
151
+ flex: none;
152
+ width: 18px;
153
+ height: 18px;
154
+ margin-inline: -1.6px -8px;
155
+ border-radius: 50%;
156
+ overflow: hidden;
157
+ color: var(--zn-chip-action-color, currentColor);
158
+
159
+ // zn-button's own :host colour would otherwise beat inheriting from this slot.
160
+ &::slotted(*) {
161
+ color: inherit;
162
+ }
163
+
164
+ &:hover {
165
+ color: var(--zn-chip-action-hover-color, currentColor);
166
+ background: var(--zn-chip-action-hover-background, rgba(var(--zn-border-color), .3));
167
+ }
168
+ }
@@ -24,4 +24,21 @@ describe('<zn-chip>', () => {
24
24
 
25
25
  expect(colour(el)).to.equal(colour(neutral));
26
26
  });
27
+
28
+ it('applies the ai, solid and dot classes', async () => {
29
+ const el = await fixture<HTMLElement>(html`<zn-chip type="ai" solid dot>Generated</zn-chip>`);
30
+ const base = el.shadowRoot!.querySelector('[part="base"]')!;
31
+
32
+ expect(base.classList.contains('chip--ai')).to.be.true;
33
+ expect(base.classList.contains('chip--solid')).to.be.true;
34
+ expect(base.classList.contains('chip--dot')).to.be.true;
35
+ });
36
+
37
+ it('fills a solid chip with its type colour', async () => {
38
+ const el = await fixture<HTMLElement>(html`
39
+ <zn-chip type="custom" solid style="--chip-color-override: rgb(15, 118, 110)">Ticket</zn-chip>`);
40
+ const base = el.shadowRoot!.querySelector('[part="base"]')!;
41
+
42
+ expect(getComputedStyle(base).backgroundColor).to.equal('rgb(15, 118, 110)');
43
+ });
27
44
  });
@@ -160,6 +160,16 @@ export default class ZnCollapsible extends ZincElement {
160
160
  }
161
161
  }
162
162
 
163
+ handleHeaderKeyDown = (e: KeyboardEvent) => {
164
+ // Keys from controls slotted into the header are theirs
165
+ if (e.target !== e.currentTarget || (e.key !== 'Enter' && e.key !== ' ')) {
166
+ return;
167
+ }
168
+
169
+ e.preventDefault();
170
+ this.expanded = !this.expanded;
171
+ }
172
+
163
173
  public recalculateNumberOfItems = () => {
164
174
  const assignedElements = this.hasSlotController.getDefaultSlot()
165
175
  .filter(node => node.nodeType === Node.ELEMENT_NODE) as HTMLElement[];
@@ -179,7 +189,10 @@ export default class ZnCollapsible extends ZincElement {
179
189
 
180
190
  return html`
181
191
  <div @click="${() => (!this.expanded ? (this.expanded = true) : '')}">
182
- <slot name="header" part="header" class="header" @click="${this.handleCollapse}">
192
+ <div class="header-toggle" role="button" tabindex="0" aria-expanded="${this.expanded}"
193
+ @click="${this.handleCollapse}"
194
+ @keydown="${this.handleHeaderKeyDown}">
195
+ <slot name="header" part="header" class="header">
183
196
  <div class="header__left">
184
197
  <p part="caption" class="caption">
185
198
  <slot name="caption">${this.caption}</slot>
@@ -196,14 +209,15 @@ export default class ZnCollapsible extends ZincElement {
196
209
  ${this.showNumber && !this.expanded ? html`
197
210
  <zn-chip size="small" type="primary">${this.numberOfItems}</zn-chip>` : ''}
198
211
  ${this.showArrow ? html`
199
- <zn-icon library="material-outlined" src="expand_more" class="expand"></zn-icon>` : ''}
212
+ <zn-icon src="chevron-down@lu" size="18" class="expand"></zn-icon>` : ''}
200
213
  </div>
201
214
  </slot>
215
+ </div>
202
216
  <div class=${classMap({
203
217
  'content': true,
204
218
  'content--flush': this.flush,
205
219
  'content--animating': this.animating,
206
- })} part="content" @transitionend="${this.handleTransitionEnd}">
220
+ })} part="content" ?inert="${!this.expanded}" @transitionend="${this.handleTransitionEnd}">
207
221
  <div class="content__inner">
208
222
  <slot @slotchange="${this.requestUpdate}"></slot>
209
223
  </div>
@@ -8,23 +8,18 @@
8
8
  transform: rotate(180deg);
9
9
  }
10
10
 
11
- :host-context(.app-navigation) .header {
12
- padding: var(--zn-base-px);
13
- }
14
-
15
11
  .header {
16
12
  display: flex;
17
13
  flex-direction: row;
18
14
  justify-content: space-between;
19
15
  cursor: pointer;
20
16
  overflow: hidden;
21
- padding: var(--zn-base-gap);
17
+ padding: var(--zn-collapsible-header-padding, var(--zn-base-gap));
22
18
  justify-items: center;
23
19
 
24
20
  .caption {
25
21
  font-weight: 600;
26
22
  margin: 0;
27
- flex-grow: 1;
28
23
  width: 100%;
29
24
  color: rgb(var(--zn-text-heading));
30
25
  }
@@ -60,6 +55,11 @@
60
55
  }
61
56
  }
62
57
 
58
+ .header-toggle:focus-visible {
59
+ outline: var(--zn-focus-ring);
60
+ outline-offset: calc(-1 * var(--zn-focus-ring-width));
61
+ }
62
+
63
63
  :host([error-label]) {
64
64
  .header {
65
65
  .caption {
@@ -90,15 +90,15 @@
90
90
  grid-template-columns: minmax(0, 1fr);
91
91
  grid-template-rows: 0fr;
92
92
  opacity: 0;
93
- transition: grid-template-rows var(--zn-transition-medium) cubic-bezier(0.32, 0.72, 0, 1),
93
+ transition: grid-template-rows var(--zn-transition-medium) var(--zn-ease-drawer),
94
94
  opacity var(--zn-transition-fast) ease-out;
95
95
 
96
96
  &__inner {
97
97
  min-height: 0;
98
98
  padding: 0 var(--zn-spacing-medium) var(--content-block-padding);
99
99
  transform: translateY(-4px);
100
- transition: transform var(--zn-transition-medium) cubic-bezier(0.32, 0.72, 0, 1),
101
- padding-bottom var(--zn-transition-medium) cubic-bezier(0.32, 0.72, 0, 1);
100
+ transition: transform var(--zn-transition-medium) var(--zn-ease-drawer),
101
+ padding-bottom var(--zn-transition-medium) var(--zn-ease-drawer);
102
102
  }
103
103
 
104
104
  &--flush &__inner {
@@ -126,11 +126,20 @@
126
126
 
127
127
  @media (prefers-reduced-motion: reduce) {
128
128
  .content,
129
- .content__inner {
129
+ .content__inner,
130
+ .expand {
130
131
  transition-duration: 0s;
131
132
  }
132
133
  }
133
134
 
134
135
  .expand {
135
136
  margin-left: var(--zn-spacing-medium);
137
+ color: rgb(var(--zn-color-muted-text));
138
+ transition: transform var(--zn-transition-medium) var(--zn-ease-out), color var(--zn-transition-fast) ease;
139
+ }
140
+
141
+ @include wc.hover-fine {
142
+ .header-toggle:hover .expand {
143
+ color: var(--zn-color-primary-fg, rgb(var(--zn-primary)));
144
+ }
136
145
  }
@@ -0,0 +1,277 @@
1
+ import {
2
+ type ColorFormat,
3
+ hslToRgb,
4
+ type Hsv,
5
+ hsvToRgb,
6
+ oklchParts,
7
+ oklchToRgb,
8
+ parseColor,
9
+ type Rgb,
10
+ rgbToHsl,
11
+ rgbToHsv,
12
+ toHex
13
+ } from '../../utilities/color';
14
+ import {type CSSResultGroup, html, type PropertyValues, unsafeCSS} from 'lit';
15
+ import {property, query} from 'lit/decorators.js';
16
+ import {styleMap} from 'lit/directives/style-map.js';
17
+ import {watch} from '../../internal/watch';
18
+ import ZincElement from '../../internal/zinc-element';
19
+
20
+ import styles from './color-picker.scss';
21
+
22
+ type FieldKey = 'hex' | 'r' | 'g' | 'b' | 'h' | 's' | 'l' | 'ol' | 'oc' | 'oh';
23
+
24
+ const FORMAT_OF: Record<FieldKey, ColorFormat> = {
25
+ hex: 'hex', r: 'rgb', g: 'rgb', b: 'rgb', h: 'hsl', s: 'hsl', l: 'hsl', ol: 'oklch', oc: 'oklch', oh: 'oklch'
26
+ };
27
+
28
+ const clamp = (n: number, min: number, max: number) => Math.min(max, Math.max(min, n));
29
+
30
+ /**
31
+ * @summary A colour panel: a saturation/brightness area, a hue slider and the colour's HEX, RGB, HSL and OKLCH values.
32
+ * @documentation https://zinc.style/components/color-picker
33
+ * @status experimental
34
+ * @since 1.0
35
+ *
36
+ * @event zn-input - Emitted when the colour or the format being worked in changes.
37
+ *
38
+ * @csspart base - The component's base wrapper.
39
+ */
40
+ export default class ZnColorPicker extends ZincElement {
41
+ static styles: CSSResultGroup = unsafeCSS(styles);
42
+
43
+ @query('.color-picker__area') area: HTMLElement;
44
+
45
+ /** The colour, in any CSS colour syntax. Reads back as `#RRGGBB`. */
46
+ @property() value = '#000000';
47
+
48
+ /** The format being worked in. Focusing or editing a row switches to that row's format. */
49
+ @property({reflect: true}) format: ColorFormat = 'hex';
50
+
51
+ @property() label = 'Colour';
52
+
53
+ private hsv: Hsv = [0, 0, 0];
54
+ private editing: FieldKey | null = null;
55
+
56
+ private get rgb(): Rgb {
57
+ return hsvToRgb(...this.hsv);
58
+ }
59
+
60
+ connectedCallback() {
61
+ super.connectedCallback();
62
+ this.adopt(this.value);
63
+ }
64
+
65
+ /** Moves focus to the colour area. */
66
+ focus(options?: FocusOptions) {
67
+ this.area?.focus(options);
68
+ }
69
+
70
+ @watch('value', {waitUntilFirstUpdate: true})
71
+ handleValueChange() {
72
+ if (this.value !== toHex(this.rgb)) {
73
+ this.adopt(this.value);
74
+ this.requestUpdate();
75
+ }
76
+ }
77
+
78
+ // Grey and black have no hue of their own, so keep the current one rather than snapping the slider to red.
79
+ private adopt(color: string | Rgb) {
80
+ const rgb = typeof color === 'string' ? parseColor(color) : color;
81
+ if (!rgb) return;
82
+ const hsv = rgbToHsv(...rgb);
83
+ this.hsv = hsv[1] > 0 && hsv[2] > 0 ? hsv : [this.hsv[0], hsv[1], hsv[2]];
84
+ this.value = toHex(rgb);
85
+ }
86
+
87
+ private commit(except?: FieldKey) {
88
+ this.editing = except ?? null;
89
+ this.value = toHex(this.rgb);
90
+ this.requestUpdate();
91
+ this.emit('zn-input');
92
+ }
93
+
94
+ private useFormat(key: FieldKey) {
95
+ if (FORMAT_OF[key] !== this.format) {
96
+ this.format = FORMAT_OF[key];
97
+ this.emit('zn-input');
98
+ }
99
+ }
100
+
101
+ private pick(event: PointerEvent) {
102
+ const rect = this.area.getBoundingClientRect();
103
+ this.hsv = [this.hsv[0], clamp((event.clientX - rect.left) / rect.width, 0, 1), 1 - clamp((event.clientY - rect.top) / rect.height, 0, 1)];
104
+ this.commit();
105
+ }
106
+
107
+ private handleAreaPointerDown = (event: PointerEvent) => {
108
+ this.area.setPointerCapture(event.pointerId);
109
+ this.area.focus();
110
+ this.pick(event);
111
+ this.area.addEventListener('pointermove', this.handleAreaPointerMove);
112
+ };
113
+
114
+ private handleAreaPointerMove = (event: PointerEvent) => this.pick(event);
115
+
116
+ private handleAreaPointerUp = () => {
117
+ this.area.removeEventListener('pointermove', this.handleAreaPointerMove);
118
+ };
119
+
120
+ private handleAreaKeyDown(event: KeyboardEvent) {
121
+ if (!event.key.startsWith('Arrow')) return;
122
+ event.preventDefault();
123
+ const step = event.shiftKey ? 0.1 : 0.01;
124
+ const [h, s, v] = this.hsv;
125
+ switch (event.key) {
126
+ case 'ArrowLeft':
127
+ this.hsv = [h, clamp(s - step, 0, 1), v];
128
+ break;
129
+ case 'ArrowRight':
130
+ this.hsv = [h, clamp(s + step, 0, 1), v];
131
+ break;
132
+ case 'ArrowUp':
133
+ this.hsv = [h, s, clamp(v + step, 0, 1)];
134
+ break;
135
+ case 'ArrowDown':
136
+ this.hsv = [h, s, clamp(v - step, 0, 1)];
137
+ break;
138
+ }
139
+ this.commit();
140
+ }
141
+
142
+ private handleHueInput(event: Event) {
143
+ this.hsv = [+(event.target as HTMLInputElement).value, this.hsv[1], this.hsv[2]];
144
+ this.commit();
145
+ }
146
+
147
+ private field(key: FieldKey) {
148
+ return this.renderRoot.querySelector<HTMLInputElement>(`[data-k="${key}"]`)!;
149
+ }
150
+
151
+ private num(key: FieldKey) {
152
+ return parseFloat(this.field(key).value) || 0;
153
+ }
154
+
155
+ private handleFieldFocus(event: FocusEvent) {
156
+ const key = (event.target as HTMLElement).dataset?.k as FieldKey | undefined;
157
+ if (key) this.useFormat(key);
158
+ }
159
+
160
+ private handleFieldInput(event: Event) {
161
+ const target = event.target as HTMLInputElement;
162
+ const key = target.dataset.k as FieldKey | undefined;
163
+ if (!key) return;
164
+ event.stopPropagation();
165
+ this.editing = key;
166
+ this.useFormat(key);
167
+
168
+ const rgb = this.readField(key, target.value.trim());
169
+ if (rgb) {
170
+ target.removeAttribute('aria-invalid');
171
+ this.adopt(rgb);
172
+ this.commit(key);
173
+ } else {
174
+ target.setAttribute('aria-invalid', 'true');
175
+ }
176
+ }
177
+
178
+ private handleFieldBlur() {
179
+ this.editing = null;
180
+ this.requestUpdate();
181
+ }
182
+
183
+ /** The colour a row describes once `value` is written into its `key` field, or null when that value is out of range. */
184
+ private readField(key: FieldKey, value: string): Rgb | null {
185
+ if (key === 'hex') {
186
+ return /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.test(value) ? parseColor(value) : null;
187
+ }
188
+
189
+ if (key === 'ol' || key === 'oc' || key === 'oh') {
190
+ if (!/^\d*\.?\d+$/.test(value)) return null;
191
+ const n = parseFloat(value);
192
+ if (n > (key === 'ol' ? 100 : key === 'oc' ? 0.5 : 360)) return null;
193
+ return oklchToRgb(
194
+ (key === 'ol' ? n : this.num('ol')) / 100,
195
+ key === 'oc' ? n : this.num('oc'),
196
+ key === 'oh' ? n : this.num('oh')
197
+ );
198
+ }
199
+
200
+ if (!/^\d+$/.test(value)) return null;
201
+ const n = +value;
202
+ if (key === 'r' || key === 'g' || key === 'b') {
203
+ if (n > 255) return null;
204
+ return [key === 'r' ? n : this.num('r'), key === 'g' ? n : this.num('g'), key === 'b' ? n : this.num('b')];
205
+ }
206
+ if (n > (key === 'h' ? 360 : 100)) return null;
207
+ return hslToRgb(key === 'h' ? n % 360 : this.num('h'), key === 's' ? n : this.num('s'), key === 'l' ? n : this.num('l'));
208
+ }
209
+
210
+ // The fields are written imperatively so the one being typed in keeps exactly what was typed.
211
+ protected updated(changed: PropertyValues) {
212
+ super.updated(changed);
213
+ const rgb = this.rgb;
214
+ const [h, s, l] = rgbToHsl(...rgb);
215
+ const [ol, oc, oh] = oklchParts(rgb);
216
+ const values: Record<FieldKey, string | number> = {
217
+ hex: toHex(rgb), r: rgb[0], g: rgb[1], b: rgb[2], h, s, l, ol, oc, oh
218
+ };
219
+ (Object.keys(values) as FieldKey[]).forEach(key => {
220
+ if (key === this.editing) return;
221
+ const input = this.field(key);
222
+ input.value = String(values[key]);
223
+ input.removeAttribute('aria-invalid');
224
+ });
225
+ }
226
+
227
+ private renderTrio(name: string, fields: [FieldKey, string, string][]) {
228
+ return html`
229
+ <div class="color-picker__row">
230
+ <span class="color-picker__row-label">${name}</span>
231
+ <div class="color-picker__trio">
232
+ ${fields.map(([key, label, mode]) => html`
233
+ <input class="color-picker__field" data-k=${key} inputmode=${mode} aria-label=${label}
234
+ spellcheck="false" autocomplete="off">`)}
235
+ </div>
236
+ </div>`;
237
+ }
238
+
239
+ render() {
240
+ const [h, s, v] = this.hsv;
241
+ const hex = toHex(this.rgb);
242
+
243
+ return html`
244
+ <div part="base" class="color-picker" role="group" aria-label="${this.label} colour picker">
245
+ <div class="color-picker__area"
246
+ tabindex="0"
247
+ role="application"
248
+ aria-label="Colour area: left to right is saturation, bottom to top is brightness. Use the arrow keys."
249
+ style=${styleMap({backgroundColor: `hsl(${Math.round(h)} 100% 50%)`})}
250
+ @pointerdown=${this.handleAreaPointerDown}
251
+ @pointerup=${this.handleAreaPointerUp}
252
+ @lostpointercapture=${this.handleAreaPointerUp}
253
+ @keydown=${this.handleAreaKeyDown}>
254
+ <span class="color-picker__dot"
255
+ style=${styleMap({left: `${s * 100}%`, top: `${(1 - v) * 100}%`, background: hex})}></span>
256
+ </div>
257
+
258
+ <input class="color-picker__hue" type="range" min="0" max="360" step="1" aria-label="Hue"
259
+ .value=${String(Math.round(h))}
260
+ @input=${this.handleHueInput}>
261
+
262
+ <div class="color-picker__values"
263
+ @focusin=${this.handleFieldFocus}
264
+ @input=${this.handleFieldInput}
265
+ @focusout=${this.handleFieldBlur}>
266
+ <label class="color-picker__row color-picker__row--hex">
267
+ <span class="color-picker__row-label">HEX</span>
268
+ <input class="color-picker__field" data-k="hex" aria-label="Hex" spellcheck="false" autocomplete="off">
269
+ </label>
270
+ ${this.renderTrio('RGB', [['r', 'Red', 'numeric'], ['g', 'Green', 'numeric'], ['b', 'Blue', 'numeric']])}
271
+ ${this.renderTrio('HSL', [['h', 'Hue degrees', 'numeric'], ['s', 'Saturation percent', 'numeric'], ['l', 'Lightness percent', 'numeric']])}
272
+ ${this.renderTrio('OKLCH', [['ol', 'OKLCH lightness percent', 'decimal'], ['oc', 'OKLCH chroma', 'decimal'], ['oh', 'OKLCH hue degrees', 'decimal']])}
273
+ </div>
274
+ </div>
275
+ `;
276
+ }
277
+ }