@kubex/zinc 1.1.184 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (362) 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 +10344 -2583
  6. package/dist/vscode.html-custom-data.json +1102 -125
  7. package/dist/web-types.json +2217 -224
  8. package/dist/zn.d.ts +2306 -417
  9. package/dist/zn.min.css +1 -1
  10. package/dist/zn.min.js +2313 -1635
  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/filter-wrapper.md +20 -0
  33. package/docs/pages/components/icon.md +46 -1
  34. package/docs/pages/components/input.md +30 -11
  35. package/docs/pages/components/item.md +35 -0
  36. package/docs/pages/components/key-value.md +25 -0
  37. package/docs/pages/components/menu.md +74 -7
  38. package/docs/pages/components/nav-item.md +15 -0
  39. package/docs/pages/components/nav-list.md +132 -0
  40. package/docs/pages/components/navbar.md +100 -1
  41. package/docs/pages/components/page.md +242 -74
  42. package/docs/pages/components/priority-list.md +43 -5
  43. package/docs/pages/components/query-builder.md +23 -0
  44. package/docs/pages/components/radio-group.md +33 -1
  45. package/docs/pages/components/radio.md +1 -1
  46. package/docs/pages/components/report-table.md +202 -0
  47. package/docs/pages/components/stat.md +30 -0
  48. package/docs/pages/components/tile.md +11 -0
  49. package/docs/pages/components/tooltip.md +1 -1
  50. package/docs/pages/components/translation-grid.md +86 -0
  51. package/docs/pages/components/vertical-stepper.md +108 -64
  52. package/package.json +2 -1
  53. package/scss/_global-spacing.scss +2 -2
  54. package/scss/_root.scss +11 -171
  55. package/scss/boot.scss +14 -0
  56. package/scss/mixins/_helper-mixins.scss +24 -2
  57. package/scss/mixins/_motion-mixins.scss +126 -0
  58. package/scss/mixins/_spacing-mixins.scss +4 -3
  59. package/scss/mixins/_tab-indicator-mixins.scss +12 -0
  60. package/scss/mixins/_typography-mixins.scss +1 -1
  61. package/scss/mixins/index.scss +14 -0
  62. package/scss/shared/_base.scss +32 -1
  63. package/scss/shared/_button-mixin.scss +7 -5
  64. package/scss/shared/_table.scss +35 -4
  65. package/scss/shared/nav.scss +4 -0
  66. package/scss/shared/shimmer.scss +1 -1
  67. package/scss/table-controls.scss +206 -0
  68. package/scss/themes/_dark.scss +1 -3
  69. package/scss/themes/_index.scss +1 -1
  70. package/scss/themes/_light.scss +46 -6
  71. package/scss/themes/_modes.scss +11 -11
  72. package/scss/themes/_openbyte.scss +944 -0
  73. package/scss/themes/_typography.scss +4 -2
  74. package/scss/variables/index.scss +0 -9
  75. package/src/components/alert/alert.component.ts +14 -4
  76. package/src/components/alert/alert.scss +55 -69
  77. package/src/components/alert/alert.test.ts +8 -0
  78. package/src/components/animated-button/animated-button.component.ts +3 -2
  79. package/src/components/animated-button/animated-button.scss +6 -23
  80. package/src/components/animated-button/animated-button.test.ts +4 -3
  81. package/src/components/avatar-group/avatar-group.component.ts +84 -0
  82. package/src/components/avatar-group/avatar-group.scss +54 -0
  83. package/src/components/avatar-group/avatar-group.test.ts +59 -0
  84. package/src/components/avatar-group/index.ts +12 -0
  85. package/src/components/badge/badge.component.ts +49 -0
  86. package/src/components/badge/badge.scss +80 -0
  87. package/src/components/badge/badge.test.ts +34 -0
  88. package/src/components/badge/index.ts +12 -0
  89. package/src/components/bulk-actions/bulk-actions.component.ts +14 -2
  90. package/src/components/button/button.component.ts +242 -11
  91. package/src/components/button/button.scss +133 -25
  92. package/src/components/button/button.test.ts +122 -0
  93. package/src/components/button-menu/button-menu.component.ts +32 -11
  94. package/src/components/button-menu/button-menu.scss +5 -1
  95. package/src/components/channel-tile/channel-tile.component.ts +34 -27
  96. package/src/components/channel-tile/channel-tile.scss +76 -43
  97. package/src/components/chart/builders.test.ts +46 -0
  98. package/src/components/chart/builders.ts +27 -3
  99. package/src/components/chart/chart-live.test.ts +4 -6
  100. package/src/components/chart/chart.component.ts +125 -5
  101. package/src/components/chart/chart.scss +121 -0
  102. package/src/components/chart/chart.test.ts +81 -0
  103. package/src/components/chat-message/chat-message.component.ts +21 -1
  104. package/src/components/chat-message/chat-message.scss +13 -2
  105. package/src/components/chat-message/chat-message.test.ts +9 -0
  106. package/src/components/checkbox/checkbox.component.ts +61 -27
  107. package/src/components/checkbox/checkbox.scss +58 -25
  108. package/src/components/checkbox-group/checkbox-group.component.ts +7 -1
  109. package/src/components/chip/chip.component.ts +11 -1
  110. package/src/components/chip/chip.scss +91 -26
  111. package/src/components/chip/chip.test.ts +17 -0
  112. package/src/components/collapsible/collapsible.component.ts +17 -3
  113. package/src/components/collapsible/collapsible.scss +19 -10
  114. package/src/components/color-picker/color-picker.component.ts +277 -0
  115. package/src/components/color-picker/color-picker.scss +141 -0
  116. package/src/components/color-picker/color-picker.test.ts +57 -0
  117. package/src/components/color-picker/index.ts +12 -0
  118. package/src/components/cols/cols.component.ts +17 -3
  119. package/src/components/cols/cols.scss +24 -13
  120. package/src/components/cols/cols.test.ts +33 -0
  121. package/src/components/confirm/confirm.component.ts +20 -6
  122. package/src/components/confirm/confirm.test.ts +52 -0
  123. package/src/components/content-block/content-block.component.ts +38 -9
  124. package/src/components/content-block/content-block.scss +11 -5
  125. package/src/components/content-block/content-block.test.ts +26 -0
  126. package/src/components/copy-button/copy-button.component.ts +2 -2
  127. package/src/components/copy-button/copy-button.scss +17 -4
  128. package/src/components/data-select/data-select.component.ts +3 -1
  129. package/src/components/data-table/data-table.component.ts +364 -142
  130. package/src/components/data-table/data-table.scss +133 -60
  131. package/src/components/data-table/data-table.test.ts +356 -4
  132. package/src/components/data-table-filter/data-table-filter.component.ts +426 -406
  133. package/src/components/data-table-filter/data-table-filter.scss +176 -148
  134. package/src/components/data-table-filter/data-table-filter.test.ts +271 -273
  135. package/src/components/data-table-search/data-table-search.component.ts +4 -2
  136. package/src/components/data-table-search/data-table-search.scss +8 -1
  137. package/src/components/data-table-search/data-table-search.test.ts +29 -0
  138. package/src/components/date-range/date-range-math.test.ts +78 -0
  139. package/src/components/date-range/date-range-math.ts +105 -0
  140. package/src/components/date-range/date-range.component.ts +375 -0
  141. package/src/components/date-range/date-range.scss +272 -0
  142. package/src/components/date-range/date-range.test.ts +247 -0
  143. package/src/components/date-range/index.ts +12 -0
  144. package/src/components/datepicker/datepicker.component.ts +44 -7
  145. package/src/components/datepicker/datepicker.scss +9 -0
  146. package/src/components/datepicker/datepicker.test.ts +2 -2
  147. package/src/components/dialog/dialog.component.ts +38 -19
  148. package/src/components/dialog/dialog.scss +65 -36
  149. package/src/components/dialog/dialog.test.ts +14 -0
  150. package/src/components/diff/diff.component.ts +55 -0
  151. package/src/components/diff/diff.scss +46 -0
  152. package/src/components/diff/diff.test.ts +15 -0
  153. package/src/components/diff/index.ts +12 -0
  154. package/src/components/diff-row/diff-row.component.ts +75 -0
  155. package/src/components/diff-row/diff-row.scss +70 -0
  156. package/src/components/diff-row/diff-row.test.ts +37 -0
  157. package/src/components/diff-row/index.ts +12 -0
  158. package/src/components/dropdown/dropdown.component.ts +12 -10
  159. package/src/components/dropdown/dropdown.scss +2 -0
  160. package/src/components/editor/editor.component.ts +159 -11
  161. package/src/components/editor/editor.scss +250 -1
  162. package/src/components/editor/editor.test.ts +31 -3
  163. package/src/components/editor/modules/ai/panel/ai-panel.scss +0 -1
  164. package/src/components/editor/modules/attachment/attachment.ts +53 -31
  165. package/src/components/editor/modules/context-menu/context-menu.ts +17 -0
  166. package/src/components/editor/modules/dialog/dialog.component.ts +23 -1
  167. package/src/components/editor/modules/dialog/dialog.test.ts +28 -0
  168. package/src/components/editor/modules/emoji/emoji.ts +4 -5
  169. package/src/components/editor/modules/merge-field/merge-field.ts +83 -0
  170. package/src/components/editor/modules/toolbar/toolbar.component.ts +121 -68
  171. package/src/components/editor/modules/toolbar/toolbar.scss +38 -0
  172. package/src/components/editor/modules/toolbar/toolbar.ts +25 -1
  173. package/src/components/empty-state/empty-state.component.ts +6 -1
  174. package/src/components/empty-state/empty-state.scss +21 -4
  175. package/src/components/expanding-action/expanding-action.component.ts +22 -34
  176. package/src/components/expanding-action/expanding-action.scss +3 -45
  177. package/src/components/expanding-action/expanding-action.test.ts +2 -1
  178. package/src/components/file/file.component.ts +30 -2
  179. package/src/components/file/file.scss +6 -0
  180. package/src/components/filter-wrapper/filter-wrapper.component.ts +45 -10
  181. package/src/components/filter-wrapper/filter-wrapper.test.ts +58 -2
  182. package/src/components/flow-builder/flow-builder.component.ts +36 -3
  183. package/src/components/flow-builder/flow-builder.scss +0 -6
  184. package/src/components/flow-builder/modules/flow-canvas/flow-canvas.component.ts +8 -0
  185. package/src/components/flow-builder/modules/flow-canvas/flow-canvas.scss +10 -5
  186. package/src/components/flow-builder/modules/flow-node/flow-node.component.ts +17 -0
  187. package/src/components/flow-builder/modules/flow-node/flow-node.scss +10 -0
  188. package/src/components/form-actions/form-actions.component.ts +48 -2
  189. package/src/components/form-actions/form-actions.scss +1 -1
  190. package/src/components/form-group/form-group.component.ts +10 -3
  191. package/src/components/form-group/form-group.scss +37 -4
  192. package/src/components/form-group/form-group.test.ts +10 -0
  193. package/src/components/header/header.component.ts +11 -4
  194. package/src/components/header/header.scss +47 -5
  195. package/src/components/hover-container/hover-container.component.ts +4 -5
  196. package/src/components/icon/icon.component.ts +95 -9
  197. package/src/components/icon/icon.scss +70 -6
  198. package/src/components/icon/icon.test.ts +55 -0
  199. package/src/components/icon-picker/icon-picker.component.ts +35 -1
  200. package/src/components/inline-edit/inline-edit.component.ts +38 -8
  201. package/src/components/inline-edit/inline-edit.scss +128 -19
  202. package/src/components/input/input.component.ts +173 -297
  203. package/src/components/input/input.scss +65 -43
  204. package/src/components/input/input.test.ts +86 -4
  205. package/src/components/item/item.component.ts +12 -0
  206. package/src/components/item/item.scss +102 -3
  207. package/src/components/item/item.test.ts +23 -0
  208. package/src/components/key/key.component.ts +2 -2
  209. package/src/components/key/key.scss +11 -0
  210. package/src/components/key-value/index.ts +12 -0
  211. package/src/components/key-value/key-value.component.ts +160 -0
  212. package/src/components/key-value/key-value.scss +20 -0
  213. package/src/components/key-value/key-value.test.ts +22 -0
  214. package/src/components/markdown-editor/markdown-editor.component.ts +8 -1
  215. package/src/components/menu/menu.component.ts +21 -2
  216. package/src/components/menu/menu.scss +43 -23
  217. package/src/components/menu/menu.test.ts +33 -0
  218. package/src/components/menu-item/menu-item.component.ts +13 -2
  219. package/src/components/menu-item/menu-item.scss +56 -23
  220. package/src/components/menu-item/menu-item.test.ts +31 -0
  221. package/src/components/menu-item/submenu-controller.ts +25 -15
  222. package/src/components/menu-label/index.ts +12 -0
  223. package/src/components/menu-label/menu-label.component.ts +30 -0
  224. package/src/components/menu-label/menu-label.scss +18 -0
  225. package/src/components/nav-item/index.ts +12 -0
  226. package/src/components/nav-item/nav-item.component.ts +83 -0
  227. package/src/components/nav-item/nav-item.scss +128 -0
  228. package/src/components/nav-list/index.ts +12 -0
  229. package/src/components/nav-list/nav-list.component.ts +313 -0
  230. package/src/components/nav-list/nav-list.scss +87 -0
  231. package/src/components/nav-list/nav-list.test.ts +250 -0
  232. package/src/components/navbar/navbar.component.ts +235 -171
  233. package/src/components/navbar/navbar.scss +333 -169
  234. package/src/components/navbar/navbar.test.ts +63 -143
  235. package/src/components/page/page.component.ts +114 -29
  236. package/src/components/page/page.scss +223 -95
  237. package/src/components/page/page.test.ts +214 -3
  238. package/src/components/page-builder/page-builder.component.ts +51 -5
  239. package/src/components/page-builder/page-builder.scss +6 -1
  240. package/src/components/page-nav/page-nav.component.ts +25 -3
  241. package/src/components/page-nav/page-nav.scss +10 -2
  242. package/src/components/pagination/pagination.scss +1 -1
  243. package/src/components/panel/panel.component.ts +14 -13
  244. package/src/components/panel/panel.scss +14 -10
  245. package/src/components/panel/panel.test.ts +42 -0
  246. package/src/components/popup/popup.component.ts +20 -13
  247. package/src/components/popup/popup.test.ts +21 -1
  248. package/src/components/priority-list/priority-list.component.ts +157 -172
  249. package/src/components/priority-list/priority-list.scss +113 -17
  250. package/src/components/priority-list/priority-list.test.ts +301 -3
  251. package/src/components/progress-bar/progress-bar.component.ts +1 -1
  252. package/src/components/progress-bar/progress-bar.scss +16 -4
  253. package/src/components/progress-tile/progress-tile.scss +1 -1
  254. package/src/components/query-builder/query-builder.component.ts +85 -10
  255. package/src/components/query-builder/query-builder.scss +7 -1
  256. package/src/components/query-builder/query-builder.test.ts +40 -0
  257. package/src/components/radio/radio.component.ts +57 -18
  258. package/src/components/radio/radio.scss +101 -26
  259. package/src/components/radio-group/radio-group.component.ts +45 -7
  260. package/src/components/radio-group/radio-group.scss +17 -0
  261. package/src/components/radio-group/radio-group.test.ts +63 -2
  262. package/src/components/rating/rating.component.ts +23 -0
  263. package/src/components/rating/rating.scss +1 -1
  264. package/src/components/remarkd-editor/remarkd-editor.component.ts +60 -5
  265. package/src/components/remarkd-editor/remarkd-editor.scss +12 -4
  266. package/src/components/report-table/index.ts +12 -0
  267. package/src/components/report-table/report-format.test.ts +86 -0
  268. package/src/components/report-table/report-format.ts +72 -0
  269. package/src/components/report-table/report-math.test.ts +74 -0
  270. package/src/components/report-table/report-math.ts +252 -0
  271. package/src/components/report-table/report-table.component.ts +681 -0
  272. package/src/components/report-table/report-table.scss +328 -0
  273. package/src/components/report-table/report-table.test.ts +702 -0
  274. package/src/components/reveal/reveal.component.ts +15 -1
  275. package/src/components/reveal/reveal.scss +5 -0
  276. package/src/components/schedule-builder/schedule-builder.component.ts +23 -8
  277. package/src/components/scroll-container/scroll-container.component.ts +19 -1
  278. package/src/components/scroll-container/scroll-container.scss +6 -1
  279. package/src/components/select/select.component.ts +52 -36
  280. package/src/components/select/select.scss +42 -25
  281. package/src/components/select/select.test.ts +14 -2
  282. package/src/components/settings-container/settings-container.scss +18 -5
  283. package/src/components/sidebar/sidebar.component.ts +8 -1
  284. package/src/components/sidebar/sidebar.scss +6 -1
  285. package/src/components/skeleton/skeleton.component.ts +3 -3
  286. package/src/components/skeleton/skeleton.scss +6 -6
  287. package/src/components/skeleton/skeleton.test.ts +9 -0
  288. package/src/components/slash-menu/slash-menu.scss +1 -1
  289. package/src/components/slideout/slideout.component.ts +20 -3
  290. package/src/components/slideout/slideout.scss +13 -6
  291. package/src/components/slideout/slideout.test.ts +19 -0
  292. package/src/components/split-button/split-button.component.ts +1 -1
  293. package/src/components/split-pane/split-pane.component.ts +71 -8
  294. package/src/components/split-pane/split-pane.scss +145 -41
  295. package/src/components/split-pane/split-pane.test.ts +23 -3
  296. package/src/components/stat/stat.component.ts +209 -14
  297. package/src/components/stat/stat.scss +305 -5
  298. package/src/components/stat/stat.test.ts +143 -2
  299. package/src/components/status-indicator/status-indicator.scss +22 -21
  300. package/src/components/stepper/stepper.scss +3 -3
  301. package/src/components/tab/tab.component.ts +1 -0
  302. package/src/components/table/table.component.ts +12 -1
  303. package/src/components/table/table.scss +21 -13
  304. package/src/components/table/table.test.ts +17 -0
  305. package/src/components/tabs/tabs.component.ts +150 -37
  306. package/src/components/tabs/tabs.scss +48 -7
  307. package/src/components/tabs/tabs.test.ts +169 -0
  308. package/src/components/textarea/textarea.component.ts +25 -3
  309. package/src/components/textarea/textarea.scss +10 -0
  310. package/src/components/textarea/textarea.test.ts +8 -4
  311. package/src/components/theme-editor/theme-editor.scss +0 -6
  312. package/src/components/tile/tile.component.ts +32 -2
  313. package/src/components/tile/tile.scss +40 -7
  314. package/src/components/tile/tile.test.ts +10 -0
  315. package/src/components/toggle/toggle.component.ts +23 -7
  316. package/src/components/toggle/toggle.scss +36 -29
  317. package/src/components/toggle/toggle.test.ts +17 -0
  318. package/src/components/tooltip/tooltip.component.ts +3 -0
  319. package/src/components/tooltip/tooltip.scss +11 -6
  320. package/src/components/tooltip/tooltip.test.ts +12 -0
  321. package/src/components/translation-field/index.ts +12 -0
  322. package/src/components/translation-field/translation-field.component.ts +55 -0
  323. package/src/components/translation-field/translation-field.scss +101 -0
  324. package/src/components/translation-fields/index.ts +12 -0
  325. package/src/components/translation-fields/translation-fields.component.ts +80 -0
  326. package/src/components/translation-fields/translation-fields.scss +60 -0
  327. package/src/components/translation-grid/index.ts +12 -0
  328. package/src/components/translation-grid/translation-grid.component.ts +157 -0
  329. package/src/components/translation-grid/translation-grid.scss +138 -0
  330. package/src/components/translation-grid/translation-grid.test.ts +28 -0
  331. package/src/components/vertical-stepper/vertical-stepper.component.ts +35 -9
  332. package/src/components/vertical-stepper/vertical-stepper.scss +110 -39
  333. package/src/components/vertical-stepper/vertical-stepper.test.ts +35 -0
  334. package/src/components/well/well.scss +2 -2
  335. package/src/events/events.ts +2 -0
  336. package/src/events/zn-chart-click.ts +13 -0
  337. package/src/events/zn-report-change.ts +7 -0
  338. package/src/form-control.scss +36 -3
  339. package/src/internal/anchored-popover.ts +175 -0
  340. package/src/internal/animate.ts +19 -5
  341. package/src/internal/cosmic-border.ts +15 -0
  342. package/src/internal/form.ts +1 -1
  343. package/src/internal/hover-intent.ts +15 -0
  344. package/src/internal/input-modality.ts +15 -0
  345. package/src/internal/pointer-intent.ts +37 -0
  346. package/src/internal/reorder.ts +625 -0
  347. package/src/internal/scroll.ts +51 -3
  348. package/src/internal/selection-card.ts +1 -1
  349. package/src/internal/tab-indicator.ts +92 -0
  350. package/src/internal/table-sort.ts +25 -0
  351. package/src/internal/table-tabs.ts +108 -0
  352. package/src/internal/test-drag.ts +35 -0
  353. package/src/internal/theme-openbyte.test.ts +126 -0
  354. package/src/internal/theme.ts +4 -4
  355. package/src/internal/zinc-element.ts +2 -2
  356. package/src/selection-card.scss +25 -0
  357. package/src/types/web-test-runner-commands.d.ts +1 -0
  358. package/src/utilities/color.test.ts +43 -0
  359. package/src/utilities/color.ts +189 -0
  360. package/src/zinc.ts +14 -0
  361. package/.github/workflows/eleventy_build.yml +0 -42
  362. package/scss/themes/_aura.scss +0 -114
@@ -1,4 +1,5 @@
1
1
  import {classMap} from "lit/directives/class-map.js";
2
+ import {type ColorFormat, detectColorFormat, formatColor, parseColor, toHex} from "../../utilities/color";
2
3
  import {defaultValue} from "../../internal/default-value";
3
4
  import {FormControlController} from "../../internal/form";
4
5
  import {getSlashMenuPreset, parseSlashItems, type SlashMenuItem} from "../slash-menu";
@@ -10,7 +11,9 @@ import {LocalizeController} from "../../utilities/localize";
10
11
  import {property, query, state} from 'lit/decorators.js';
11
12
  import {watch} from "../../internal/watch";
12
13
  import ZincElement from '../../internal/zinc-element';
14
+ import ZnColorPicker from "../color-picker";
13
15
  import ZnIcon from "../icon";
16
+ import ZnPopup from "../popup";
14
17
  import ZnSlashItem from "../slash-item";
15
18
  import ZnSlashMenu, {SlashMenuController} from "../slash-menu";
16
19
  import ZnTooltip from "../tooltip";
@@ -25,7 +28,9 @@ import styles from './input.scss';
25
28
  * @status experimental
26
29
  * @since 1.0
27
30
  *
31
+ * @dependency zn-color-picker
28
32
  * @dependency zn-icon
33
+ * @dependency zn-popup
29
34
  * @dependency zn-tooltip
30
35
  *
31
36
  * @event zn-blur - Emitted when the control loses focus.
@@ -67,6 +72,8 @@ import styles from './input.scss';
67
72
  * @csspart clear-button - The clear button.
68
73
  * @csspart password-toggle-button - The password toggle button.
69
74
  * @csspart suffix - The container that wraps the suffix.
75
+ * @csspart color-swatch - The swatch that opens the colour panel on colour inputs.
76
+ * @csspart color-picker - The colour panel.
70
77
  *
71
78
  * @cssproperty --zn-range-track-height - The height of a range input's track.
72
79
  * @cssproperty --zn-range-track-color - The color of a range input's track.
@@ -77,7 +84,9 @@ import styles from './input.scss';
77
84
  export default class ZnInput extends ZincElement implements ZincFormControl {
78
85
  static styles = [unsafeCSS(formControlStyles), unsafeCSS(styles)];
79
86
  static dependencies = {
87
+ 'zn-color-picker': ZnColorPicker,
80
88
  'zn-icon': ZnIcon,
89
+ 'zn-popup': ZnPopup,
81
90
  'zn-slash-item': ZnSlashItem,
82
91
  'zn-slash-menu': ZnSlashMenu,
83
92
  'zn-tooltip': ZnTooltip
@@ -100,7 +109,9 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
100
109
  });
101
110
 
102
111
  @query('.input__control') input: HTMLInputElement;
103
- @query('.input__color-picker') colorPicker: HTMLInputElement;
112
+ @query('.input__color-swatch') colorSwatch: HTMLButtonElement;
113
+ @query('zn-color-picker') colorPanel: ZnColorPicker;
114
+ @query('.input__color-popup') colorPopup: ZnPopup;
104
115
  @query('zn-slash-menu') slashMenuElement: ZnSlashMenu | null;
105
116
 
106
117
  @state() private hasFocus = false;
@@ -108,6 +119,9 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
108
119
  /** Renders the slash menu only once it has been needed, keeping unused inputs cheap. */
109
120
  @state() private hasSlashMenu = false;
110
121
  @state() private isUserTyping = false;
122
+ @state() private colorOpen = false;
123
+ @state() private colorInvalid = false;
124
+ private escapeSwallowed = false;
111
125
  @property() title = "" // make reactive pass through
112
126
 
113
127
  private __numberInput = Object.assign(document.createElement('input'), {type: 'number'});
@@ -181,8 +195,11 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
181
195
  /** Hides the browsers built-in increment/decrement spin buttons for number inputs **/
182
196
  @property({attribute: 'no-spin-buttons', type: Boolean}) noSpinButtons: boolean = false;
183
197
 
184
- /** The color format to display for color inputs. Only applies when type is 'color'. **/
185
- @property({attribute: 'color-format'}) colorFormat: 'hex' | 'rgb' | 'hsl' | 'oklch' = 'hex';
198
+ /**
199
+ * The format a colour input writes its value in. Typing a colour in another format, or working in another format's
200
+ * row of the colour panel, switches to that format. Only applies when type is 'color'.
201
+ */
202
+ @property({attribute: 'color-format'}) colorFormat: ColorFormat = 'hex';
186
203
 
187
204
  /**
188
205
  * Appended to the value that range inputs display above the track, on the right, e.g. `rem`. Only applies when type
@@ -200,6 +217,9 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
200
217
  /** Makes the input a required field. */
201
218
  @property({type: Boolean, reflect: true}) required = false;
202
219
 
220
+ /** Short text shown next to the label, styled like the help text, e.g. `optional`. */
221
+ @property({attribute: 'label-note'}) labelNote = '';
222
+
203
223
  /** A regular expression pattern to validate input against. */
204
224
  @property() pattern: string;
205
225
 
@@ -354,201 +374,34 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
354
374
  }
355
375
  }
356
376
 
357
- // Color format conversion utilities
358
- private hexToRgb(hex: string): string {
359
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
360
- if (!result) return '';
361
- const r = parseInt(result[1], 16);
362
- const g = parseInt(result[2], 16);
363
- const b = parseInt(result[3], 16);
364
- return `rgb(${r}, ${g}, ${b})`;
365
- }
366
-
367
- private rgbToHex(rgb: string): string {
368
- const match = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
369
- if (!match) return '';
370
- const r = parseInt(match[1]).toString(16).padStart(2, '0');
371
- const g = parseInt(match[2]).toString(16).padStart(2, '0');
372
- const b = parseInt(match[3]).toString(16).padStart(2, '0');
373
- return `#${r}${g}${b}`;
377
+ /**
378
+ * A colour value as it should be shown: kept as written when it is already a tidy value in the current format (so
379
+ * `oklch(70% 0.15 250)` isn't nudged by rounding), otherwise written out in full. Anything that isn't a colour is
380
+ * returned untouched.
381
+ */
382
+ private tidyColor(value: string): string {
383
+ const text = String(value ?? '').trim();
384
+ const rgb = parseColor(text);
385
+ if (!rgb) return value;
386
+ const format = detectColorFormat(text);
387
+ if (format === this.colorFormat && format !== 'hex') return text;
388
+ return formatColor(rgb, this.colorFormat);
374
389
  }
375
390
 
376
- private hexToOklch(hex: string): string {
377
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
378
- if (!result) return '';
379
-
380
- // Convert hex to RGB (0-1 range)
381
- let r = parseInt(result[1], 16) / 255;
382
- let g = parseInt(result[2], 16) / 255;
383
- let b = parseInt(result[3], 16) / 255;
384
-
385
- // Apply gamma correction
386
- r = r > 0.04045 ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;
387
- g = g > 0.04045 ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;
388
- b = b > 0.04045 ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92;
389
-
390
- // Convert to XYZ
391
- const x = r * 0.4124564 + g * 0.3575761 + b * 0.1804375;
392
- const y = r * 0.2126729 + g * 0.7151522 + b * 0.0721750;
393
- const z = r * 0.0193339 + g * 0.1191920 + b * 0.9503041;
394
-
395
- // Convert XYZ to Lab
396
- const xn = x / 0.95047;
397
- const yn = y / 1.00000;
398
- const zn = z / 1.08883;
399
-
400
- const fx = xn > 0.008856 ? Math.pow(xn, 1 / 3) : (7.787 * xn + 16 / 116);
401
- const fy = yn > 0.008856 ? Math.pow(yn, 1 / 3) : (7.787 * yn + 16 / 116);
402
- const fz = zn > 0.008856 ? Math.pow(zn, 1 / 3) : (7.787 * zn + 16 / 116);
403
-
404
- const L = 116 * fy - 16;
405
- const a = 500 * (fx - fy);
406
- const b_lab = 200 * (fy - fz);
407
-
408
- // Convert Lab to LCH (which is close to OKLCH)
409
- const C = Math.sqrt(a * a + b_lab * b_lab);
410
- let H = Math.atan2(b_lab, a) * 180 / Math.PI;
411
- if (H < 0) H += 360;
412
-
413
- // Normalize to OKLCH ranges
414
- const l = (L / 100).toFixed(3);
415
- const c = (C / 150).toFixed(3);
416
- const h = H.toFixed(1);
417
-
418
- return `oklch(${l} ${c} ${h})`;
391
+ private get colorText(): string {
392
+ return String(this.value ?? '');
419
393
  }
420
394
 
421
- private oklchToHex(oklch: string): string {
422
- const match = oklch.match(/^oklch\(([0-9.]+)\s+([0-9.]+)\s+([0-9.]+)\)$/);
423
- if (!match) return '';
424
-
425
- const L = parseFloat(match[1]) * 100;
426
- const C = parseFloat(match[2]) * 150;
427
- const H = parseFloat(match[3]);
428
-
429
- // Convert LCH to Lab
430
- const a = C * Math.cos(H * Math.PI / 180);
431
- const b_lab = C * Math.sin(H * Math.PI / 180);
432
-
433
- // Convert Lab to XYZ
434
- const fy = (L + 16) / 116;
435
- const fx = a / 500 + fy;
436
- const fz = fy - b_lab / 200;
437
-
438
- const xn = fx > 0.206897 ? Math.pow(fx, 3) : (fx - 16 / 116) / 7.787;
439
- const yn = fy > 0.206897 ? Math.pow(fy, 3) : (fy - 16 / 116) / 7.787;
440
- const zn = fz > 0.206897 ? Math.pow(fz, 3) : (fz - 16 / 116) / 7.787;
441
-
442
- const x = xn * 0.95047;
443
- const y = yn * 1.00000;
444
- const z = zn * 1.08883;
445
-
446
- // Convert XYZ to RGB
447
- let r = x * 3.2404542 + y * -1.5371385 + z * -0.4985314;
448
- let g = x * -0.9692660 + y * 1.8760108 + z * 0.0415560;
449
- let b = x * 0.0556434 + y * -0.2040259 + z * 1.0572252;
450
-
451
- // Apply gamma correction
452
- r = r > 0.0031308 ? 1.055 * Math.pow(r, 1 / 2.4) - 0.055 : 12.92 * r;
453
- g = g > 0.0031308 ? 1.055 * Math.pow(g, 1 / 2.4) - 0.055 : 12.92 * g;
454
- b = b > 0.0031308 ? 1.055 * Math.pow(b, 1 / 2.4) - 0.055 : 12.92 * b;
455
-
456
- // Clamp and convert to hex
457
- r = Math.max(0, Math.min(1, r));
458
- g = Math.max(0, Math.min(1, g));
459
- b = Math.max(0, Math.min(1, b));
460
-
461
- const rHex = Math.round(r * 255).toString(16).padStart(2, '0');
462
- const gHex = Math.round(g * 255).toString(16).padStart(2, '0');
463
- const bHex = Math.round(b * 255).toString(16).padStart(2, '0');
464
-
465
- return `#${rHex}${gHex}${bHex}`;
395
+ private get colorHex(): string {
396
+ const rgb = parseColor(this.colorText);
397
+ return rgb ? toHex(rgb) : '';
466
398
  }
467
399
 
468
- private hexToHsl(hex: string): string {
469
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
470
- if (!result) return '';
471
- const r = parseInt(result[1], 16) / 255;
472
- const g = parseInt(result[2], 16) / 255;
473
- const b = parseInt(result[3], 16) / 255;
474
-
475
- const max = Math.max(r, g, b);
476
- const min = Math.min(r, g, b);
477
- const l = (max + min) / 2;
478
- let h = 0;
479
- let s = 0;
480
-
481
- if (max !== min) {
482
- const d = max - min;
483
- s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
484
- switch (max) {
485
- case r:
486
- h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
487
- break;
488
- case g:
489
- h = ((b - r) / d + 2) / 6;
490
- break;
491
- case b:
492
- h = ((r - g) / d + 4) / 6;
493
- break;
494
- }
400
+ private commitColor() {
401
+ this.colorInvalid = Boolean(this.colorText) && !parseColor(this.colorText);
402
+ if (!this.colorInvalid && this.colorText) {
403
+ this.value = this.tidyColor(this.colorText);
495
404
  }
496
-
497
- return `hsl(${Math.round(h * 360)}, ${Math.round(s * 100)}%, ${Math.round(l * 100)}%)`;
498
- }
499
-
500
- private hslToHex(hsl: string): string {
501
- const match = hsl.match(/^hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$/);
502
- if (!match) return '';
503
- const h = parseInt(match[1]) / 360;
504
- const s = parseInt(match[2]) / 100;
505
- const l = parseInt(match[3]) / 100;
506
-
507
- let r: number;
508
- let g: number;
509
- let b: number;
510
- if (s === 0) {
511
- r = l;
512
- g = l;
513
- b = l;
514
- } else {
515
- const hue2rgb = (p: number, q: number, t: number) => {
516
- if (t < 0) t += 1;
517
- if (t > 1) t -= 1;
518
- if (t < 1 / 6) return p + (q - p) * 6 * t;
519
- if (t < 1 / 2) return q;
520
- if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
521
- return p;
522
- };
523
- const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
524
- const p = 2 * l - q;
525
- r = hue2rgb(p, q, h + 1 / 3);
526
- g = hue2rgb(p, q, h);
527
- b = hue2rgb(p, q, h - 1 / 3);
528
- }
529
-
530
- const rHex = Math.round(r * 255).toString(16).padStart(2, '0');
531
- const gHex = Math.round(g * 255).toString(16).padStart(2, '0');
532
- const bHex = Math.round(b * 255).toString(16).padStart(2, '0');
533
- return `#${rHex}${gHex}${bHex}`;
534
- }
535
-
536
- private convertToHex(value: string): string {
537
- if (!value) return '#000000';
538
- if (value.startsWith('#')) return value;
539
- if (value.startsWith('rgb')) return this.rgbToHex(value);
540
- if (value.startsWith('hsl')) return this.hslToHex(value);
541
- if (value.startsWith('oklch')) return this.oklchToHex(value);
542
- return value;
543
- }
544
-
545
- private convertFromHex(hex: string): string {
546
- if (!hex) return '';
547
- if (this.colorFormat === 'hex') return hex;
548
- if (this.colorFormat === 'rgb') return this.hexToRgb(hex);
549
- if (this.colorFormat === 'hsl') return this.hexToHsl(hex);
550
- if (this.colorFormat === 'oklch') return this.hexToOklch(hex);
551
- return hex;
552
405
  }
553
406
 
554
407
  private handleBlur() {
@@ -556,15 +409,8 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
556
409
  this.isUserTyping = false;
557
410
  this.validateMinMax();
558
411
 
559
- // Normalize color value to the correct format on blur
560
- if (this.type === 'color' && this.value) {
561
- const hexValue = this.convertToHex(this.value);
562
- if (hexValue) {
563
- const normalizedValue = this.convertFromHex(hexValue);
564
- if (normalizedValue !== this.value) {
565
- this.value = normalizedValue;
566
- }
567
- }
412
+ if (this.type === 'color') {
413
+ this.commitColor();
568
414
  }
569
415
 
570
416
  this.emit('zn-blur');
@@ -574,37 +420,8 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
574
420
  if (this.type === 'currency' && this.input.value) {
575
421
  this.value = parseFloat(this.input.value as string).toFixed(2)
576
422
  } else if (this.type === 'color') {
577
- const colorValue = this.input.value.trim();
578
-
579
- // Validate based on format
580
- if (colorValue === '') {
581
- this.value = '';
582
- } else if (this.colorFormat === 'hex') {
583
- // Validate hex format
584
- let hexValue = colorValue;
585
- if (hexValue && !hexValue.startsWith('#')) {
586
- hexValue = '#' + hexValue;
587
- }
588
- if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(hexValue)) {
589
- this.value = hexValue.toLowerCase();
590
- }
591
- } else if (this.colorFormat === 'rgb') {
592
- // Validate rgb format
593
- if (/^rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)$/.test(colorValue)) {
594
- this.value = colorValue;
595
- }
596
- } else if (this.colorFormat === 'hsl') {
597
- // Validate hsl format
598
- if (/^hsl\(\s*\d+\s*,\s*\d+%\s*,\s*\d+%\s*\)$/.test(colorValue)) {
599
- this.value = colorValue;
600
- }
601
- } else if (this.colorFormat === 'oklch') {
602
- // Validate oklch format
603
- if (/^oklch\(\s*[0-9.]+\s+[0-9.]+\s+[0-9.]+\s*\)$/.test(colorValue)) {
604
- this.value = colorValue;
605
- }
606
- }
607
- // If invalid, keep the previous value
423
+ this.value = this.input.value;
424
+ this.commitColor();
608
425
  } else {
609
426
  this.value = this.input.value;
610
427
  this.syncEmailDisplay();
@@ -630,11 +447,10 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
630
447
 
631
448
  private handleInput() {
632
449
  this.isUserTyping = true;
633
- if (this.type === 'color') {
634
- // For color inputs, update value on input but don't validate format yet
635
- this.value = this.input.value;
636
- } else {
637
- this.value = this.input.value;
450
+ this.value = this.input.value;
451
+ if (this.type === 'color' && parseColor(this.colorText)) {
452
+ this.colorFormat = detectColorFormat(this.colorText) ?? this.colorFormat;
453
+ this.colorInvalid = false;
638
454
  }
639
455
  this.formControlController.updateValidity();
640
456
  this.emit('zn-input');
@@ -654,14 +470,18 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
654
470
 
655
471
  private handleKeyDown(event: KeyboardEvent) {
656
472
  // The slash menu claims its own keys; while it is open Escape closes it and Enter picks an item,
657
- // rather than blurring the field or submitting the form
473
+ // rather than reaching the field or submitting the form
658
474
  if (this.slashController.open) return;
659
475
 
476
+ // The first Escape stays in the field so it doesn't also dismiss a containing popup; a repeat bubbles
660
477
  if (event.key === 'Escape') {
661
- this.input.blur();
662
- event.stopPropagation();
478
+ if (!this.escapeSwallowed) {
479
+ this.escapeSwallowed = true;
480
+ event.stopPropagation();
481
+ }
663
482
  return;
664
483
  }
484
+ this.escapeSwallowed = false;
665
485
 
666
486
  const hasModifier = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
667
487
 
@@ -689,20 +509,54 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
689
509
  this.passwordVisible = !this.passwordVisible;
690
510
  }
691
511
 
692
- private handleColorSwatchClick() {
693
- if (this.type === 'color' && this.colorPicker) {
694
- this.colorPicker.click();
512
+ private handleColorSwatchClick(event: MouseEvent) {
513
+ // Keep the field's own click handler from moving focus into the text control.
514
+ event.stopPropagation();
515
+ if (this.colorOpen) {
516
+ this.closeColorPanel();
517
+ } else {
518
+ this.openColorPanel();
695
519
  }
696
520
  }
697
521
 
698
- private handleColorPickerChange() {
699
- if (this.type === 'color' && this.colorPicker) {
700
- // Convert hex from native picker to the specified format
701
- const hexValue = this.colorPicker.value;
702
- this.value = this.convertFromHex(hexValue);
703
- this.emit('zn-change');
704
- this.emit('zn-input');
522
+ private openColorPanel() {
523
+ // The popup stays hidden until it is first positioned, and a hidden panel can't take focus.
524
+ this.colorPopup.addEventListener('zn-reposition', () => this.colorPanel?.focus(), {once: true});
525
+ this.colorOpen = true;
526
+ document.addEventListener('mousedown', this.handleColorDocumentMouseDown, true);
527
+ document.addEventListener('keydown', this.handleColorDocumentKeyDown, true);
528
+ }
529
+
530
+ private closeColorPanel(returnFocus = false) {
531
+ this.colorOpen = false;
532
+ document.removeEventListener('mousedown', this.handleColorDocumentMouseDown, true);
533
+ document.removeEventListener('keydown', this.handleColorDocumentKeyDown, true);
534
+ if (returnFocus) this.colorSwatch?.focus();
535
+ }
536
+
537
+ private handleColorDocumentMouseDown = (event: MouseEvent) => {
538
+ const path = event.composedPath();
539
+ if (!path.includes(this.colorPanel) && !path.includes(this.colorSwatch)) {
540
+ this.closeColorPanel();
541
+ }
542
+ };
543
+
544
+ private handleColorDocumentKeyDown = (event: KeyboardEvent) => {
545
+ if (event.key === 'Escape') {
546
+ event.stopPropagation();
547
+ this.closeColorPanel(true);
705
548
  }
549
+ };
550
+
551
+ private handleColorPanelInput(event: Event) {
552
+ event.stopPropagation();
553
+ const rgb = parseColor(this.colorPanel.value);
554
+ if (!rgb) return;
555
+ this.colorFormat = this.colorPanel.format;
556
+ this.colorInvalid = false;
557
+ this.value = formatColor(rgb, this.colorFormat);
558
+ this.emit('zn-input');
559
+ this.emit('zn-change');
706
560
  }
707
561
 
708
562
  private focusInput(event: MouseEvent) {
@@ -727,9 +581,18 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
727
581
  this.value = this.input.value;
728
582
  }
729
583
 
584
+ if (this.type === 'color' && this.colorText) {
585
+ this.commitColor();
586
+ }
587
+
730
588
  this.slashController.attach(this.input);
731
589
  }
732
590
 
591
+ disconnectedCallback() {
592
+ super.disconnectedCallback();
593
+ this.closeColorPanel();
594
+ }
595
+
733
596
  /** The insertions the slash menu offers, gathered from every source, in the order they were declared. */
734
597
  async resolveSlashItems(search = ''): Promise<SlashMenuItem[]> {
735
598
  const items = [
@@ -818,41 +681,27 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
818
681
  async handleValueChange() {
819
682
  await this.updateComplete;
820
683
 
821
- // Normalize color value to match colorFormat, but NOT while user is typing
822
- // Only normalize on initial load or programmatic changes
823
- if (this.type === 'color' && this.value && !this.isUserTyping) {
824
- const hexValue = this.convertToHex(this.value);
825
- if (hexValue) {
826
- const normalizedValue = this.convertFromHex(hexValue);
827
- // Only update if the conversion actually changed something
828
- // This prevents infinite loops because once normalized, it won't change again
829
- if (normalizedValue !== this.value) {
830
- this.value = normalizedValue;
831
- this.emit('zn-change');
832
- return; // Will re-trigger, but next time values will match
833
- }
684
+ // Programmatic values are written in the current format; typed ones are left alone until the field is left
685
+ if (this.type === 'color' && this.colorText && !this.isUserTyping) {
686
+ const tidied = this.tidyColor(this.colorText);
687
+ if (tidied !== this.value) {
688
+ this.value = tidied;
689
+ this.emit('zn-change');
690
+ return; // Re-runs with the tidied value, which tidies to itself
834
691
  }
692
+ this.colorInvalid = !parseColor(this.colorText);
835
693
  }
836
694
 
837
695
  this.formControlController.updateValidity();
838
-
839
- // Update color picker if this is a color input (needs hex)
840
- if (this.type === 'color' && this.colorPicker) {
841
- this.colorPicker.value = this.convertToHex(this.value) || '#000000';
842
- }
843
696
  }
844
697
 
845
698
  @watch('colorFormat', {waitUntilFirstUpdate: true})
846
699
  handleColorFormatChange() {
847
- // When color format changes, convert the current value to the new format
848
- if (this.type === 'color' && this.value) {
849
- const hexValue = this.convertToHex(this.value);
850
- if (hexValue) {
851
- const normalizedValue = this.convertFromHex(hexValue);
852
- if (normalizedValue !== this.value) {
853
- this.value = normalizedValue;
854
- this.emit('zn-change');
855
- }
700
+ if (this.type === 'color' && this.colorText && !this.isUserTyping) {
701
+ const tidied = this.tidyColor(this.colorText);
702
+ if (tidied !== this.value) {
703
+ this.value = tidied;
704
+ this.emit('zn-change');
856
705
  }
857
706
  }
858
707
  }
@@ -978,13 +827,15 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
978
827
  ${hasLabelTooltip
979
828
  ? html`
980
829
  <zn-tooltip class="form-control--label-tooltip">
981
- <div slot="content">
830
+ <div slot="content" id="label-tooltip">
982
831
  <slot name="label-tooltip">${this.labelTooltip}</slot>
983
832
  </div>
984
- <zn-icon src="info"></zn-icon>
833
+ <zn-icon src="info" tabindex="0" role="img" aria-label="More information"
834
+ aria-describedby="label-tooltip"></zn-icon>
985
835
  </zn-tooltip>`
986
836
  : ''}
987
837
 
838
+ ${this.labelNote ? html`<span class="form-control__label-note">${this.labelNote}</span>` : ''}
988
839
  </label>
989
840
 
990
841
  ${hasContextNote
@@ -1011,6 +862,8 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
1011
862
  'input--empty': !this.value,
1012
863
  'input--range': isRange,
1013
864
  'input--no-spin-buttons': this.noSpinButtons && this.type !== 'currency',
865
+ 'input--color': this.type === 'color',
866
+ 'input--color-invalid': this.colorInvalid,
1014
867
  })}>
1015
868
 
1016
869
  <span part="prefix" class="input__prefix">
@@ -1027,6 +880,23 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
1027
880
  <slot name="prefix"></slot>
1028
881
  </span>
1029
882
 
883
+ ${this.type === 'color'
884
+ ? html`
885
+ <button
886
+ id="color-swatch"
887
+ part="color-swatch"
888
+ class="input__color-swatch"
889
+ type="button"
890
+ aria-haspopup="dialog"
891
+ aria-expanded=${this.colorOpen ? 'true' : 'false'}
892
+ aria-label=${this.label ? `Choose a colour: ${this.label}` : 'Choose a colour'}
893
+ ?disabled=${this.disabled || this.readonly}
894
+ @click=${this.handleColorSwatchClick}>
895
+ <span class="input__color-swatch-inner"
896
+ style="background: ${this.colorHex || 'transparent'}"></span>
897
+ </button>`
898
+ : ''}
899
+
1030
900
  <input
1031
901
  part="input"
1032
902
  id="input"
@@ -1054,6 +924,7 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
1054
924
  enterkeyhint=${ifDefined(this.enterkeyhint)}
1055
925
  inputmode=${ifDefined(this.inputmode)}
1056
926
  aria-describedby="help-text"
927
+ aria-invalid=${this.colorInvalid ? 'true' : 'false'}
1057
928
  @change=${this.handleChange}
1058
929
  @input=${this.handleInput}
1059
930
  @invalid=${this.handleInvalid}
@@ -1087,8 +958,7 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
1087
958
  class="input__password-toggle"
1088
959
  type="button"
1089
960
  aria-label=${this.localize.term(this.passwordVisible ? 'hidePassword' : 'showPassword')}
1090
- @click=${this.handlePasswordToggle}
1091
- tabindex="-1">
961
+ @click=${this.handlePasswordToggle}>
1092
962
  ${this.passwordVisible
1093
963
  ? html`
1094
964
  <slot name="show-password-icon">
@@ -1105,25 +975,31 @@ export default class ZnInput extends ZincElement implements ZincFormControl {
1105
975
 
1106
976
  <span part="suffix" class="input__suffix">
1107
977
  <slot name="suffix"></slot>
1108
- ${this.type === 'color'
1109
- ? html`
1110
- <div
1111
- class="input__color-swatch"
1112
- @click=${this.handleColorSwatchClick}
1113
- style="--color-value: ${this.convertToHex(this.value) || '#000000'}">
1114
- <div class="input__color-swatch-inner"></div>
1115
- </div>
1116
- <input
1117
- class="input__color-picker"
1118
- type="color"
1119
- .value=${this.convertToHex(this.value) || '#000000'}
1120
- @change=${this.handleColorPickerChange}
1121
- @input=${this.handleColorPickerChange}
1122
- tabindex="-1"
1123
- />`
1124
- : ''}
1125
978
  </span>
1126
979
  </div>
980
+ ${this.type === 'color'
981
+ ? html`
982
+ <zn-popup
983
+ class="input__color-popup"
984
+ anchor="color-swatch"
985
+ placement="bottom-start"
986
+ strategy="fixed"
987
+ distance="4"
988
+ flip
989
+ shift
990
+ shift-padding="8"
991
+ ?active=${this.colorOpen}>
992
+ ${this.colorOpen
993
+ ? html`
994
+ <zn-color-picker
995
+ part="color-picker"
996
+ .value=${this.colorHex || '#000000'}
997
+ .format=${this.colorFormat}
998
+ label=${this.label || 'Colour'}
999
+ @zn-input=${this.handleColorPanelInput}></zn-color-picker>`
1000
+ : ''}
1001
+ </zn-popup>`
1002
+ : ''}
1127
1003
  ${this.hasSlashMenu
1128
1004
  ? html`
1129
1005
  <zn-slash-menu