@kubex/zinc 1.1.185 → 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 (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 +9998 -2345
  6. package/dist/vscode.html-custom-data.json +1022 -67
  7. package/dist/web-types.json +2129 -177
  8. package/dist/zn.d.ts +2293 -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 +133 -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 +625 -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
@@ -0,0 +1,272 @@
1
+ @use "../../wc";
2
+
3
+ :host {
4
+ display: inline-block;
5
+ }
6
+
7
+ .date-range__trigger {
8
+ --zn-text-button-label-transform: none;
9
+ --zn-text-button-label-font-weight: var(--zn-input-font-weight);
10
+ }
11
+
12
+ .date-range__trigger::part(label) {
13
+ display: inline-flex;
14
+ align-items: center;
15
+ gap: 6px;
16
+ }
17
+
18
+ .date-range__trigger::part(base) {
19
+ font-size: var(--zn-input-font-size-medium);
20
+ font-weight: var(--zn-input-font-weight);
21
+ color: var(--zn-input-color);
22
+ background-color: var(--zn-input-background-color);
23
+ border: solid var(--zn-input-border-width) var(--zn-input-border-color);
24
+ border-radius: var(--zn-input-border-radius-medium);
25
+ box-shadow: var(--zn-input-shadow, none);
26
+ }
27
+
28
+ .date-range__trigger:hover::part(base) {
29
+ background-color: var(--zn-input-background-color-hover);
30
+ border-color: var(--zn-input-border-color-hover);
31
+ }
32
+
33
+ .date-range__trigger::part(icon),
34
+ .date-range__chevron {
35
+ color: var(--zn-input-icon-color);
36
+ }
37
+
38
+ .date-range__pop {
39
+ position: fixed;
40
+ z-index: var(--zn-z-index-dropdown, 900);
41
+ display: flex;
42
+ width: min(736px, 94vw);
43
+ box-sizing: border-box;
44
+ max-height: calc(100vh - 16px);
45
+ overflow: auto;
46
+ background: rgb(var(--zn-panel));
47
+ border: 1px solid rgb(var(--zn-border-color));
48
+ border-radius: var(--zn-border-radius-large, 8px);
49
+ box-shadow: var(--zn-shadow-large, 0 12px 32px rgb(15 23 42 / 0.14));
50
+ color: rgb(var(--zn-text));
51
+ font-size: var(--zn-font-size-small);
52
+ }
53
+
54
+ .date-range__presets {
55
+ display: flex;
56
+ flex: 0 0 168px;
57
+ flex-direction: column;
58
+ gap: 1px;
59
+ padding: var(--zn-spacing-x-small);
60
+ box-sizing: border-box;
61
+ background: color-mix(in srgb, rgb(var(--zn-primary)) 4%, rgb(var(--zn-panel)));
62
+ border-right: 1px solid rgb(var(--zn-border-color));
63
+ }
64
+
65
+ .date-range__preset {
66
+ height: var(--zn-menu-item-min-height, 36px);
67
+ padding: 0 10px;
68
+ border: 0;
69
+ border-radius: var(--zn-border-radius-medium, 6px);
70
+ background: none;
71
+ color: inherit;
72
+ font: inherit;
73
+ font-weight: var(--zn-font-weight-medium, 500);
74
+ text-align: left;
75
+ cursor: pointer;
76
+
77
+ &:hover {
78
+ background: color-mix(in srgb, rgb(var(--zn-primary)) 8%, transparent);
79
+ }
80
+
81
+ &.is-active {
82
+ background: color-mix(in srgb, rgb(var(--zn-primary)) 8%, transparent);
83
+ color: rgb(var(--zn-primary));
84
+ font-weight: var(--zn-font-weight-bold, 600);
85
+ }
86
+
87
+ &[aria-disabled="true"] {
88
+ background: none;
89
+ color: color-mix(in srgb, rgb(var(--zn-text)) 35%, transparent);
90
+ cursor: default;
91
+ }
92
+
93
+ &:focus-visible {
94
+ outline: 2px solid rgb(var(--zn-primary));
95
+ outline-offset: -2px;
96
+ }
97
+ }
98
+
99
+ .date-range__main {
100
+ display: flex;
101
+ flex: 1 1 auto;
102
+ flex-direction: column;
103
+ gap: 12px;
104
+ min-width: 0;
105
+ padding: var(--zn-spacing-small, 16px);
106
+ }
107
+
108
+ .date-range__months {
109
+ position: relative;
110
+ display: grid;
111
+ grid-template-columns: 1fr 1fr;
112
+ gap: 20px;
113
+ }
114
+
115
+ .date-range__head {
116
+ display: flex;
117
+ align-items: center;
118
+ justify-content: center;
119
+ height: var(--zn-density-control-height, 36px);
120
+ font-weight: var(--zn-font-weight-bold, 600);
121
+ }
122
+
123
+ .date-range__nav {
124
+ position: absolute;
125
+ z-index: 1;
126
+ top: 0;
127
+ }
128
+
129
+ .date-range__nav--prev {
130
+ left: 0;
131
+ }
132
+
133
+ .date-range__nav--next {
134
+ right: 0;
135
+ }
136
+
137
+ .date-range__week,
138
+ .date-range__row {
139
+ display: grid;
140
+ grid-template-columns: repeat(7, 1fr);
141
+ }
142
+
143
+ .date-range__grid {
144
+ display: flex;
145
+ flex-direction: column;
146
+ gap: 2px;
147
+ }
148
+
149
+ .date-range__week span {
150
+ color: color-mix(in srgb, rgb(var(--zn-text)) 55%, transparent);
151
+ font-size: 11px;
152
+ font-weight: var(--zn-font-weight-bold, 600);
153
+ letter-spacing: 0.04em;
154
+ line-height: 19px;
155
+ text-align: center;
156
+ text-transform: uppercase;
157
+ }
158
+
159
+ .date-range__day {
160
+ height: 32px;
161
+ padding: 0;
162
+ border: 0;
163
+ border-radius: var(--zn-border-radius-medium, 6px);
164
+ background: none;
165
+ color: inherit;
166
+ font: inherit;
167
+ font-weight: var(--zn-font-weight-medium, 500);
168
+ cursor: pointer;
169
+
170
+ &:hover {
171
+ background: color-mix(in srgb, rgb(var(--zn-primary)) 10%, transparent);
172
+ }
173
+
174
+ &:focus-visible {
175
+ outline: 2px solid rgb(var(--zn-primary));
176
+ outline-offset: -2px;
177
+ }
178
+
179
+ &.is-today {
180
+ box-shadow: inset 0 0 0 1px rgb(var(--zn-primary));
181
+ }
182
+
183
+ &.is-in {
184
+ border-radius: 0;
185
+ background: color-mix(in srgb, rgb(var(--zn-primary)) 12%, transparent);
186
+ }
187
+
188
+ &.is-start,
189
+ &.is-end {
190
+ background: rgb(var(--zn-primary));
191
+ color: rgb(var(--zn-color-on-primary, 255, 255, 255));
192
+ font-weight: var(--zn-font-weight-bold, 600);
193
+ }
194
+
195
+ &.is-start.is-end {
196
+ border-radius: var(--zn-border-radius-medium, 6px);
197
+ }
198
+
199
+ &.is-start:not(.is-end) {
200
+ border-radius: var(--zn-border-radius-medium, 6px) 0 0 var(--zn-border-radius-medium, 6px);
201
+ }
202
+
203
+ &.is-end:not(.is-start) {
204
+ border-radius: 0 var(--zn-border-radius-medium, 6px) var(--zn-border-radius-medium, 6px) 0;
205
+ }
206
+
207
+ &[aria-disabled="true"] {
208
+ background: none;
209
+ box-shadow: none;
210
+ color: color-mix(in srgb, rgb(var(--zn-text)) 30%, transparent);
211
+ cursor: default;
212
+ }
213
+ }
214
+
215
+ .date-range__inputs {
216
+ --zn-input-label-line-height: 16px;
217
+
218
+ display: grid;
219
+ grid-template-columns: 1fr auto 1fr;
220
+ align-items: end;
221
+ gap: var(--zn-spacing-x-small, 8px);
222
+ }
223
+
224
+ .date-range__dash {
225
+ padding-bottom: 8px;
226
+ color: color-mix(in srgb, rgb(var(--zn-text)) 55%, transparent);
227
+ }
228
+
229
+ .date-range__note {
230
+ margin: 0;
231
+ color: color-mix(in srgb, rgb(var(--zn-text)) 55%, transparent);
232
+ font-size: var(--zn-font-size-x-small);
233
+ line-height: 18px;
234
+ }
235
+
236
+ .date-range__foot {
237
+ display: flex;
238
+ align-items: center;
239
+ gap: var(--zn-spacing-x-small, 8px);
240
+ padding-top: 12px;
241
+ border-top: 1px solid rgb(var(--zn-border-color));
242
+ }
243
+
244
+ .date-range__summary {
245
+ flex: 1 1 auto;
246
+ color: color-mix(in srgb, rgb(var(--zn-text)) 65%, transparent);
247
+ font-size: var(--zn-font-size-x-small);
248
+ }
249
+
250
+ .date-range__pop.is-narrow {
251
+ flex-direction: column;
252
+ }
253
+
254
+ .date-range__pop.is-narrow .date-range__presets {
255
+ flex: none;
256
+ flex-flow: row wrap;
257
+ border-right: 0;
258
+ border-bottom: 1px solid rgb(var(--zn-border-color));
259
+ }
260
+
261
+ .date-range__pop.is-narrow .date-range__preset {
262
+ flex: none;
263
+ white-space: nowrap;
264
+ }
265
+
266
+ .date-range__pop.is-narrow .date-range__months {
267
+ grid-template-columns: 1fr;
268
+ }
269
+
270
+ .date-range__pop.is-narrow .date-range__month + .date-range__month {
271
+ display: none;
272
+ }
@@ -0,0 +1,247 @@
1
+ import '../../../dist/zn.min.js';
2
+ import {expect, fixture, html} from '@open-wc/testing';
3
+ import {setViewport} from '@web/test-runner-commands';
4
+ import type ZnButton from '../button';
5
+ import type ZnDateRange from './date-range.component';
6
+ import type ZnInput from '../input';
7
+
8
+ const click = (el: Element) =>
9
+ el.dispatchEvent(new MouseEvent('click', {bubbles: true, composed: true, cancelable: true}));
10
+
11
+ const pop = (el: ZnDateRange) => el.shadowRoot!.querySelector('.date-range__pop');
12
+ const day = (el: ZnDateRange, iso: string) => el.shadowRoot!.querySelector<HTMLElement>(`[data-date="${iso}"]`)!;
13
+ const preset = (el: ZnDateRange, label: string) =>
14
+ [...el.shadowRoot!.querySelectorAll<HTMLElement>('.date-range__preset')].find(b => b.textContent!.trim() === label)!;
15
+ const commit = async (el: ZnDateRange, end: 'from' | 'to', value: string) => {
16
+ const input = el.shadowRoot!.querySelector<ZnInput>(`.date-range__${end}`)!;
17
+ input.value = value;
18
+ input.dispatchEvent(new CustomEvent('zn-blur', {bubbles: true, composed: true}));
19
+ await el.updateComplete;
20
+ };
21
+ const inputValue = (el: ZnDateRange, end: 'from' | 'to') =>
22
+ el.shadowRoot!.querySelector<ZnInput>(`.date-range__${end}`)!.value as string;
23
+ const apply = (el: ZnDateRange) => el.shadowRoot!.querySelector<ZnButton>('.date-range__apply')!;
24
+
25
+ async function openRange(template = html`<zn-date-range from="2026-10-10" to="2026-10-12" max="2026-10-20"></zn-date-range>`) {
26
+ const el = await fixture<ZnDateRange>(template);
27
+ click(el.shadowRoot!.querySelector('.date-range__trigger')!);
28
+ await el.updateComplete;
29
+ return el;
30
+ }
31
+
32
+ describe('<zn-date-range>', () => {
33
+ it('opens a dialog with two month grids and eight quick ranges', async () => {
34
+ const el = await openRange();
35
+
36
+ expect(pop(el)).to.exist;
37
+ expect(pop(el)!.getAttribute('role')).to.equal('dialog');
38
+ expect(el.shadowRoot!.querySelectorAll('[role="grid"]')).to.have.length(2);
39
+ expect(el.shadowRoot!.querySelectorAll('.date-range__preset')).to.have.length(8);
40
+ });
41
+
42
+ it('greys out days after max and ignores clicks on them', async () => {
43
+ const el = await openRange();
44
+
45
+ expect(day(el, '2026-10-25').getAttribute('aria-disabled')).to.equal('true');
46
+ expect(day(el, '2026-10-20').getAttribute('aria-disabled')).to.equal('false');
47
+
48
+ click(day(el, '2026-10-25'));
49
+ await el.updateComplete;
50
+
51
+ expect(day(el, '2026-10-10').classList.contains('is-start')).to.be.true;
52
+ expect(day(el, '2026-10-12').classList.contains('is-end')).to.be.true;
53
+ expect(day(el, '2026-10-25').classList.contains('is-start')).to.be.false;
54
+ });
55
+
56
+ it('enables Apply only once both days are picked', async () => {
57
+ const el = await openRange();
58
+
59
+ click(day(el, '2026-10-14'));
60
+ await el.updateComplete;
61
+ expect(apply(el).disabled).to.be.true;
62
+
63
+ click(day(el, '2026-10-18'));
64
+ await el.updateComplete;
65
+ expect(apply(el).disabled).to.be.false;
66
+ expect(day(el, '2026-10-16').classList.contains('is-in')).to.be.true;
67
+ });
68
+
69
+ it('applies the picked range and fires zn-change once', async () => {
70
+ const el = await openRange();
71
+ const details: unknown[] = [];
72
+ el.addEventListener('zn-change', (e: Event) => details.push((e as CustomEvent).detail));
73
+
74
+ click(day(el, '2026-10-14'));
75
+ await el.updateComplete;
76
+ click(day(el, '2026-10-18'));
77
+ await el.updateComplete;
78
+ click(apply(el));
79
+ await el.updateComplete;
80
+
81
+ expect(el.from).to.equal('2026-10-14');
82
+ expect(el.to).to.equal('2026-10-18');
83
+ expect(el.getAttribute('from')).to.equal('2026-10-14');
84
+ expect(details).to.deep.equal([{from: '2026-10-14', to: '2026-10-18'}]);
85
+ expect(pop(el)).to.not.exist;
86
+ });
87
+
88
+ it('closes on Escape without changing the range or firing zn-change', async () => {
89
+ const el = await openRange();
90
+ let fired = 0;
91
+ el.addEventListener('zn-change', () => fired++);
92
+
93
+ click(day(el, '2026-10-14'));
94
+ click(day(el, '2026-10-18'));
95
+ await el.updateComplete;
96
+ document.dispatchEvent(new KeyboardEvent('keydown', {key: 'Escape', bubbles: true, composed: true}));
97
+ await el.updateComplete;
98
+
99
+ expect(pop(el)).to.not.exist;
100
+ expect(el.from).to.equal('2026-10-10');
101
+ expect(el.to).to.equal('2026-10-12');
102
+ expect(fired).to.equal(0);
103
+ });
104
+
105
+ it('stays open for clicks on its own arrows and inputs, and closes on an outside click', async () => {
106
+ const el = await openRange();
107
+
108
+ click(el.shadowRoot!.querySelector('.date-range__nav--next')!);
109
+ await el.updateComplete;
110
+ expect(pop(el)).to.exist;
111
+
112
+ click(el.shadowRoot!.querySelector('.date-range__from')!);
113
+ await el.updateComplete;
114
+ expect(pop(el)).to.exist;
115
+
116
+ click(document.body);
117
+ await el.updateComplete;
118
+ expect(pop(el)).to.not.exist;
119
+ });
120
+
121
+ it('submits name[from] and name[to] with its form', async () => {
122
+ const form = await fixture<HTMLFormElement>(html`
123
+ <form><zn-date-range name="period" from="2026-10-01" to="2026-10-20"></zn-date-range></form>`);
124
+ await (form.querySelector('zn-date-range')!).updateComplete;
125
+
126
+ const data = new FormData(form);
127
+ expect(data.get('period[from]')).to.equal('2026-10-01');
128
+ expect(data.get('period[to]')).to.equal('2026-10-20');
129
+ });
130
+ it('applies a quick range straight away and closes', async () => {
131
+ const el = await openRange(html`<zn-date-range from="2026-10-10" to="2026-10-12"
132
+ presets='[{"label":"Early Oct","from":"2026-10-01","to":"2026-10-05"}]'></zn-date-range>`);
133
+ const details: unknown[] = [];
134
+ el.addEventListener('zn-change', (e: Event) => details.push((e as CustomEvent).detail));
135
+
136
+ click(preset(el, 'Early Oct'));
137
+ await el.updateComplete;
138
+
139
+ expect(el.from).to.equal('2026-10-01');
140
+ expect(el.to).to.equal('2026-10-05');
141
+ expect(details).to.deep.equal([{from: '2026-10-01', to: '2026-10-05'}]);
142
+ expect(pop(el)).to.not.exist;
143
+ });
144
+
145
+ it('clamps a quick range to max and disables one wholly past it', async () => {
146
+ const el = await openRange(html`<zn-date-range from="2026-10-10" to="2026-10-12" max="2026-10-20"
147
+ presets='[{"label":"Wide","from":"2026-10-15","to":"2026-10-30"},{"label":"Later","from":"2026-11-01","to":"2026-11-05"}]'></zn-date-range>`);
148
+ let fired = 0;
149
+ el.addEventListener('zn-change', () => fired++);
150
+
151
+ expect(preset(el, 'Later').getAttribute('aria-disabled')).to.equal('true');
152
+ click(preset(el, 'Later'));
153
+ await el.updateComplete;
154
+ expect(fired).to.equal(0);
155
+ expect(pop(el)).to.exist;
156
+
157
+ click(preset(el, 'Wide'));
158
+ await el.updateComplete;
159
+ expect(el.from).to.equal('2026-10-15');
160
+ expect(el.to).to.equal('2026-10-20');
161
+ });
162
+
163
+ it('takes typed dates on commit, restarting when To is before From and ignoring partial years', async () => {
164
+ const el = await openRange();
165
+
166
+ await commit(el, 'to', '0202-10-05');
167
+ expect(inputValue(el, 'from')).to.equal('2026-10-10');
168
+ expect(apply(el).disabled).to.be.false;
169
+
170
+ await commit(el, 'to', '2026-10-05');
171
+ expect(inputValue(el, 'from')).to.equal('2026-10-05');
172
+ expect(inputValue(el, 'to')).to.equal('');
173
+ expect(apply(el).disabled).to.be.true;
174
+
175
+ await commit(el, 'to', '2026-10-08');
176
+ expect(apply(el).disabled).to.be.false;
177
+ expect(day(el, '2026-10-08').classList.contains('is-end')).to.be.true;
178
+
179
+ await commit(el, 'from', '2026-08-03');
180
+ expect(el.shadowRoot!.querySelector('.date-range__head')!.textContent).to.equal('August 2026');
181
+ expect(day(el, '2026-08-03').classList.contains('is-start')).to.be.true;
182
+ });
183
+
184
+ it('keeps Escape from reaching an enclosing element, and moves focus in and back out', async () => {
185
+ const wrap = await fixture<HTMLDivElement>(html`
186
+ <div><zn-date-range from="2026-10-10" to="2026-10-12"></zn-date-range></div>`);
187
+ const el = wrap.querySelector('zn-date-range')!;
188
+ let escapes = 0;
189
+ wrap.addEventListener('keydown', () => escapes++);
190
+
191
+ click(el.shadowRoot!.querySelector('.date-range__trigger')!);
192
+ await el.updateComplete;
193
+ await el.updateComplete;
194
+ expect(el.shadowRoot!.activeElement).to.equal(day(el, '2026-10-10'));
195
+
196
+ day(el, '2026-10-10').dispatchEvent(new KeyboardEvent('keydown', {key: 'Escape', bubbles: true, composed: true}));
197
+ await el.updateComplete;
198
+
199
+ expect(pop(el)).to.not.exist;
200
+ expect(escapes).to.equal(0);
201
+ expect(el.shadowRoot!.activeElement).to.equal(el.shadowRoot!.querySelector('.date-range__trigger'));
202
+ });
203
+
204
+ it('shows one month on a narrow screen', async () => {
205
+ await setViewport({width: 375, height: 800});
206
+ try {
207
+ const el = await openRange();
208
+ const months = el.shadowRoot!.querySelectorAll('.date-range__month');
209
+ expect([window.innerWidth, matchMedia('(max-width: 560px)').matches]).to.deep.equal([375, true]);
210
+ expect(getComputedStyle(months[1]).display).to.equal('none');
211
+ expect(pop(el)!.getBoundingClientRect().right).to.be.at.most(375);
212
+ const popRect = pop(el)!.getBoundingClientRect();
213
+ el.shadowRoot!.querySelectorAll('.date-range__preset').forEach(p => {
214
+ expect(p.getBoundingClientRect().right).to.be.at.most(popRect.right);
215
+ });
216
+ } finally {
217
+ await setViewport({width: 800, height: 600});
218
+ }
219
+ });
220
+
221
+ it('opens above the trigger when there is no room below', async () => {
222
+ await setViewport({width: 1000, height: 1000});
223
+ try {
224
+ const el = await fixture<ZnDateRange>(html`
225
+ <zn-date-range style="display: block; margin-top: 900px" from="2026-10-10" to="2026-10-12"></zn-date-range>`);
226
+ click(el.shadowRoot!.querySelector('.date-range__trigger')!);
227
+ await el.updateComplete;
228
+ await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
229
+ await el.updateComplete;
230
+
231
+ const rect = pop(el)!.getBoundingClientRect();
232
+ const trigger = el.shadowRoot!.querySelector('.date-range__trigger')!.getBoundingClientRect();
233
+ expect(rect.bottom).to.be.at.most(trigger.top);
234
+ expect(rect.top).to.be.at.least(8);
235
+ } finally {
236
+ await setViewport({width: 800, height: 600});
237
+ }
238
+ });
239
+
240
+ it('shows a note under the calendar only when given one', async () => {
241
+ const plain = await openRange();
242
+ expect(plain.shadowRoot!.querySelector('.date-range__note')).to.not.exist;
243
+
244
+ const noted = await openRange(html`<zn-date-range from="2026-10-10" to="2026-10-12" note="Compared day by day."></zn-date-range>`);
245
+ expect(noted.shadowRoot!.querySelector('.date-range__note')!.textContent).to.equal('Compared day by day.');
246
+ });
247
+ });
@@ -0,0 +1,12 @@
1
+ import ZnDateRange from './date-range.component';
2
+
3
+ export * from './date-range.component';
4
+ export default ZnDateRange;
5
+
6
+ ZnDateRange.define('zn-date-range');
7
+
8
+ declare global {
9
+ interface HTMLElementTagNameMap {
10
+ 'zn-date-range': ZnDateRange;
11
+ }
12
+ }
@@ -71,6 +71,7 @@ export default class ZnDatepicker extends ZincElement implements ZincFormControl
71
71
  @query('.input__control') input: HTMLInputElement;
72
72
 
73
73
  @state() private hasFocus = false;
74
+ private escapeSwallowed = false;
74
75
 
75
76
  @property() title = "" // make reactive pass through
76
77
 
@@ -127,6 +128,9 @@ export default class ZnDatepicker extends ZincElement implements ZincFormControl
127
128
  /** Makes the input a required field. */
128
129
  @property({type: Boolean, reflect: true}) required = false;
129
130
 
131
+ /** Short text shown next to the label, styled like the help text, e.g. `optional`. */
132
+ @property({attribute: 'label-note'}) labelNote = '';
133
+
130
134
  /** Adds a clear button to the calendar for removing a selected date. **/
131
135
  @property({type: Boolean}) clearable = false;
132
136
 
@@ -300,7 +304,6 @@ export default class ZnDatepicker extends ZincElement implements ZincFormControl
300
304
  container: this.container ?? this.getCalendarContainer(),
301
305
  altField: timestampField,
302
306
  onSelect: ({date}) => {
303
- // Blur the input after selection to prevent invisible keyboard navigation
304
307
  if (this.timePicker || this.onlyTimepicker){
305
308
  // An inline calendar never hides, so there's no later event to report the time on
306
309
  if (this.inline) {
@@ -309,12 +312,11 @@ export default class ZnDatepicker extends ZincElement implements ZincFormControl
309
312
  return
310
313
  }
311
314
  if (date && !this.range) {
312
- // For single date selection, blur immediately
313
- this.input.blur();
315
+ this.hideCalendarKeepingFocus();
314
316
  this.handleChange();
315
317
  } else if (date && this.range && Array.isArray(date) && date.length === 2) {
316
- // For range selection, blur only after both dates are selected
317
- this.input.blur();
318
+ // For range selection, hide only after both dates are selected
319
+ this.hideCalendarKeepingFocus();
318
320
  this.handleChange();
319
321
  }
320
322
  },
@@ -440,14 +442,30 @@ export default class ZnDatepicker extends ZincElement implements ZincFormControl
440
442
 
441
443
  private handleKeyDown(event: KeyboardEvent) {
442
444
  if (this._instance && this._instance.visible) {
445
+ // AirDatepicker's own Escape handler would hide through its blurring hide()
446
+ if (event.key === 'Escape' && !this.inline) {
447
+ event.stopImmediatePropagation();
448
+ this.hideCalendarKeepingFocus();
449
+ }
450
+ return;
451
+ }
452
+
453
+ // The calendar only opens on focus, so a focused field needs another way to reopen it
454
+ if (event.key === 'ArrowDown' && this._instance && !this.inline) {
455
+ event.preventDefault();
456
+ this._instance.show();
443
457
  return;
444
458
  }
445
459
 
460
+ // The first Escape stays in the field so it doesn't also dismiss a containing popup; a repeat bubbles
446
461
  if (event.key === 'Escape') {
447
- this.input.blur();
448
- event.stopPropagation();
462
+ if (!this.escapeSwallowed) {
463
+ this.escapeSwallowed = true;
464
+ event.stopPropagation();
465
+ }
449
466
  return;
450
467
  }
468
+ this.escapeSwallowed = false;
451
469
 
452
470
  // Allow navigation and control keys
453
471
  const allowedKeys = [
@@ -489,6 +507,23 @@ export default class ZnDatepicker extends ZincElement implements ZincFormControl
489
507
  }
490
508
  }
491
509
 
510
+ // AirDatepicker's hide() blurs its input, and refocusing would reopen the calendar
511
+ // shortcut: toggles AirDatepicker's private elIsInput (v3.6), recheck on upgrade
512
+ private hideCalendarKeepingFocus() {
513
+ if (this.inline) return;
514
+ const instance = this._instance as unknown as {elIsInput: boolean};
515
+ const elIsInput = instance.elIsInput;
516
+ instance.elIsInput = false;
517
+ this._instance.hide();
518
+ instance.elIsInput = elIsInput;
519
+ }
520
+
521
+ private handleInputClick() {
522
+ if (this._instance && !this._instance.visible) {
523
+ this._instance.show();
524
+ }
525
+ }
526
+
492
527
  private handleBlur() {
493
528
  if (this._instance && this._instance.visible) {
494
529
  return;
@@ -698,6 +733,7 @@ export default class ZnDatepicker extends ZincElement implements ZincFormControl
698
733
  <zn-icon src="info"></zn-icon>
699
734
  </zn-tooltip>`
700
735
  : ''}
736
+ ${this.labelNote ? html`<span class="form-control__label-note">${this.labelNote}</span>` : ''}
701
737
  </label>
702
738
 
703
739
  ${hasContextNote
@@ -745,6 +781,7 @@ export default class ZnDatepicker extends ZincElement implements ZincFormControl
745
781
  @input=${this.handleInput}
746
782
  @invalid=${this.handleInvalid}
747
783
  @keydown=${this.handleKeyDown}
784
+ @click=${this.handleInputClick}
748
785
  @paste=${this.handlePaste}
749
786
  @blur=${this.handleBlur}>
750
787
  <input id="date-picker-timestamp" type="hidden">
@@ -44,6 +44,15 @@
44
44
  color: var(--zn-input-color-focus);
45
45
  }
46
46
 
47
+ /* Invalid, once the user has interacted */
48
+ :host([data-user-invalid]) .input--standard:not(.input--disabled) {
49
+ border-color: var(--zn-input-border-color-invalid);
50
+ }
51
+
52
+ :host([data-user-invalid]) .input--standard.input--focused:not(.input--disabled) {
53
+ box-shadow: 0 0 0 var(--zn-focus-ring-width) var(--zn-input-focus-ring-color-invalid);
54
+ }
55
+
47
56
  .input--standard.input--disabled {
48
57
  background-color: var(--zn-input-background-color-disabled);
49
58
  border-color: var(--zn-input-border-color-disabled);
@@ -37,7 +37,7 @@ describe('<zn-datepicker>', () => {
37
37
  });
38
38
 
39
39
  describe('Escape key', () => {
40
- it('blurs the inner input when focused and the calendar is closed', async () => {
40
+ it('keeps focus on the inner input when the calendar is closed', async () => {
41
41
  const el = await fixture<ZnDatepicker>(html`<zn-datepicker></zn-datepicker>`);
42
42
  await el.updateComplete;
43
43
 
@@ -62,7 +62,7 @@ describe('<zn-datepicker>', () => {
62
62
  }));
63
63
  await el.updateComplete;
64
64
 
65
- expect(el.shadowRoot!.activeElement, 'inner input should be blurred').to.not.equal(el.input);
65
+ expect(el.shadowRoot!.activeElement, 'inner input should keep focus').to.equal(el.input);
66
66
  });
67
67
  });
68
68
  });