@xleddyl/443-components-vue 0.4.0 → 0.6.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 (323) hide show
  1. package/README.md +25 -6
  2. package/dist/chart/BarChart.vue +11 -4
  3. package/dist/chart/BarChart.vue.d.ts +2 -0
  4. package/dist/chart/ChartLegend.vue +43 -4
  5. package/dist/chart/ChartLegend.vue.d.ts +4 -2
  6. package/dist/chart/ChartPanel.vue +10 -10
  7. package/dist/chart/ChartPanel.vue.d.ts +11 -8
  8. package/dist/chart/ChartSummary.vue +8 -3
  9. package/dist/chart/ChartSummary.vue.d.ts +3 -1
  10. package/dist/chart/DoughnutChart.vue +11 -4
  11. package/dist/chart/LineChart.vue +8 -2
  12. package/dist/chart/LineChart.vue.d.ts +2 -0
  13. package/dist/chart/chart.css +19 -0
  14. package/dist/chart/patterns.d.ts +6 -0
  15. package/dist/chart/patterns.mjs +79 -0
  16. package/dist/chart/types.d.ts +5 -0
  17. package/dist/chart/useChartColors.mjs +1 -1
  18. package/dist/components/core/Button.vue +11 -1
  19. package/dist/components/core/Button.vue.d.ts +1 -1
  20. package/dist/components/core/FieldMessage.vue +6 -0
  21. package/dist/components/core/FieldMessage.vue.d.ts +1 -1
  22. package/dist/components/core/Form.vue +18 -1
  23. package/dist/components/core/FormAlerts.vue +41 -3
  24. package/dist/components/core/FormField.vue +43 -24
  25. package/dist/components/core/Input.vue.d.ts +6 -6
  26. package/dist/components/core/Root.vue +6 -1
  27. package/dist/components/core/Root.vue.d.ts +2 -2
  28. package/dist/components/core/Spinner.vue +6 -11
  29. package/dist/components/core/Spinner.vue.d.ts +2 -2
  30. package/dist/components/core/Textarea.vue.d.ts +2 -2
  31. package/dist/components/core/VisuallyHidden.vue.d.ts +2 -2
  32. package/dist/components/data/Avatar.vue +8 -5
  33. package/dist/components/data/AvatarGroup.vue +6 -3
  34. package/dist/components/data/Badge.vue.d.ts +1 -1
  35. package/dist/components/data/FilterPills.vue +18 -2
  36. package/dist/components/data/Pagination.vue +24 -18
  37. package/dist/components/data/Pagination.vue.d.ts +2 -2
  38. package/dist/components/data/SelectionBar.vue +32 -3
  39. package/dist/components/data/Stat.vue +7 -1
  40. package/dist/components/data/Stat.vue.d.ts +2 -2
  41. package/dist/components/data/Table.vue +62 -14
  42. package/dist/components/data/Tag.vue +24 -4
  43. package/dist/components/data/Tag.vue.d.ts +3 -3
  44. package/dist/components/data/Timeline.vue +12 -1
  45. package/dist/components/data/Tree.vue +137 -36
  46. package/dist/components/data/Tree.vue.d.ts +13 -14
  47. package/dist/components/data/types.d.ts +2 -1
  48. package/dist/components/feedback/Alert.vue +18 -9
  49. package/dist/components/feedback/Alert.vue.d.ts +2 -2
  50. package/dist/components/feedback/LoadingOverlay.vue +35 -3
  51. package/dist/components/feedback/Meter.vue +28 -3
  52. package/dist/components/feedback/Progress.vue +2 -7
  53. package/dist/components/feedback/Progress.vue.d.ts +1 -1
  54. package/dist/components/feedback/ProgressRing.vue +2 -6
  55. package/dist/components/feedback/ProgressRing.vue.d.ts +1 -1
  56. package/dist/components/forms/Calendar.vue +49 -26
  57. package/dist/components/forms/Calendar.vue.d.ts +9 -5
  58. package/dist/components/forms/CheckboxGroup.vue +14 -4
  59. package/dist/components/forms/CheckboxGroup.vue.d.ts +1 -0
  60. package/dist/components/forms/ColorInput.vue +68 -15
  61. package/dist/components/forms/ColorInput.vue.d.ts +16 -13
  62. package/dist/components/forms/Combobox.vue +18 -4
  63. package/dist/components/forms/DateInput.vue.d.ts +6 -6
  64. package/dist/components/forms/DatePicker.vue +23 -7
  65. package/dist/components/forms/DatePicker.vue.d.ts +4 -2
  66. package/dist/components/forms/FileInput.vue +31 -13
  67. package/dist/components/forms/FileInput.vue.d.ts +2 -2
  68. package/dist/components/forms/InputAddon.vue +18 -2
  69. package/dist/components/forms/InputGroup.vue +27 -0
  70. package/dist/components/forms/InputGroup.vue.d.ts +2 -0
  71. package/dist/components/forms/NumberInput.vue +6 -7
  72. package/dist/components/forms/NumberInput.vue.d.ts +6 -6
  73. package/dist/components/forms/PasswordInput.vue +12 -6
  74. package/dist/components/forms/PasswordInput.vue.d.ts +6 -7
  75. package/dist/components/forms/PinInput.vue +11 -1
  76. package/dist/components/forms/PinInput.vue.d.ts +8 -6
  77. package/dist/components/forms/RadioGroup.vue +15 -5
  78. package/dist/components/forms/RadioGroup.vue.d.ts +1 -0
  79. package/dist/components/forms/SegmentedControl.vue +15 -10
  80. package/dist/components/forms/SegmentedControl.vue.d.ts +1 -0
  81. package/dist/components/forms/Slider.vue +9 -0
  82. package/dist/components/forms/Slider.vue.d.ts +8 -6
  83. package/dist/components/forms/Switch.vue +7 -0
  84. package/dist/components/forms/Switch.vue.d.ts +2 -2
  85. package/dist/components/forms/TimeInput.vue.d.ts +6 -6
  86. package/dist/components/forms/types.d.ts +4 -0
  87. package/dist/components/layout/Accordion.vue.d.ts +2 -2
  88. package/dist/components/layout/AppShell.vue +40 -18
  89. package/dist/components/layout/AppShell.vue.d.ts +11 -9
  90. package/dist/components/layout/Card.vue +50 -16
  91. package/dist/components/layout/Card.vue.d.ts +13 -11
  92. package/dist/components/layout/CopyButton.vue +13 -5
  93. package/dist/components/layout/CopyButton.vue.d.ts +3 -3
  94. package/dist/components/layout/NavLink.vue +10 -1
  95. package/dist/components/layout/NavLink.vue.d.ts +2 -2
  96. package/dist/components/layout/PageHeader.vue +2 -2
  97. package/dist/components/layout/Separator.vue +6 -3
  98. package/dist/components/layout/Sidebar.vue +14 -7
  99. package/dist/components/layout/Sidebar.vue.d.ts +0 -2
  100. package/dist/components/layout/SidebarNav.vue +16 -44
  101. package/dist/components/layout/Stack.vue.d.ts +2 -2
  102. package/dist/components/layout/Tabs.vue +10 -18
  103. package/dist/components/layout/Tabs.vue.d.ts +2 -2
  104. package/dist/components/layout/Toolbar.vue +10 -4
  105. package/dist/components/layout/Toolbar.vue.d.ts +1 -0
  106. package/dist/components/layout/types.d.ts +1 -0
  107. package/dist/components/overlays/CommandPalette.vue +8 -4
  108. package/dist/components/overlays/CommandPalette.vue.d.ts +3 -8
  109. package/dist/components/overlays/ConfirmDialog.vue +7 -5
  110. package/dist/components/overlays/ConfirmDialog.vue.d.ts +12 -8
  111. package/dist/components/overlays/ConfirmHost.vue +22 -19
  112. package/dist/components/overlays/ConfirmHost.vue.d.ts +8 -6
  113. package/dist/components/overlays/ContextMenu.vue +9 -11
  114. package/dist/components/overlays/ContextMenu.vue.d.ts +2 -2
  115. package/dist/components/overlays/Drawer.vue +2 -0
  116. package/dist/components/overlays/Drawer.vue.d.ts +4 -2
  117. package/dist/components/overlays/DropdownMenu.vue +13 -21
  118. package/dist/components/overlays/DropdownMenu.vue.d.ts +2 -2
  119. package/dist/components/overlays/HoverCard.vue +8 -2
  120. package/dist/components/overlays/HoverCard.vue.d.ts +2 -2
  121. package/dist/components/overlays/Modal.vue +2 -0
  122. package/dist/components/overlays/Modal.vue.d.ts +4 -2
  123. package/dist/components/overlays/Popover.vue +19 -4
  124. package/dist/components/overlays/Popover.vue.d.ts +2 -2
  125. package/dist/components/overlays/Toaster.vue +71 -8
  126. package/dist/components/overlays/Toaster.vue.d.ts +3 -0
  127. package/dist/components/overlays/Tooltip.vue +39 -14
  128. package/dist/components/overlays/types.d.ts +7 -0
  129. package/dist/components/templates/AccountMenu.vue.d.ts +2 -2
  130. package/dist/components/templates/AppearanceSettings.vue +83 -77
  131. package/dist/components/templates/AppearanceSettings.vue.d.ts +2 -2
  132. package/dist/components/templates/AuthLayout.vue +2 -2
  133. package/dist/components/templates/Brand.vue.d.ts +2 -2
  134. package/dist/components/templates/DashboardLayout.vue +3 -7
  135. package/dist/components/templates/DashboardLayout.vue.d.ts +8 -18
  136. package/dist/components/templates/DetailPage.vue +3 -1
  137. package/dist/components/templates/DetailPage.vue.d.ts +2 -2
  138. package/dist/components/templates/ErrorPage.vue +11 -5
  139. package/dist/components/templates/ErrorPage.vue.d.ts +10 -6
  140. package/dist/components/templates/FormPage.vue +16 -3
  141. package/dist/components/templates/ListPage.vue +19 -6
  142. package/dist/components/templates/ListPage.vue.d.ts +1 -0
  143. package/dist/components/templates/NotFoundPage.vue +2 -0
  144. package/dist/components/templates/NotFoundPage.vue.d.ts +4 -0
  145. package/dist/components/templates/SaveBar.vue +18 -3
  146. package/dist/components/templates/SaveBar.vue.d.ts +5 -5
  147. package/dist/components/templates/SettingsGroup.vue +1 -1
  148. package/dist/components/templates/SettingsGroup.vue.d.ts +3 -1
  149. package/dist/components/templates/SettingsModal.vue +73 -46
  150. package/dist/components/templates/SettingsModal.vue.d.ts +9 -11
  151. package/dist/components/templates/SettingsPage.vue.d.ts +2 -2
  152. package/dist/components/templates/SettingsSection.vue.d.ts +4 -2
  153. package/dist/components/templates/types.d.ts +4 -0
  154. package/dist/composables/useAlert.mjs +5 -10
  155. package/dist/composables/useConfirm.mjs +5 -5
  156. package/dist/composables/useField.d.ts +4 -0
  157. package/dist/composables/useField.mjs +16 -5
  158. package/dist/composables/useFileDrop.mjs +2 -1
  159. package/dist/composables/useOverlay.mjs +2 -1
  160. package/dist/composables/usePreset.mjs +4 -10
  161. package/dist/composables/usePrompt.mjs +6 -13
  162. package/dist/composables/useScrollLock.mjs +4 -3
  163. package/dist/composables/useTheme.mjs +3 -11
  164. package/dist/composables/useToast.mjs +2 -1
  165. package/dist/editor/RichTextEditor.vue +31 -6
  166. package/dist/editor/RichTextEditor.vue.d.ts +2 -2
  167. package/dist/editor/rich-text-editor.css +9 -4
  168. package/dist/grid/DataGrid.vue +72 -12
  169. package/dist/grid/GridToolbar.vue +1 -1
  170. package/dist/grid/GridToolbar.vue.d.ts +2 -2
  171. package/dist/grid/cells/BadgeCell.vue +3 -1
  172. package/dist/grid/cells/BadgeCell.vue.d.ts +17 -3
  173. package/dist/grid/cells/DateCell.vue +3 -1
  174. package/dist/grid/cells/DateCell.vue.d.ts +17 -3
  175. package/dist/grid/cells/EmptyCell.vue +15 -0
  176. package/dist/grid/cells/EmptyCell.vue.d.ts +392 -0
  177. package/dist/grid/cells/LinkCell.vue +9 -2
  178. package/dist/grid/cells/LinkCell.vue.d.ts +17 -3
  179. package/dist/grid/data-grid.css +5 -5
  180. package/dist/grid/types.d.ts +2 -0
  181. package/dist/internal/core/ButtonContent.vue +3 -0
  182. package/dist/internal/core/LiveRegion.vue +26 -0
  183. package/dist/internal/core/LiveRegion.vue.d.ts +393 -0
  184. package/dist/internal/core/copy.d.ts +6 -0
  185. package/dist/internal/core/copy.mjs +14 -0
  186. package/dist/internal/core/focus.d.ts +6 -0
  187. package/dist/internal/core/focus.mjs +46 -0
  188. package/dist/internal/core/intl.d.ts +4 -2
  189. package/dist/internal/core/intl.mjs +23 -22
  190. package/dist/internal/core/live.d.ts +6 -0
  191. package/dist/internal/core/live.mjs +22 -0
  192. package/dist/internal/core/preset.d.ts +0 -1
  193. package/dist/internal/core/preset.mjs +4 -14
  194. package/dist/internal/core/resize.d.ts +1 -0
  195. package/dist/internal/core/resize.mjs +11 -0
  196. package/dist/internal/core/roving.d.ts +7 -0
  197. package/dist/internal/core/roving.mjs +19 -1
  198. package/dist/internal/core/scroll-edges.d.ts +5 -0
  199. package/dist/internal/core/scroll-edges.mjs +63 -0
  200. package/dist/internal/core/storage.d.ts +2 -0
  201. package/dist/internal/core/storage.mjs +8 -0
  202. package/dist/internal/core/theme.d.ts +0 -1
  203. package/dist/internal/core/theme.mjs +4 -13
  204. package/dist/internal/core/tone.d.ts +1 -0
  205. package/dist/internal/core/tone.mjs +4 -0
  206. package/dist/internal/core/transition.d.ts +1 -0
  207. package/dist/internal/core/transition.mjs +20 -0
  208. package/dist/internal/data/rows.mjs +6 -5
  209. package/dist/internal/data/tree.d.ts +2 -3
  210. package/dist/internal/feedback/LoadingHost.vue +16 -6
  211. package/dist/internal/feedback/loading.mjs +2 -1
  212. package/dist/internal/feedback/progress.d.ts +12 -0
  213. package/dist/internal/feedback/progress.mjs +13 -0
  214. package/dist/internal/floating/layer.d.ts +1 -1
  215. package/dist/internal/floating/layer.mjs +17 -10
  216. package/dist/internal/forms/keys.d.ts +4 -0
  217. package/dist/internal/forms/keys.mjs +1 -0
  218. package/dist/internal/forms/name.d.ts +2 -0
  219. package/dist/internal/forms/name.mjs +12 -0
  220. package/dist/internal/forms/standard-schema.d.ts +0 -2
  221. package/dist/internal/forms/standard-schema.mjs +0 -18
  222. package/dist/internal/layout/NavGroup.vue +104 -0
  223. package/dist/internal/layout/NavGroup.vue.d.ts +412 -0
  224. package/dist/internal/layout/route.mjs +1 -1
  225. package/dist/internal/overlays/DialogFrame.vue +53 -6
  226. package/dist/internal/overlays/DialogFrame.vue.d.ts +5 -2
  227. package/dist/internal/overlays/MenuList.vue +4 -11
  228. package/dist/internal/overlays/MenuList.vue.d.ts +6 -6
  229. package/dist/internal/overlays/OverlayView.vue +2 -0
  230. package/dist/internal/overlays/OverlayView.vue.d.ts +8 -6
  231. package/dist/internal/overlays/confirm.d.ts +6 -1
  232. package/dist/internal/overlays/confirm.mjs +5 -2
  233. package/dist/internal/overlays/dialog.d.ts +1 -0
  234. package/dist/internal/overlays/dialog.mjs +9 -9
  235. package/dist/internal/overlays/hover.d.ts +1 -0
  236. package/dist/internal/overlays/hover.mjs +20 -3
  237. package/dist/internal/overlays/menu.d.ts +9 -0
  238. package/dist/internal/overlays/menu.mjs +15 -0
  239. package/dist/internal/overlays/stack.d.ts +1 -0
  240. package/dist/locale/en.mjs +72 -19
  241. package/dist/locale/index.d.ts +1 -1
  242. package/dist/locale/index.mjs +5 -11
  243. package/dist/locale/it.mjs +72 -19
  244. package/dist/locale/types.d.ts +55 -2
  245. package/dist/nuxt/module.mjs +1 -1
  246. package/dist/nuxt/runtime/RouteAnnouncer.vue +21 -0
  247. package/dist/nuxt/runtime/RouteAnnouncer.vue.d.ts +3 -0
  248. package/dist/nuxt/runtime/layout-head.d.ts +2 -0
  249. package/dist/nuxt/runtime/layout-head.mjs +13 -0
  250. package/dist/nuxt/runtime/layouts/auth.vue +4 -0
  251. package/dist/nuxt/runtime/layouts/auth.vue.d.ts +2 -2
  252. package/dist/nuxt/runtime/layouts/dashboard.vue +4 -0
  253. package/dist/nuxt/runtime/layouts/dashboard.vue.d.ts +2 -2
  254. package/dist/nuxt/runtime/locale.mjs +8 -1
  255. package/dist/nuxt/runtime/theme.mjs +17 -12
  256. package/dist/styles/base.css +0 -13
  257. package/dist/styles/core/button.css +8 -8
  258. package/dist/styles/core/field-message.css +2 -2
  259. package/dist/styles/core/form-field.css +13 -4
  260. package/dist/styles/core/input.css +4 -7
  261. package/dist/styles/core/kbd.css +0 -1
  262. package/dist/styles/data/badge.css +8 -4
  263. package/dist/styles/data/description-list.css +7 -5
  264. package/dist/styles/data/filter-pills.css +5 -8
  265. package/dist/styles/data/pagination.css +2 -2
  266. package/dist/styles/data/stat.css +3 -3
  267. package/dist/styles/data/table.css +3 -2
  268. package/dist/styles/data/tag.css +1 -2
  269. package/dist/styles/data/timeline.css +2 -1
  270. package/dist/styles/data/tree.css +31 -10
  271. package/dist/styles/feedback/alert.css +1 -3
  272. package/dist/styles/feedback/empty-state.css +6 -4
  273. package/dist/styles/feedback/loading-host.css +4 -0
  274. package/dist/styles/feedback/loading-overlay.css +4 -0
  275. package/dist/styles/forced-colors.css +215 -0
  276. package/dist/styles/forms/calendar.css +2 -2
  277. package/dist/styles/forms/choice.css +2 -2
  278. package/dist/styles/forms/color-input.css +1 -1
  279. package/dist/styles/forms/combobox.css +2 -24
  280. package/dist/styles/forms/date-input.css +2 -2
  281. package/dist/styles/forms/date-picker.css +3 -7
  282. package/dist/styles/forms/file-input.css +4 -3
  283. package/dist/styles/forms/input-group.css +6 -0
  284. package/dist/styles/forms/number-input.css +2 -2
  285. package/dist/styles/forms/password-input.css +5 -2
  286. package/dist/styles/forms/pin-input.css +4 -2
  287. package/dist/styles/forms/segmented.css +2 -2
  288. package/dist/styles/forms/select.css +6 -6
  289. package/dist/styles/forms/slider.css +5 -4
  290. package/dist/styles/forms/switch.css +5 -1
  291. package/dist/styles/index.css +1 -0
  292. package/dist/styles/layout/accordion.css +1 -1
  293. package/dist/styles/layout/app-shell.css +15 -3
  294. package/dist/styles/layout/card.css +12 -2
  295. package/dist/styles/layout/copy-button.css +1 -1
  296. package/dist/styles/layout/nav-link.css +1 -33
  297. package/dist/styles/layout/page-header.css +2 -1
  298. package/dist/styles/layout/separator.css +1 -1
  299. package/dist/styles/layout/sidebar-nav.css +15 -4
  300. package/dist/styles/layout/sidebar.css +20 -6
  301. package/dist/styles/layout/tabs.css +9 -4
  302. package/dist/styles/layout/theme-switch.css +0 -6
  303. package/dist/styles/overlays/command.css +9 -15
  304. package/dist/styles/overlays/confirm.css +1 -1
  305. package/dist/styles/overlays/drawer.css +23 -3
  306. package/dist/styles/overlays/menu.css +2 -17
  307. package/dist/styles/overlays/modal.css +33 -9
  308. package/dist/styles/overlays/toast.css +1 -2
  309. package/dist/styles/presets.css +4 -4
  310. package/dist/styles/shared.css +96 -3
  311. package/dist/styles/templates/account-menu.css +3 -3
  312. package/dist/styles/templates/auth-layout.css +5 -1
  313. package/dist/styles/templates/brand.css +3 -3
  314. package/dist/styles/templates/empty-page.css +5 -1
  315. package/dist/styles/templates/error-page.css +4 -0
  316. package/dist/styles/templates/form-page.css +10 -0
  317. package/dist/styles/templates/save-bar.css +10 -0
  318. package/dist/styles/templates/settings-modal.css +46 -45
  319. package/dist/styles/templates/settings-page.css +3 -3
  320. package/dist/styles/tokens.css +12 -2
  321. package/package.json +1 -1
  322. package/dist/internal/overlays/focus.d.ts +0 -1
  323. package/dist/internal/overlays/focus.mjs +0 -24
package/README.md CHANGED
@@ -108,6 +108,8 @@ Tokens live on `.k-root`, never on `:root`. Override them on a root element: Roo
108
108
 
109
109
  Input tokens, declared only on the outermost root so nested roots inherit them: `--k-accent`, `--k-accent-contrast`, `--k-font-sans`, `--k-font-mono`, `--k-font-size`, `--k-font-features`, `--k-line-height`, `--k-radius-scale`. Every other token (surfaces, text, lines, accent shades, status colors, radii, shadows, motion) is derived per root, so a nested root with its own `theme` or `accent` re-themes a region. The full list is in [CONVENTIONS.md](./CONVENTIONS.md#tokens).
110
110
 
111
+ Contrast is part of the tokens: `--k-text-muted` is the lightest text color (4.5:1), `--k-text-dimmed` is for icons and decoration (3:1), `--k-ring` is an opaque focus color (3:1 on every surface), and `--k-danger-text`, `--k-success-text`, `--k-warning-text`, `--k-info-text` color status text (4.5:1, also on soft tints). Every preset sets them for light and dark; keep the same ratios when you override them.
112
+
111
113
  Themes: `data-theme="light"` or `"dark"` forces the scheme, `system` (or no attribute) follows the OS. `useTheme()` returns `preference`, `set()` and `cycle()`; `KThemeSwitch` is a ready control.
112
114
 
113
115
  Titles (headings, `k-title`, `k-display`, PageHeader, Card, Modal and Drawer titles, Brand, Stat values, auth and error titles) use `--k-font-display`, which falls back to `--k-font-sans`. Set it (or the `fontDisplay` prop of `Root`) for a separate display face.
@@ -209,6 +211,7 @@ k: { locale: 'it', messages: { it: { common: { cancel: 'Indietro' } } } }
209
211
  - With `@nuxtjs/i18n` the module follows the current i18n locale (a `de-CH` locale uses `de` overrides, then English).
210
212
  - A new language is a full `messages[code]` object, or `defineLocale({ code: 'de', messages: {...} })` passed to `createLocale()` / `provideLocale()` in plain Vue.
211
213
  - `useLocale()` returns `{ locale, t }` for your own components (`t('common.cancel')`).
214
+ - The outermost `Root` sets `lang` and `dir` from the provided locale, and the Nuxt module sets them on `<html>` unless `@nuxtjs/i18n` manages the document language.
212
215
  - The `*Label` and `*Text` props of each component still override a single instance.
213
216
 
214
217
  ## Forms
@@ -260,7 +263,7 @@ Nested forms. Overlays stay in the DOM, so a `Form` inside a Modal opened from a
260
263
  </KForm>
261
264
  ```
262
265
 
263
- Several controls in one `FormField`. Only the primary control takes the field id, name, description and invalid state. Wrap secondary controls (a search input above a Tree) in `<KFieldBoundary auxiliary>`, or call `useFieldControl({ role: 'auxiliary' })` in your own control: they keep their own id and only follow `disabled`. `<KFieldBoundary>` without `auxiliary` cuts the field context completely, like the overlays do. A primary control that is not labelable (a tree, a group, a contenteditable editor) passes `useFieldControl({ labelable: false })`; `FormField` then drops `for` from its label and the control uses `aria-labelledby` with `field.labelId` instead. `Tree`, `CheckboxGroup`, `RadioGroup`, `SegmentedControl` and `RichTextEditor` already do.
266
+ Several controls in one `FormField`. Only the primary control takes the field id, name, description and invalid state. Wrap secondary controls (a search input above a Tree) in `<KFieldBoundary auxiliary>`, or call `useFieldControl({ role: 'auxiliary' })` in your own control: they keep their own id and only follow `disabled`. `<KFieldBoundary>` without `auxiliary` cuts the field context completely, like the overlays do. A primary control that is not labelable (a tree, a group, a contenteditable editor) passes `useFieldControl({ labelable: false })`; `FormField` then drops `for` from its label and the control uses `aria-labelledby` with `control.labelledBy` (the label id, or `undefined` when the field has no label). A control that cannot carry `required` itself (a button trigger, a fieldset) passes `requiredHint: true`: `FormField` then adds a visually hidden "(required)" to its label. Errors and warnings render inside a polite live region, prefixed with a hidden "Error:" or "Warning:". `Tree`, `CheckboxGroup`, `RadioGroup`, `SegmentedControl` and `RichTextEditor` already do.
264
267
 
265
268
  ```vue
266
269
  <KFormField label="Folder">
@@ -546,7 +549,7 @@ export default defineAppConfig({
546
549
 
547
550
  The account row opens the SettingsModal (`account.action: 'menu'` with `account.menu` for a dropdown instead). Sections from `app.config` can be built-ins or links (`to`); `settings.presets` limits the preset cards, `settings.signOutTo` adds Sign out.
548
551
 
549
- Pages pick them with `definePageMeta({ layout: 'k-dashboard', title: 'Items' })` or `layout: 'k-auth'` (the auth card shows `title` and `description` from the page meta). Breadcrumbs come from `meta.breadcrumbs`, otherwise from the matching navigation item (plus `meta.title` on deeper pages). At runtime `useDashboard().value = { account, breadcrumbs, badges: { '/items': 12 } }` overrides the account, the breadcrumbs and the badges.
552
+ Pages pick them with `definePageMeta({ layout: 'k-dashboard', title: 'Items' })` or `layout: 'k-auth'` (the auth card shows `title` and `description` from the page meta). Both layouts set the document title from `meta.title` and the brand name (`Items · Acme`; a page's own `useHead({ title })` wins) and announce route changes through a polite live region, unless the app already renders `<NuxtRouteAnnouncer />`. Breadcrumbs come from `meta.breadcrumbs`, otherwise from the matching navigation item (plus `meta.title` on deeper pages). At runtime `useDashboard().value = { account, breadcrumbs, badges: { '/items': 12 } }` overrides the account, the breadcrumbs and the badges. Set it in route middleware (`definePageMeta({ middleware })`) or a plugin, not in the page `setup`: the layout renders the sidebar and breadcrumbs before the page, so a value set during the page render would differ between the server HTML and hydration.
550
553
 
551
554
  ### Starter
552
555
 
@@ -567,13 +570,29 @@ Components are typed for `strictTemplates`: native attributes and listeners of t
567
570
 
568
571
  ## Accessibility
569
572
 
573
+ The components target WCAG 2.2 AA in every preset and both themes.
574
+
570
575
  - Native elements first (`button`, `a`, `input`, `select`, `dialog`, `table`), full ARIA patterns where none fits (combobox, listbox, menu, tabs, tree, radio group, grid calendar).
571
- - Labels, descriptions and errors are wired through `FormField` (`for`/`id`, `aria-describedby`, `aria-invalid`, required state). Every form control works inside it.
572
- - Keyboard: arrows inside composite widgets, Escape closes overlays, focus moves into overlays on open and returns to the trigger on close, modals trap focus natively. Tag supports Delete and Backspace on its remove button.
573
- - Icon-only buttons need an `aria-label` (Button warns in development). Decorative icons are hidden from assistive technology.
574
- - Toasts are announced through polite or assertive live regions. While a modal dialog is open the toast stack and the live regions move inside that dialog, so they stay reachable and announced (the rest of the page is inert).
576
+ - Labels, descriptions and errors are wired through `FormField` (`for`/`id`, `aria-describedby`, `aria-invalid`, required state, hidden "(required)" for controls that cannot carry it). Every form control works inside it, and errors are announced when they appear.
577
+ - Every control has an accessible name: components that can end up unnamed take a `label` prop (falling back to the FormField label) and warn in development. Icon-only buttons need an `aria-label` (Button warns in development). Decorative icons are hidden from assistive technology.
578
+ - Keyboard: arrows inside composite widgets, Escape closes overlays, focus moves into overlays on open and returns to the trigger on close, modals trap focus natively. Tag supports Delete and Backspace on its remove button. Busy buttons (`loading`, or a submitting form) keep focus: they use `aria-disabled` and ignore clicks instead of becoming disabled.
579
+ - Focus is a 2px opaque ring at 3:1 or more; text is 4.5:1 or more, icons and control boundaries 3:1 or more.
580
+ - Windows forced colors (high contrast) keep focus rings, checked, selected and current states, switch thumbs, progress and meter fills, and overlay borders.
581
+ - Pointer targets are at least 24px.
582
+ - Status changes (loading, results, copy, uploads, sorting, toasts) are announced through persistent live regions. While a modal dialog is open the toast stack and the live regions move inside that dialog, so they stay reachable and announced (the rest of the page is inert).
575
583
  - `prefers-reduced-motion` disables animations inside every root. Colors are never the only signal.
576
584
 
585
+ Accessibility related props and defaults:
586
+
587
+ - Accessible names: `label` on `RadioGroup` and `CheckboxGroup` (alias of `legend`), `SegmentedControl`, `Slider`, `PinInput`, `InputGroup`, `Tabs`, `Toolbar`, `Modal`, `Drawer` and `useModal`/`useDrawer` options (when there is no `title`), `Card` (stretched link without a title), `ChartLegend`. `AppShell` takes `closeLabel` for the close button of the mobile navigation, which opens as a named modal dialog. `Sidebar`'s collapse button keeps one label (`collapseLabel`) and exposes the state with `aria-expanded`.
588
+ - Heading levels: `level` on `Calendar`, `DatePicker`, `ChartPanel`, `ErrorPage` and `NotFoundPage`; page templates pass `level + 1` to their sections and empty states.
589
+ - Live roles are opt in on `Alert`: pass `role="alert"` or `role="status"` for messages that appear at runtime; static banners get none. Without a title the tone is spoken from a hidden prefix.
590
+ - `Toolbar` uses roving focus by default (`:roving="false"` renders a plain named group).
591
+ - Copy props that contain a value are templates: `removeLabel` (`'Remove {label}'`) on `Tag`, `Combobox` and `FileInput`, `moreLabel` (`'{count} more'`) on `AvatarGroup`, `pageLabel` (`'Page {page}'`) on `Pagination`. `ListPage` takes `resultsText: (count) => string` for the announced result count, `ColorInput` takes `invalidText`.
592
+ - `ColorInput` `swatches` accept `{ value, label }` objects (a radio group named by `label` or the hex). `PasswordInput` picks `autocomplete="new-password"` with `generate` or `strength`, `current-password` otherwise.
593
+ - `Tree` with `reorderable` shows small move buttons (up, down, indent, outdent) on the hovered or focused row, as a single pointer alternative to dragging. Set `patterns` on `LineChart` and `BarChart` to tell series apart without color (dash and point style per line, a fill pattern per bar series, mirrored in the legend); it is off by default.
594
+ - `DataGrid` translates the AG Grid ARIA strings through the catalog (`grid.*`; your own `localeText` still wins) and announces row count changes.
595
+
577
596
  ## Browser support
578
597
 
579
598
  - Required: native `popover`, `<dialog>`, `:where()`, cascade layers, `color-mix()`, `light-dark()` (Chrome and Edge 123, Firefox 128, Safari 17.5).
@@ -10,7 +10,7 @@
10
10
  :position="legendPosition"
11
11
  @toggle="toggle"
12
12
  />
13
- <ChartSummary :caption="label" :labels="labels" :rows="series" />
13
+ <ChartSummary :caption="label" :labels="labels" :rows="series" :format-value="formatValue" />
14
14
  </div>
15
15
  </template>
16
16
 
@@ -22,6 +22,7 @@ import ChartSummary from "./ChartSummary.vue";
22
22
  import { buildCartesianOptions } from "./options";
23
23
  import { registerChartModules } from "./register";
24
24
  import { useChartSurface } from "./useChartSurface";
25
+ import { barFill, seriesPattern } from "./patterns";
25
26
  registerChartModules();
26
27
  const props = defineProps({
27
28
  labels: { type: Array, required: true },
@@ -32,6 +33,7 @@ const props = defineProps({
32
33
  legendPosition: { type: String, required: false, default: "bottom" },
33
34
  stacked: { type: Boolean, required: false },
34
35
  horizontal: { type: Boolean, required: false },
36
+ patterns: { type: Boolean, required: false },
35
37
  formatValue: { type: Function, required: false },
36
38
  options: { type: null, required: false }
37
39
  });
@@ -46,17 +48,22 @@ const legendItems = computed(
46
48
  () => props.series.map((entry, index) => ({
47
49
  label: entry.label,
48
50
  color: colorAt(index),
49
- hidden: isHidden(index)
51
+ hidden: isHidden(index),
52
+ pattern: props.patterns ? seriesPattern(index) : void 0
50
53
  }))
51
54
  );
55
+ function fillAt(index) {
56
+ const color = colorAt(index);
57
+ return props.patterns ? barFill(color, seriesPattern(index), colors.value.surface) : color;
58
+ }
52
59
  const chartData = computed(() => ({
53
60
  labels: props.labels,
54
61
  datasets: props.series.map((entry, index) => ({
55
62
  label: entry.label,
56
63
  data: entry.data,
57
64
  hidden: isHidden(index),
58
- backgroundColor: colorAt(index),
59
- hoverBackgroundColor: colorAt(index),
65
+ backgroundColor: fillAt(index),
66
+ hoverBackgroundColor: fillAt(index),
60
67
  borderRadius: 4,
61
68
  borderSkipped: false,
62
69
  maxBarThickness: 36
@@ -9,6 +9,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
9
9
  legendPosition?: ChartLegendPosition;
10
10
  stacked?: boolean;
11
11
  horizontal?: boolean;
12
+ patterns?: boolean;
12
13
  formatValue?: ChartValueFormatter;
13
14
  options?: ChartOptions<"bar">;
14
15
  innerHTML?: string | undefined | undefined;
@@ -212,6 +213,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
212
213
  legendPosition?: ChartLegendPosition;
213
214
  stacked?: boolean;
214
215
  horizontal?: boolean;
216
+ patterns?: boolean;
215
217
  formatValue?: ChartValueFormatter;
216
218
  options?: ChartOptions<"bar">;
217
219
  innerHTML?: string | undefined | undefined;
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <ul class="k-chart-legend" :class="`is-${position}`">
2
+ <ul class="k-chart-legend" :class="`is-${position}`" role="list" :aria-label="legendLabel">
3
3
  <li v-for="(item, index) in items" :key="item.label">
4
4
  <button
5
5
  type="button"
@@ -8,7 +8,37 @@
8
8
  :aria-pressed="!item.hidden"
9
9
  @click="emit('toggle', index)"
10
10
  >
11
- <span class="k-chart-legend-swatch" :style="{ background: item.color }" />
11
+ <svg
12
+ v-if="item.dash"
13
+ class="k-chart-legend-line"
14
+ viewBox="0 0 18 10"
15
+ aria-hidden="true"
16
+ :stroke="item.color"
17
+ >
18
+ <line
19
+ x1="1"
20
+ y1="5"
21
+ x2="17"
22
+ y2="5"
23
+ stroke-width="2"
24
+ :stroke-dasharray="dashArray(item.dash)"
25
+ />
26
+ <circle v-if="item.point === 'circle'" cx="9" cy="5" r="2.5" />
27
+ <rect v-else-if="item.point === 'rect'" x="6.5" y="2.5" width="5" height="5" />
28
+ <polygon v-else-if="item.point === 'triangle'" points="9,2 12,7.5 6,7.5" />
29
+ <polygon v-else-if="item.point === 'rectRot'" points="9,1.5 12.5,5 9,8.5 5.5,5" />
30
+ <polygon
31
+ v-else-if="item.point === 'star'"
32
+ points="9,1.5 10,4 12.5,4 10.5,5.75 11.25,8.5 9,7 6.75,8.5 7.5,5.75 5.5,4 8,4"
33
+ />
34
+ <path v-else-if="item.point === 'crossRot'" d="M6.5 2.5l5 5m0-5l-5 5" />
35
+ </svg>
36
+ <span
37
+ v-else
38
+ class="k-chart-legend-swatch"
39
+ aria-hidden="true"
40
+ :style="{ background: patternBackground(item.color, item.pattern) }"
41
+ />
12
42
  <span class="k-chart-legend-label">{{ item.label }}</span>
13
43
  <span v-if="item.value" class="k-chart-legend-value k-tabular">{{ item.value }}</span>
14
44
  </button>
@@ -17,9 +47,18 @@
17
47
  </template>
18
48
 
19
49
  <script setup>
20
- defineProps({
50
+ import { computed } from "vue";
51
+ import { useLocale } from "../composables/useLocale";
52
+ import { patternBackground } from "./patterns";
53
+ const props = defineProps({
21
54
  items: { type: Array, required: true },
22
- position: { type: String, required: false, default: "bottom" }
55
+ position: { type: String, required: false, default: "bottom" },
56
+ label: { type: String, required: false }
23
57
  });
24
58
  const emit = defineEmits(["toggle"]);
59
+ const { t } = useLocale();
60
+ const legendLabel = computed(() => props.label ?? t("chart.legend"));
61
+ function dashArray(dash) {
62
+ return dash.length ? dash.map((value) => value * 0.5).join(" ") : void 0;
63
+ }
25
64
  </script>
@@ -2,12 +2,14 @@ import type { ChartLegendItem, ChartLegendPosition } from './types';
2
2
  declare const __VLS_export: import("vue").DefineComponent<{
3
3
  items: ChartLegendItem[];
4
4
  position?: ChartLegendPosition;
5
+ label?: string;
5
6
  key?: PropertyKey | undefined;
6
7
  prefix?: string | undefined | undefined;
7
8
  title?: string | undefined | undefined;
8
9
  style?: import("vue").StyleValue;
9
10
  results?: (string | number) | undefined;
10
11
  placeholder?: string | undefined | undefined;
12
+ dir?: string | undefined | undefined;
11
13
  class?: import("vue").ClassValue;
12
14
  role?: string | undefined | undefined;
13
15
  'aria-label'?: string | undefined | undefined;
@@ -162,7 +164,6 @@ declare const __VLS_export: import("vue").DefineComponent<{
162
164
  accesskey?: string | undefined | undefined;
163
165
  contenteditable?: (boolean | "true" | "false") | "inherit" | "plaintext-only" | undefined;
164
166
  contextmenu?: string | undefined | undefined;
165
- dir?: string | undefined | undefined;
166
167
  draggable?: (boolean | "true" | "false") | undefined;
167
168
  enterkeyhint?: "enter" | "done" | "go" | "next" | "previous" | "search" | "send" | undefined | undefined;
168
169
  enterKeyHint?: "search" | "done" | "previous" | "next" | "enter" | "go" | "send" | undefined;
@@ -198,12 +199,14 @@ declare const __VLS_export: import("vue").DefineComponent<{
198
199
  }, string, import("vue").PublicProps, Readonly<{
199
200
  items: ChartLegendItem[];
200
201
  position?: ChartLegendPosition;
202
+ label?: string;
201
203
  key?: PropertyKey | undefined;
202
204
  prefix?: string | undefined | undefined;
203
205
  title?: string | undefined | undefined;
204
206
  style?: import("vue").StyleValue;
205
207
  results?: (string | number) | undefined;
206
208
  placeholder?: string | undefined | undefined;
209
+ dir?: string | undefined | undefined;
207
210
  class?: import("vue").ClassValue;
208
211
  role?: string | undefined | undefined;
209
212
  'aria-label'?: string | undefined | undefined;
@@ -358,7 +361,6 @@ declare const __VLS_export: import("vue").DefineComponent<{
358
361
  accesskey?: string | undefined | undefined;
359
362
  contenteditable?: (boolean | "true" | "false") | "inherit" | "plaintext-only" | undefined;
360
363
  contextmenu?: string | undefined | undefined;
361
- dir?: string | undefined | undefined;
362
364
  draggable?: (boolean | "true" | "false") | undefined;
363
365
  enterkeyhint?: "enter" | "done" | "go" | "next" | "previous" | "search" | "send" | undefined | undefined;
364
366
  enterKeyHint?: "search" | "done" | "previous" | "next" | "enter" | "go" | "send" | undefined;
@@ -2,24 +2,20 @@
2
2
  <section
3
3
  class="k-chart-panel"
4
4
  :aria-busy="loading ? 'true' : void 0"
5
- :aria-labelledby="titleId"
5
+ :aria-labelledby="title ? titleId : void 0"
6
6
  >
7
7
  <header v-if="title || $slots.actions || $slots.legend" class="k-chart-panel-header">
8
8
  <div class="k-chart-panel-heading">
9
- <h3 v-if="title" :id="titleId" class="k-chart-panel-title">{{ title }}</h3>
9
+ <component :is="headingTag" v-if="title" :id="titleId" class="k-chart-panel-title">{{
10
+ title
11
+ }}</component>
10
12
  <p v-if="description" class="k-chart-panel-description">{{ description }}</p>
11
13
  </div>
12
14
  <div v-if="$slots.legend" class="k-chart-panel-legend"><slot name="legend" /></div>
13
15
  <div v-if="$slots.actions" class="k-chart-panel-actions"><slot name="actions" /></div>
14
16
  </header>
15
17
  <div class="k-chart-panel-body">
16
- <div
17
- v-if="loading"
18
- class="k-chart-skeleton"
19
- :style="{ height }"
20
- role="status"
21
- :aria-label="loadingLabel"
22
- >
18
+ <div v-if="loading" class="k-chart-skeleton" :style="{ height }" aria-hidden="true">
23
19
  <span v-for="bar in skeletonBars" :key="bar" :style="{ height: `${bar}%` }" />
24
20
  </div>
25
21
  <div v-else-if="empty" class="k-chart-empty" :style="{ height }">
@@ -28,12 +24,14 @@
28
24
  <slot v-else />
29
25
  </div>
30
26
  <footer v-if="$slots.footer" class="k-chart-panel-footer"><slot name="footer" /></footer>
27
+ <LiveRegion :message="loading ? loadingLabel : ''" />
31
28
  </section>
32
29
  </template>
33
30
 
34
31
  <script setup>
35
32
  import { computed, useId } from "vue";
36
33
  import { useLocale } from "../composables/useLocale";
34
+ import LiveRegion from "../internal/core/LiveRegion.vue";
37
35
  const props = defineProps({
38
36
  title: { type: String, required: false },
39
37
  description: { type: String, required: false },
@@ -41,11 +39,13 @@ const props = defineProps({
41
39
  empty: { type: Boolean, required: false },
42
40
  emptyText: { type: String, required: false },
43
41
  loadingText: { type: String, required: false },
44
- height: { type: String, required: false, default: "16rem" }
42
+ height: { type: String, required: false, default: "16rem" },
43
+ level: { type: Number, required: false, default: 3 }
45
44
  });
46
45
  const { t } = useLocale();
47
46
  const emptyLabel = computed(() => props.emptyText ?? t("chart.empty"));
48
47
  const loadingLabel = computed(() => props.loadingText ?? t("chart.loading"));
49
48
  const titleId = useId();
49
+ const headingTag = computed(() => `h${props.level}`);
50
50
  const skeletonBars = [42, 68, 54, 86, 62, 74, 48, 92, 58, 70, 52, 80];
51
51
  </script>
@@ -1,14 +1,14 @@
1
- declare var __VLS_1: {}, __VLS_3: {}, __VLS_6: {}, __VLS_8: {}, __VLS_10: {};
1
+ declare var __VLS_7: {}, __VLS_9: {}, __VLS_12: {}, __VLS_14: {}, __VLS_16: {};
2
2
  type __VLS_Slots = {} & {
3
- legend?: (props: typeof __VLS_1) => any;
3
+ legend?: (props: typeof __VLS_7) => any;
4
4
  } & {
5
- actions?: (props: typeof __VLS_3) => any;
5
+ actions?: (props: typeof __VLS_9) => any;
6
6
  } & {
7
- empty?: (props: typeof __VLS_6) => any;
7
+ empty?: (props: typeof __VLS_12) => any;
8
8
  } & {
9
- default?: (props: typeof __VLS_8) => any;
9
+ default?: (props: typeof __VLS_14) => any;
10
10
  } & {
11
- footer?: (props: typeof __VLS_10) => any;
11
+ footer?: (props: typeof __VLS_16) => any;
12
12
  };
13
13
  declare const __VLS_base: import("vue").DefineComponent<{
14
14
  title?: string | undefined;
@@ -18,6 +18,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
18
18
  emptyText?: string;
19
19
  loadingText?: string;
20
20
  height?: string;
21
+ level?: 1 | 2 | 3 | 4 | 5 | 6;
21
22
  innerHTML?: string | undefined | undefined;
22
23
  class?: import("vue").ClassValue;
23
24
  style?: import("vue").StyleValue;
@@ -62,7 +63,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
62
63
  is?: string | undefined | undefined;
63
64
  exportparts?: string | undefined;
64
65
  part?: string | undefined;
65
- popover?: (import("..").PopoverMode | "" | boolean) | undefined;
66
+ popover?: (import("../index.ts").PopoverMode | "" | boolean) | undefined;
66
67
  'aria-activedescendant'?: string | undefined | undefined;
67
68
  'aria-atomic'?: (boolean | "true" | "false") | undefined;
68
69
  'aria-autocomplete'?: "none" | "inline" | "list" | "both" | undefined | undefined;
@@ -217,6 +218,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
217
218
  emptyText?: string;
218
219
  loadingText?: string;
219
220
  height?: string;
221
+ level?: 1 | 2 | 3 | 4 | 5 | 6;
220
222
  innerHTML?: string | undefined | undefined;
221
223
  class?: import("vue").ClassValue;
222
224
  style?: import("vue").StyleValue;
@@ -261,7 +263,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
261
263
  is?: string | undefined | undefined;
262
264
  exportparts?: string | undefined;
263
265
  part?: string | undefined;
264
- popover?: (import("..").PopoverMode | "" | boolean) | undefined;
266
+ popover?: (import("../index.ts").PopoverMode | "" | boolean) | undefined;
265
267
  'aria-activedescendant'?: string | undefined | undefined;
266
268
  'aria-atomic'?: (boolean | "true" | "false") | undefined;
267
269
  'aria-autocomplete'?: "none" | "inline" | "list" | "both" | undefined | undefined;
@@ -410,6 +412,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
410
412
  ref_key?: string | undefined | undefined;
411
413
  }> & Readonly<{}>, {
412
414
  height: string;
415
+ level: 1 | 2 | 4 | 3 | 5 | 6;
413
416
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
414
417
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
415
418
  declare const _default: typeof __VLS_export;
@@ -14,16 +14,21 @@
14
14
  <tbody>
15
15
  <tr v-for="(label, index) in labels" :key="label">
16
16
  <th scope="row">{{ label }}</th>
17
- <td v-for="series in rows" :key="series.label">{{ series.data[index] ?? "" }}</td>
17
+ <td v-for="series in rows" :key="series.label">{{ cellText(series.data[index]) }}</td>
18
18
  </tr>
19
19
  </tbody>
20
20
  </table>
21
21
  </template>
22
22
 
23
23
  <script setup>
24
- defineProps({
24
+ const props = defineProps({
25
25
  caption: { type: String, required: true },
26
26
  labels: { type: Array, required: true },
27
- rows: { type: Array, required: true }
27
+ rows: { type: Array, required: true },
28
+ formatValue: { type: Function, required: false }
28
29
  });
30
+ function cellText(value) {
31
+ if (value === null || value === void 0) return "";
32
+ return props.formatValue ? props.formatValue(value) : String(value);
33
+ }
29
34
  </script>
@@ -1,8 +1,9 @@
1
- import type { ChartSeries } from './types';
1
+ import type { ChartSeries, ChartValueFormatter } from './types';
2
2
  declare const __VLS_export: import("vue").DefineComponent<{
3
3
  caption: string;
4
4
  labels: string[];
5
5
  rows: ChartSeries[];
6
+ formatValue?: ChartValueFormatter;
6
7
  cellpadding?: (string | number) | undefined;
7
8
  cellspacing?: (string | number) | undefined;
8
9
  summary?: string | undefined | undefined;
@@ -203,6 +204,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
203
204
  caption: string;
204
205
  labels: string[];
205
206
  rows: ChartSeries[];
207
+ formatValue?: ChartValueFormatter;
206
208
  cellpadding?: (string | number) | undefined;
207
209
  cellspacing?: (string | number) | undefined;
208
210
  summary?: string | undefined | undefined;
@@ -1,14 +1,21 @@
1
1
  <template>
2
2
  <div ref="root" class="k-chart k-chart-doughnut" :class="`k-chart-legend-${legendPosition}`">
3
- <div class="k-chart-canvas" :style="{ height }" role="img" :aria-label="label">
4
- <Doughnut v-if="ready" :data="chartData" :options="chartOptions" />
5
- <div v-else class="k-chart-placeholder is-round" aria-hidden="true" />
3
+ <div class="k-chart-canvas" :style="{ height }">
4
+ <div class="k-chart-figure" role="img" :aria-label="label">
5
+ <Doughnut v-if="ready" :data="chartData" :options="chartOptions" />
6
+ <div v-else class="k-chart-placeholder is-round" aria-hidden="true" />
7
+ </div>
6
8
  <div v-if="$slots.default" class="k-chart-center">
7
9
  <slot />
8
10
  </div>
9
11
  </div>
10
12
  <ChartLegend v-if="legend" :items="legendItems" :position="legendPosition" @toggle="toggle" />
11
- <ChartSummary :caption="label" :labels="labels" :rows="[{ label, data: values }]" />
13
+ <ChartSummary
14
+ :caption="label"
15
+ :labels="labels"
16
+ :rows="[{ label, data: values }]"
17
+ :format-value="formatValue"
18
+ />
12
19
  </div>
13
20
  </template>
14
21
 
@@ -10,7 +10,7 @@
10
10
  :position="legendPosition"
11
11
  @toggle="toggle"
12
12
  />
13
- <ChartSummary :caption="label" :labels="labels" :rows="series" />
13
+ <ChartSummary :caption="label" :labels="labels" :rows="series" :format-value="formatValue" />
14
14
  </div>
15
15
  </template>
16
16
 
@@ -22,6 +22,7 @@ import ChartSummary from "./ChartSummary.vue";
22
22
  import { buildCartesianOptions } from "./options";
23
23
  import { registerChartModules } from "./register";
24
24
  import { useChartSurface } from "./useChartSurface";
25
+ import { seriesDash, seriesPoint } from "./patterns";
25
26
  import { withAlpha } from "./useChartColors";
26
27
  registerChartModules();
27
28
  const props = defineProps({
@@ -34,6 +35,7 @@ const props = defineProps({
34
35
  smooth: { type: Boolean, required: false, default: true },
35
36
  fill: { type: Boolean, required: false },
36
37
  points: { type: Boolean, required: false },
38
+ patterns: { type: Boolean, required: false },
37
39
  stacked: { type: Boolean, required: false },
38
40
  formatValue: { type: Function, required: false },
39
41
  options: { type: null, required: false }
@@ -49,7 +51,9 @@ const legendItems = computed(
49
51
  () => props.series.map((entry, index) => ({
50
52
  label: entry.label,
51
53
  color: colorAt(index),
52
- hidden: isHidden(index)
54
+ hidden: isHidden(index),
55
+ dash: props.patterns ? seriesDash(index) : void 0,
56
+ point: props.patterns && props.points ? seriesPoint(index) : void 0
53
57
  }))
54
58
  );
55
59
  const chartData = computed(() => ({
@@ -63,6 +67,8 @@ const chartData = computed(() => ({
63
67
  borderColor: color,
64
68
  backgroundColor: withAlpha(color, props.fill ? 0.14 : 1),
65
69
  borderWidth: 2,
70
+ borderDash: props.patterns ? seriesDash(index) : [],
71
+ pointStyle: props.patterns ? seriesPoint(index) : "circle",
66
72
  tension: props.smooth ? 0.35 : 0,
67
73
  fill: props.fill ? "origin" : false,
68
74
  pointRadius: props.points ? 3 : 0,
@@ -10,6 +10,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
10
10
  smooth?: boolean;
11
11
  fill?: boolean;
12
12
  points?: boolean;
13
+ patterns?: boolean;
13
14
  stacked?: boolean;
14
15
  formatValue?: ChartValueFormatter;
15
16
  options?: ChartOptions<"line">;
@@ -215,6 +216,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
215
216
  smooth?: boolean;
216
217
  fill?: boolean;
217
218
  points?: boolean;
219
+ patterns?: boolean;
218
220
  stacked?: boolean;
219
221
  formatValue?: ChartValueFormatter;
220
222
  options?: ChartOptions<"line">;
@@ -97,6 +97,12 @@
97
97
  border-radius: var(--k-radius-full);
98
98
  }
99
99
 
100
+ .k-chart-figure {
101
+ position: relative;
102
+ width: 100%;
103
+ height: 100%;
104
+ }
105
+
100
106
  .k-chart-center {
101
107
  position: absolute;
102
108
  inset: 0;
@@ -168,6 +174,19 @@
168
174
  border-radius: 3px;
169
175
  }
170
176
 
177
+ .k-chart-legend-line {
178
+ flex: none;
179
+ width: 1.125rem;
180
+ height: 0.625rem;
181
+ overflow: visible;
182
+ fill: var(--k-surface);
183
+ stroke-width: 1.5;
184
+ }
185
+
186
+ .k-chart-legend-line > line {
187
+ stroke-width: 2;
188
+ }
189
+
171
190
  .k-chart-legend-label {
172
191
  flex: 1;
173
192
  min-width: 0;
@@ -0,0 +1,6 @@
1
+ import type { ChartBarPattern, ChartPointStyle } from './types';
2
+ export declare function seriesDash(index: number): number[];
3
+ export declare function seriesPoint(index: number): ChartPointStyle;
4
+ export declare function seriesPattern(index: number): ChartBarPattern;
5
+ export declare function barFill(color: string, pattern: ChartBarPattern, surface: string): string | CanvasPattern;
6
+ export declare function patternBackground(color: string, pattern: ChartBarPattern | undefined): string;
@@ -0,0 +1,79 @@
1
+ import { isClient } from "../internal/core/env.mjs";
2
+ const dashes = [[], [6, 4], [2, 3], [10, 3, 2, 3], [14, 5], [4, 2, 1, 2]];
3
+ const points = ["circle", "rect", "triangle", "rectRot", "star", "crossRot"];
4
+ const patterns = ["solid", "diagonal", "dots", "horizontal", "reverse", "cross"];
5
+ export function seriesDash(index) {
6
+ return dashes[index % dashes.length];
7
+ }
8
+ export function seriesPoint(index) {
9
+ return points[index % points.length];
10
+ }
11
+ export function seriesPattern(index) {
12
+ return patterns[index % patterns.length];
13
+ }
14
+ const TILE = 8;
15
+ const cache = /* @__PURE__ */ new Map();
16
+ function strokeLine(context, x1, y1, x2, y2) {
17
+ context.beginPath();
18
+ context.moveTo(x1, y1);
19
+ context.lineTo(x2, y2);
20
+ context.stroke();
21
+ }
22
+ function drawMarks(context, pattern) {
23
+ if (pattern === "diagonal" || pattern === "cross") {
24
+ strokeLine(context, -1, TILE + 1, TILE + 1, -1);
25
+ strokeLine(context, -1, 1, 1, -1);
26
+ strokeLine(context, TILE - 1, TILE + 1, TILE + 1, TILE - 1);
27
+ }
28
+ if (pattern === "reverse" || pattern === "cross") {
29
+ strokeLine(context, -1, -1, TILE + 1, TILE + 1);
30
+ strokeLine(context, TILE - 1, -1, TILE + 1, 1);
31
+ strokeLine(context, -1, TILE - 1, 1, TILE + 1);
32
+ }
33
+ if (pattern === "horizontal") strokeLine(context, 0, TILE / 2, TILE, TILE / 2);
34
+ if (pattern === "dots") {
35
+ context.beginPath();
36
+ context.arc(TILE / 2, TILE / 2, 1.5, 0, Math.PI * 2);
37
+ context.fill();
38
+ }
39
+ }
40
+ export function barFill(color, pattern, surface) {
41
+ if (pattern === "solid" || !isClient()) return color;
42
+ const key = `${color}|${pattern}|${surface}`;
43
+ const cached = cache.get(key);
44
+ if (cached) return cached;
45
+ const canvas = document.createElement("canvas");
46
+ canvas.width = TILE;
47
+ canvas.height = TILE;
48
+ const context = canvas.getContext("2d");
49
+ if (!context) return color;
50
+ context.fillStyle = color;
51
+ context.fillRect(0, 0, TILE, TILE);
52
+ context.globalAlpha = 0.6;
53
+ context.strokeStyle = surface;
54
+ context.fillStyle = surface;
55
+ context.lineWidth = 1.5;
56
+ drawMarks(context, pattern);
57
+ const result = context.createPattern(canvas, "repeat") ?? color;
58
+ if (cache.size > 200) cache.clear();
59
+ cache.set(key, result);
60
+ return result;
61
+ }
62
+ export function patternBackground(color, pattern) {
63
+ const mark = `color-mix(in oklab, var(--k-surface) 60%, ${color})`;
64
+ const stripes = (angle) => `repeating-linear-gradient(${angle}, ${color} 0 2.5px, ${mark} 2.5px 4px)`;
65
+ switch (pattern) {
66
+ case "diagonal":
67
+ return stripes("-45deg");
68
+ case "reverse":
69
+ return stripes("45deg");
70
+ case "horizontal":
71
+ return stripes("0deg");
72
+ case "cross":
73
+ return `repeating-linear-gradient(45deg, transparent 0 2.5px, ${mark} 2.5px 4px), ${stripes("-45deg")}`;
74
+ case "dots":
75
+ return `radial-gradient(circle, ${mark} 0 1px, transparent 1.25px) 0 0 / 4px 4px, ${color}`;
76
+ default:
77
+ return color;
78
+ }
79
+ }