@xleddyl/443-components-vue 0.8.1 → 0.8.3
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.
- package/README.md +52 -4
- package/dist/chart/BarChart.vue +33 -22
- package/dist/chart/BarChart.vue.d.ts +4 -0
- package/dist/chart/chart.css +2 -2
- package/dist/chart/useChartColors.mjs +8 -8
- package/dist/components/core/Button.vue.d.ts +1 -1
- package/dist/components/core/FormField.vue +5 -0
- package/dist/components/core/FormField.vue.d.ts +4 -0
- package/dist/components/core/Input.vue +59 -5
- package/dist/components/core/Input.vue.d.ts +9 -5
- package/dist/components/core/Textarea.vue +28 -3
- package/dist/components/core/Textarea.vue.d.ts +7 -5
- package/dist/components/core/types.d.ts +1 -1
- package/dist/components/data/Badge.vue.d.ts +1 -1
- package/dist/components/data/DescriptionList.vue.d.ts +1 -1
- package/dist/components/data/FilterPills.vue +43 -4
- package/dist/components/data/FilterPills.vue.d.ts +4 -1
- package/dist/components/data/Pagination.vue +63 -2
- package/dist/components/data/Pagination.vue.d.ts +12 -0
- package/dist/components/data/Tag.vue.d.ts +1 -1
- package/dist/components/data/Tree.vue.d.ts +1 -1
- package/dist/components/data/types.d.ts +1 -0
- package/dist/components/feedback/Meter.vue.d.ts +1 -1
- package/dist/components/feedback/Progress.vue.d.ts +1 -1
- package/dist/components/feedback/ProgressRing.vue.d.ts +1 -1
- package/dist/components/forms/Calendar.vue +31 -7
- package/dist/components/forms/Calendar.vue.d.ts +13 -2
- package/dist/components/forms/CheckboxGroup.vue +7 -1
- package/dist/components/forms/CheckboxGroup.vue.d.ts +1 -0
- package/dist/components/forms/ColorInput.vue.d.ts +5 -5
- package/dist/components/forms/Combobox.vue +4 -3
- package/dist/components/forms/Combobox.vue.d.ts +1 -0
- package/dist/components/forms/DateInput.vue.d.ts +10 -6
- package/dist/components/forms/DatePicker.vue.d.ts +1 -1
- package/dist/components/forms/FileInput.vue.d.ts +1 -1
- package/dist/components/forms/NumberInput.vue.d.ts +5 -5
- package/dist/components/forms/PasswordInput.vue.d.ts +9 -5
- package/dist/components/forms/PinInput.vue.d.ts +5 -5
- package/dist/components/forms/Select.vue +21 -2
- package/dist/components/forms/Select.vue.d.ts +4 -4
- package/dist/components/forms/Slider.vue.d.ts +5 -5
- package/dist/components/forms/Switch.vue.d.ts +1 -1
- package/dist/components/forms/TimeInput.vue.d.ts +9 -5
- package/dist/components/layout/AppShell.vue +4 -2
- package/dist/components/layout/AppShell.vue.d.ts +2 -0
- package/dist/components/layout/Card.vue.d.ts +1 -1
- package/dist/components/layout/CopyButton.vue.d.ts +1 -1
- package/dist/components/layout/PageHeader.vue +15 -3
- package/dist/components/layout/PageHeader.vue.d.ts +5 -0
- package/dist/components/layout/Separator.vue +17 -4
- package/dist/components/layout/Separator.vue.d.ts +7 -1
- package/dist/components/layout/Tabs.vue +10 -0
- package/dist/components/layout/types.d.ts +3 -0
- package/dist/components/overlays/ConfirmDialog.vue.d.ts +8 -4
- package/dist/components/overlays/ConfirmHost.vue.d.ts +8 -4
- package/dist/components/overlays/ContextMenu.vue.d.ts +4 -2
- package/dist/components/overlays/DropdownMenu.vue +5 -1
- package/dist/components/overlays/DropdownMenu.vue.d.ts +8 -2
- package/dist/components/overlays/Modal.vue +9 -2
- package/dist/components/overlays/Modal.vue.d.ts +4 -0
- package/dist/components/overlays/types.d.ts +2 -2
- package/dist/components/templates/DashboardLayout.vue +2 -0
- package/dist/components/templates/DashboardLayout.vue.d.ts +2 -0
- package/dist/components/templates/SettingsModal.vue.d.ts +8 -4
- package/dist/components/templates/types.d.ts +1 -0
- package/dist/composables/useForm.d.ts +24 -2
- package/dist/composables/useForm.mjs +51 -8
- package/dist/composables/useOverlay.d.ts +1 -1
- package/dist/editor/RichTextEditor.vue +108 -10
- package/dist/editor/RichTextEditor.vue.d.ts +5 -2
- package/dist/editor/rich-text-editor.css +31 -4
- package/dist/editor/types.d.ts +2 -1
- package/dist/grid/DataGrid.vue +106 -24
- package/dist/grid/DataGrid.vue.d.ts +14 -2
- package/dist/grid/cells/DateCell.vue +18 -3
- package/dist/grid/cells/ImageCell.vue +6 -1
- package/dist/grid/data-grid.css +17 -3
- package/dist/grid/index.d.ts +2 -1
- package/dist/grid/index.mjs +6 -1
- package/dist/grid/modules.d.ts +2 -0
- package/dist/grid/modules.mjs +32 -0
- package/dist/grid/theme.mjs +5 -5
- package/dist/grid/types.d.ts +4 -0
- package/dist/icons/builtin.d.ts +3 -0
- package/dist/icons/builtin.mjs +3 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +1 -0
- package/dist/internal/core/intl.d.ts +2 -0
- package/dist/internal/core/intl.mjs +17 -0
- package/dist/internal/forms/counter.d.ts +18 -0
- package/dist/internal/forms/counter.mjs +31 -0
- package/dist/internal/keys.d.ts +2 -1
- package/dist/internal/keys.mjs +1 -0
- package/dist/internal/overlays/MenuList.vue +4 -2
- package/dist/internal/overlays/MenuList.vue.d.ts +2 -0
- package/dist/internal/overlays/OverlayView.vue.d.ts +8 -4
- package/dist/locale/de.mjs +21 -3
- package/dist/locale/en.mjs +21 -3
- package/dist/locale/it.mjs +21 -3
- package/dist/locale/types.d.ts +18 -0
- package/dist/nuxt/runtime/layouts/dashboard.vue +1 -0
- package/dist/styles/base.css +2 -1
- package/dist/styles/core/button.css +42 -16
- package/dist/styles/core/form-field.css +7 -1
- package/dist/styles/core/input.css +31 -2
- package/dist/styles/core/kbd.css +1 -1
- package/dist/styles/core/textarea.css +10 -0
- package/dist/styles/data/avatar.css +1 -1
- package/dist/styles/data/badge.css +39 -23
- package/dist/styles/data/description-list.css +2 -2
- package/dist/styles/data/filter-pills.css +24 -3
- package/dist/styles/data/pagination.css +26 -3
- package/dist/styles/data/selection-bar.css +1 -1
- package/dist/styles/data/stat.css +4 -4
- package/dist/styles/data/table.css +4 -4
- package/dist/styles/data/tag.css +1 -1
- package/dist/styles/data/timeline.css +1 -1
- package/dist/styles/data/tree.css +2 -2
- package/dist/styles/feedback/empty-state.css +1 -1
- package/dist/styles/feedback/loading-host.css +1 -1
- package/dist/styles/feedback/loading-overlay.css +1 -1
- package/dist/styles/feedback/meter.css +1 -1
- package/dist/styles/feedback/progress.css +2 -2
- package/dist/styles/forced-colors.css +6 -1
- package/dist/styles/forms/calendar.css +30 -3
- package/dist/styles/forms/checkbox.css +1 -1
- package/dist/styles/forms/choice.css +7 -1
- package/dist/styles/forms/combobox.css +2 -2
- package/dist/styles/forms/file-input.css +1 -1
- package/dist/styles/forms/password-input.css +1 -1
- package/dist/styles/forms/pin-input.css +1 -1
- package/dist/styles/forms/radio.css +1 -1
- package/dist/styles/forms/segmented.css +2 -2
- package/dist/styles/forms/select.css +2 -2
- package/dist/styles/forms/slider.css +1 -1
- package/dist/styles/forms/switch.css +4 -4
- package/dist/styles/layout/accordion.css +2 -2
- package/dist/styles/layout/app-shell.css +18 -6
- package/dist/styles/layout/breadcrumbs.css +3 -3
- package/dist/styles/layout/card.css +1 -1
- package/dist/styles/layout/nav-link.css +1 -1
- package/dist/styles/layout/page-header.css +21 -1
- package/dist/styles/layout/separator.css +21 -1
- package/dist/styles/layout/sidebar.css +1 -1
- package/dist/styles/layout/tabs.css +30 -1
- package/dist/styles/layout/theme-switch.css +1 -1
- package/dist/styles/overlays/command.css +3 -3
- package/dist/styles/overlays/drawer.css +4 -3
- package/dist/styles/overlays/floating.css +2 -2
- package/dist/styles/overlays/menu.css +5 -1
- package/dist/styles/overlays/modal.css +12 -9
- package/dist/styles/overlays/toast.css +3 -3
- package/dist/styles/overlays/tooltip.css +26 -8
- package/dist/styles/shared.css +2 -2
- package/dist/styles/templates/account-menu.css +1 -1
- package/dist/styles/templates/auth-layout.css +4 -4
- package/dist/styles/templates/brand.css +2 -2
- package/dist/styles/templates/dashboard-page.css +1 -1
- package/dist/styles/templates/empty-page.css +3 -3
- package/dist/styles/templates/error-page.css +1 -1
- package/dist/styles/templates/save-bar.css +1 -1
- package/dist/styles/templates/settings-modal.css +6 -6
- package/dist/styles/utilities.css +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,7 +108,22 @@ 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
|
-
A few component tokens are unset by default and only exist to be overridden on a root
|
|
111
|
+
A few component tokens are unset by default and only exist to be overridden on a root (or any ancestor). Unset, every one keeps the library look. The full list with defaults is in [CONVENTIONS.md](./CONVENTIONS.md#tokens):
|
|
112
|
+
|
|
113
|
+
| Area | Tokens |
|
|
114
|
+
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
115
|
+
| Type | `--k-font-weight` (400), `--k-font-weight-medium` (500), `--k-font-weight-strong` (600), `--k-font-weight-display` (650) |
|
|
116
|
+
| Buttons | `--k-btn-highlight`, `--k-btn-shadow` (`0 0 #0000` for a flat button), `--k-btn-border`, `--k-btn-padding-x`, `--k-btn-font-size`, `--k-btn-icon-size`, `--k-press-scale` (0.97) |
|
|
117
|
+
| Fields | `--k-field-padding-x` |
|
|
118
|
+
| Overlays | `--k-modal-radius`, `--k-modal-shadow`, `--k-drawer-radius`, `--k-drawer-shadow`, `--k-popover-radius`, `--k-popover-shadow`, `--k-toast-radius`, `--k-toast-shadow`, `--k-scrim-blur` (3px) |
|
|
119
|
+
| Tooltip | `--k-tooltip-background`, `--k-tooltip-text`, `--k-tooltip-border`, `--k-tooltip-radius`, `--k-tooltip-shadow`, `--k-tooltip-padding` |
|
|
120
|
+
| Shell | `--k-sidebar-width` (15rem), `--k-sidebar-rail-width`, `--k-topbar-height` (3rem), `--k-shell-inset`, `--k-shell-padding`, `--k-shell-content` |
|
|
121
|
+
| Switch | `--k-switch-track-width`, `--k-switch-track-height`, `--k-switch-thumb-size` (md size) |
|
|
122
|
+
| Badge | `--k-badge-radius`, `--k-badge-font`, `--k-badge-font-size`, `--k-badge-font-weight`, `--k-badge-text-transform`, `--k-badge-letter-spacing` |
|
|
123
|
+
| DataGrid | `--k-grid-header-background`, `--k-grid-header-text`, `--k-grid-header-font`, `--k-grid-header-font-weight`, `--k-grid-header-font-size` (12px), `--k-grid-font-size` (13px), `--k-grid-radius`, `--k-grid-selected-background` |
|
|
124
|
+
| Charts | `--k-chart-1` to `--k-chart-8` (series palette) |
|
|
125
|
+
|
|
126
|
+
Some tokens are meant for one element: set `--k-btn-tone`, `--k-btn-on-tone` and `--k-btn-tone-text` on a button for a brand tone outside `Tone` (a lime fill with green text), and `--k-badge-background`, `--k-badge-color` and `--k-badge-border-color` on a badge for a custom pill color. `variant="subtle"` is a borderless tinted fill (`--k-fill` for the neutral tone).
|
|
112
127
|
|
|
113
128
|
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.
|
|
114
129
|
|
|
@@ -218,7 +233,7 @@ k: { locale: 'it', messages: { it: { common: { cancel: 'Indietro' } } } }
|
|
|
218
233
|
|
|
219
234
|
## Forms
|
|
220
235
|
|
|
221
|
-
`useForm({ schema })` takes field definitions (`initial`, `required`, `min`, `max`, `minLength`, `maxLength`, `pattern`, `validate`). Any [Standard Schema](https://standardschema.dev) library (Zod 4, Valibot, ArkType) also works, per field (`schema` in the definition) or for the whole form (`validator`, issues are matched to fields by their first path segment):
|
|
236
|
+
`useForm({ schema })` takes field definitions (`initial`, `required`, `min`, `max`, `minLength`, `maxLength`, `pattern`, `validate`, `isEmpty`). Any [Standard Schema](https://standardschema.dev) library (Zod 4, Valibot, ArkType) also works, per field (`schema` in the definition) or for the whole form (`validator`, issues are matched to fields by their first path segment):
|
|
222
237
|
|
|
223
238
|
```ts
|
|
224
239
|
const form = useForm({
|
|
@@ -232,6 +247,12 @@ Issues are keyed by their first path segment and by their full dotted path (`blo
|
|
|
232
247
|
|
|
233
248
|
Built-in messages follow the active language. `messages` overrides them for one form.
|
|
234
249
|
|
|
250
|
+
`required` treats `null`, `undefined`, blank strings, empty arrays, `NaN` and plain objects whose values are all empty (a per-language map like `{ it: '', en: '' }`) as empty. A field with another idea of empty passes `isEmpty: (value) => boolean`, which replaces the check for that field. `isEmptyValue(value)` is the default check, exported for reuse.
|
|
251
|
+
|
|
252
|
+
A `validate` function returns a message, `false` (the built-in invalid message), nothing (valid), or `{ message, params }` when the message needs values: `validate: (value) => value.length > 80 ? { message: 'errors.maxLength', params: { max: 80 } } : undefined`. The params reach `translateMessage` as `context.params`; without a translator they fill the `{name}` placeholders of the message.
|
|
253
|
+
|
|
254
|
+
Rules that return message keys instead of text (an existing validator library, server side codes) go through `translateMessage(message, { name, label, kind, params })`: it receives every string returned by a `validate` function, a field `schema`, the form `validator` and `warnings` (`kind` is `'error'` or `'warning'`), and returns the text to show. Built-in messages are not passed to it. Set `translateMessage` and `messages` once for the whole app with `app.use(createFormConfig({ translateMessage: (key) => i18n.t(key), messages: { required: () => i18n.t('form.required') } }))`, or `provideFormConfig()` in a component; the options of one `useForm` still win.
|
|
255
|
+
|
|
235
256
|
Forms can also drive values they do not own. Pass `values` (a reactive object, ref or getter) and the form reads, validates, tracks dirty state and submits it. The schema becomes optional and only adds rules, labels and `required`; its `initial` values are not used. `reset()` restores the clean snapshot, `reset(next)` replaces the values and makes them the new clean state (`form.reset(record.value)` after a save). `validator` accepts a ref or getter, so it can change with the record type:
|
|
236
257
|
|
|
237
258
|
```ts
|
|
@@ -287,7 +308,7 @@ Several controls in one `FormField`. Only the primary control takes the field id
|
|
|
287
308
|
| layout | Accordion, AppShell, Breadcrumbs, Card, CopyButton, Grid, NavLink, PageHeader, Separator, Sidebar, SidebarNav, SidebarSection, Stack, Tabs, ThemeSwitch, Toolbar |
|
|
288
309
|
| templates | DashboardLayout, AuthLayout, DashboardPage, ListPage, DetailPage, FormPage, SettingsPage, SettingsSection, SettingsModal, SettingsGroup, AppearanceSettings, SaveBar, ErrorPage, NotFoundPage, EmptyPage, Brand, AccountMenu |
|
|
289
310
|
| feedback | Alert, EmptyState, LoadingOverlay, Meter, Progress, ProgressRing, Skeleton |
|
|
290
|
-
| grid | DataGrid, GridToolbar, cell renderers (BadgeCell, BooleanCell, DateCell, ImageCell, LinkCell)
|
|
311
|
+
| grid | DataGrid, GridToolbar, cell renderers (BadgeCell, BooleanCell, DateCell, EmptyCell, ImageCell, LinkCell) |
|
|
291
312
|
| editor | RichTextEditor |
|
|
292
313
|
| chart | ChartPanel, ChartLegend, LineChart, BarChart, DoughnutChart |
|
|
293
314
|
|
|
@@ -296,6 +317,23 @@ Composables: `useTheme`, `usePreset`, `useForm`, `useField`, `useFieldControl`,
|
|
|
296
317
|
### Lists, trees and uploads
|
|
297
318
|
|
|
298
319
|
- `Tree` takes `size="sm"` for dense rows, `checkmark`, `descriptionVariant` (`mono`, the default for paths and slugs, or `text`; a node can override it with its own `descriptionVariant`), `emptyText` (or `#empty`) and works inside a `FormField` (labelled and described by the field). With `reorderable` rows get a drag handle and Alt with the up and down arrows moves them; `nestable` also lets rows change level (drop in the middle of a row, Alt with left and right). The `move` event carries `{ node, target, position, from: { parent, index }, to: { parent, index } }`, `to.index` being the final index, and `applyTreeMove(nodes, move)` returns the updated tree. `canDrop(move)` vetoes a move. Moves are announced to screen readers.
|
|
320
|
+
- `Select` takes string, number or boolean option values (`{ value: true, label: 'Yes' }`); the model gets the option value back with its type and is typed `T | null | undefined`, so `v-model` works on a narrow ref (`Ref<'a' | 'b' | null>`). An option whose value is `''` is a real option: it is selected and shown with its own label, distinct from the placeholder, which still maps to `null`.
|
|
321
|
+
- `Input` and `Textarea` take `counter`: with a `maxlength` attribute they show `length / maxlength` inside the field, describe the control with "12 of 40 characters" (`form.characterCount`) and announce "Character limit reached" when it is hit.
|
|
322
|
+
- `Input` takes `transform: (value: string) => string`, applied to the typed text before it reaches the model (the caret keeps its place; IME composition is transformed when it ends). `:transform="(value) => value.toUpperCase()"` enforces uppercase codes. Values set from outside are not transformed.
|
|
323
|
+
- `FormField` takes `optional`, a muted localized "Optional" marker in the label row (`form.optional`, `optionalText` overrides it), the counterpart of the required asterisk. It is hidden when the field is required.
|
|
324
|
+
- `CheckboxGroup` takes `columns` (a number): the options are laid out in a grid with that many equal columns. Unset keeps the `orientation` layout.
|
|
325
|
+
- `Combobox` with `multiple` takes `hideTags`: the selected values are not rendered as tags inside the control (the host shows them elsewhere), the placeholder stays visible and Backspace no longer removes the last value. Selection is still announced through the options.
|
|
326
|
+
- `Pagination` takes `showEdges` (first and last page buttons, `firstLabel`, `lastLabel`) and `showJump` (a typed page number, `jumpLabel`). Both are off by default. `page` is clamped to the page count only when `total` is known: while `total` is `0` or `loading` is set (it also sets `aria-busy`), the page is kept, so a server reload does not reset it.
|
|
327
|
+
- `FilterPills` can list active filters: options with `removable: true` render as active pills with a remove button (`removeLabel`, a `'Remove {label}'` template). Clicking the pill emits `select(value)`, the button emits `remove(value)` (and drops the value from the model when it is selected); focus moves to the next pill when the removed one disappears.
|
|
328
|
+
- `Calendar` takes `dayText: (iso) => string | undefined`, a short line under each day number that is also added to the day's accessible name (availability, prices). The `#day` slot (`{ date, number, text, selected, unavailable, inMonth }`) replaces the cell content; it is decorative, so announced text still comes from `dayText`.
|
|
329
|
+
- `Separator` takes `description` (or `#description`) under its label, and an `#actions` slot rendered next to them (a small add or edit button).
|
|
330
|
+
- `Modal` takes `width` (any CSS length, overrides `size`) and `minHeight` (keeps the height steady while tabs or steps change). The size widths are tokens: `--k-modal-width-sm` (26rem), `--k-modal-width-md` (32rem), `--k-modal-width-lg` (50rem), `--k-modal-width-xl` (64rem).
|
|
331
|
+
- `Tabs` items take `dot: true` for a small indicator without a number (`dotTone`, default `danger`; `dotLabel` is the hidden text, default "Needs attention").
|
|
332
|
+
- `DropdownMenu` takes `matchTriggerWidth` (the menu is at least as wide as its trigger) and `minWidth` (a CSS length).
|
|
333
|
+
- `PageHeader` takes `align` (`'end'` by default, `'start'` or `'center'`) for the title block and the actions, and `truncate` to keep the title on one line with an ellipsis (the full title is in its `title` attribute).
|
|
334
|
+
- `RichTextEditor` accepts `'underline'` in `toolbar` (not in the default toolbar).
|
|
335
|
+
- `RichTextEditor` takes `linkTypes` (`EditorLinkType[]`, `'url' | 'email' | 'phone'`, default `['url']`). With more than one type the link form shows a type select; email links get `mailto:` and phone links `tel:` (spaces and separators removed). When no text is selected the form asks for the link text too (`editor.linkText`) and inserts it as a link, falling back to the address.
|
|
336
|
+
- `BarChart` takes `colorByPoint` (every bar of a series takes the next palette color, or one from `colors: string[]`; the legend lists and toggles the categories). The palette comes from `--k-chart-1` to `--k-chart-8`.
|
|
299
337
|
- `SelectionBar` shows a plural aware selected count (announced in a live region), select all (`total` plus `@select-all`) and clear (`@clear`) actions, an `#actions` slot and `placement` `inline`, `sticky` or `floating`. With `count` 0 it renders no box (only its live region), so it can sit in a flex toolbar without adding a gap; the first count after mount is announced too. `ListPage` renders it for `#bulk`.
|
|
300
338
|
- `FilterPills` is generic: the value type comes from the options, `multiple` makes the model an array and `required` drops `null` from it (like `SegmentedControl`). Options take `menu` items (a dropdown trigger showing `valueLabel`) or `panel: true`, which opens a popover with the `#panel-<value>` slot (`{ option, active, toggle, close }`, `panelLabel` names it). The `#pill` slot renders any trigger and its scope has `defaultPill`, a component that renders the built-in pill for that option, so one custom pill does not mean rewriting the others: `<component :is="defaultPill" v-else />`. `FilterPill` is exported so custom triggers match the other pills.
|
|
301
339
|
- `SegmentedControl` with `required` narrows `v-model` to a non null value. Options take `title` (tooltip and description) and `muted`, and the `#option` slot replaces the content of each option.
|
|
@@ -412,6 +450,7 @@ Ready defaults to start a dashboard project: an app frame, an auth frame and pag
|
|
|
412
450
|
| `shortcut` | `'mod+b'` | Toggles the rail on desktop and the drawer on mobile. `searchShortcut` defaults to `mod+k`. |
|
|
413
451
|
| `v-model:open` | `false` | Mobile drawer (below 768px of shell width): scrim, focus moves into the drawer, the content is `inert`, Escape and the scrim close it and focus returns to the burger. |
|
|
414
452
|
| `fluid`, `viewTransition` | `false`, `true` | Full width content; `view-transition-name` on sidebar and panel so page transitions keep the frame still. |
|
|
453
|
+
| `fill` | `false` | Full height, full bleed content: no padding, gap or max width, and the content fills the panel (a page that is one DataGrid with `height="100%"`). Also on `AppShell`; in the `k-dashboard` layout per page with `definePageMeta({ fill: true })` or for every page with `dashboard.fill`. |
|
|
415
454
|
|
|
416
455
|
Slots: `brand`, `search`, `sidebar` (replaces the navigation), `sidebar-footer`, `account`, `topbar-brand`, `topbar`, `actions`, default, plus `settings-section-<id>`, `settings-actions-<id>` and `settings-account-fields`, forwarded to the SettingsModal. The exposed `openSettings(id?)` opens it on a section. Landmarks: skip link, `aside` and `nav` with labels, `main` with an id, `aria-current="page"` on the active link. `SidebarNav` (layout group) renders the same navigation data inside your own `Sidebar`.
|
|
417
456
|
|
|
@@ -593,7 +632,16 @@ Accessibility related props and defaults:
|
|
|
593
632
|
- 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`.
|
|
594
633
|
- `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.
|
|
595
634
|
- `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.
|
|
596
|
-
- `DataGrid`
|
|
635
|
+
- `DataGrid` sort is a model: `v-model:sort` (`{ column, direction } | null`) sets the sorted column from outside (server side sort, a sort restored from the URL) and follows header clicks. Leave it unset for an uncontrolled grid. `sortChange` still fires with `serverSort`.
|
|
636
|
+
- `DataGrid` takes `rowHeight` and `headerHeight` (pixels) for an exact row size on top of `density`, and `headerText: (field, column) => string | undefined` to name every column that has a `field` and no `headerName` (from your i18n).
|
|
637
|
+
- `DataGrid` `filterable` registers the AG Grid text, number and date filters (`registerGridFilterModules()` does it without the prop) and turns `filter` on for every column. `filterChange(model, event)` fires with `api.getFilterModel()` whenever a column filter changes, also when the filters come from `gridOptions` or your own registered modules.
|
|
638
|
+
- `DateCell` takes `relative: true` (or `Intl.RelativeTimeFormatOptions`) in `cellRendererParams` for "3 days ago"; the absolute date stays in the `title`.
|
|
639
|
+
- `DataGrid` takes `gridOptions` for any AG Grid option it does not wrap (`rowHeight`, `onCellEditingStopped`, `onFilterChanged`, ...). A prop of `DataGrid` wins only when it is set: `getRowId`, `rowSelection` (when `selectable` is off), `autoSizeStrategy` (also `undefined` to turn it off), `tooltipShowDelay`, `overlayNoRowsTemplate` and `overlayLoadingTemplate` from `gridOptions` are used otherwise; `defaultColDef` and `localeText` are merged over the component defaults, and `context` is merged under the cell context. Handlers from both run.
|
|
640
|
+
- `DataGrid` row ids: `getRowId: (params) => string` (wins), `rowIdKey` (a field of the row), or `gridOptions.getRowId`.
|
|
641
|
+
- `DataGrid` `fill` makes the grid, with its toolbar and footer, fill its parent (a flex or grid item with `flex: 1; min-height: 0`, or `height: 100%` in a block with a height) instead of the fixed `height`. Ignored with `autoHeight`.
|
|
642
|
+
- `DataGrid` `showEdges` and `showJump` reach the footer pager (first and last page buttons, page number input). `loading` also keeps the pager from resetting the page while a server total is unknown.
|
|
643
|
+
- `DataGrid` registers the AG Grid cell editors (text, large text, number, date, checkbox, select, custom and undo/redo) when `editable` is set or when a column or `gridOptions.defaultColDef` has `editable` at mount. `registerGridEditorModules()` from `/grid` does it by hand, before the first grid mounts, for columns that become editable later.
|
|
644
|
+
- `ImageCell` takes `fit` (`'cover'` default, or `'contain'` for logos) and `size` (any CSS length, default `1.75rem`) in `cellRendererParams`. `EmptyCell` (the hidden "Empty" dash) is exported from `/grid` for custom renderers.
|
|
597
645
|
- `DataGrid` translates the AG Grid ARIA strings through the catalog (`grid.*`; your own `localeText` still wins) and announces row count changes.
|
|
598
646
|
|
|
599
647
|
## Browser support
|
package/dist/chart/BarChart.vue
CHANGED
|
@@ -35,40 +35,51 @@ const props = defineProps({
|
|
|
35
35
|
horizontal: { type: Boolean, required: false },
|
|
36
36
|
patterns: { type: Boolean, required: false },
|
|
37
37
|
formatValue: { type: Function, required: false },
|
|
38
|
-
options: { type: null, required: false }
|
|
38
|
+
options: { type: null, required: false },
|
|
39
|
+
colorByPoint: { type: Boolean, required: false },
|
|
40
|
+
colors: { type: Array, required: false }
|
|
39
41
|
});
|
|
40
42
|
const { colors, reducedMotion, ready, colorAt, isHidden, toggle } = useChartSurface(
|
|
41
43
|
useTemplateRef("root"),
|
|
42
44
|
{
|
|
43
|
-
color: (index) => props.series[index]?.color,
|
|
44
|
-
key: (index) => props.series[index]?.label
|
|
45
|
+
color: (index) => props.colorByPoint ? props.colors?.[index] : props.series[index]?.color,
|
|
46
|
+
key: (index) => props.colorByPoint ? props.labels[index] : props.series[index]?.label
|
|
45
47
|
}
|
|
46
48
|
);
|
|
47
49
|
const legendItems = computed(
|
|
48
|
-
() => props.series.map((entry
|
|
49
|
-
label
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
() => (props.colorByPoint ? props.labels : props.series.map((entry) => entry.label)).map(
|
|
51
|
+
(label, index) => ({
|
|
52
|
+
label,
|
|
53
|
+
color: colorAt(index),
|
|
54
|
+
hidden: isHidden(index),
|
|
55
|
+
pattern: props.patterns ? seriesPattern(index) : void 0
|
|
56
|
+
})
|
|
57
|
+
)
|
|
58
|
+
);
|
|
59
|
+
const visibleIndexes = computed(
|
|
60
|
+
() => props.labels.map((_, index) => index).filter((index) => !props.colorByPoint || !isHidden(index))
|
|
54
61
|
);
|
|
55
62
|
function fillAt(index) {
|
|
56
63
|
const color = colorAt(index);
|
|
57
64
|
return props.patterns ? barFill(color, seriesPattern(index), colors.value.surface) : color;
|
|
58
65
|
}
|
|
59
|
-
const chartData = computed(() =>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
const chartData = computed(() => {
|
|
67
|
+
const indexes = visibleIndexes.value;
|
|
68
|
+
const pointFills = props.colorByPoint ? indexes.map(fillAt) : void 0;
|
|
69
|
+
return {
|
|
70
|
+
labels: props.colorByPoint ? indexes.map((index) => props.labels[index] ?? "") : props.labels,
|
|
71
|
+
datasets: props.series.map((entry, index) => ({
|
|
72
|
+
label: entry.label,
|
|
73
|
+
data: props.colorByPoint ? indexes.map((point) => entry.data[point] ?? null) : entry.data,
|
|
74
|
+
hidden: props.colorByPoint ? false : isHidden(index),
|
|
75
|
+
backgroundColor: pointFills ?? fillAt(index),
|
|
76
|
+
hoverBackgroundColor: pointFills ?? fillAt(index),
|
|
77
|
+
borderRadius: 4,
|
|
78
|
+
borderSkipped: false,
|
|
79
|
+
maxBarThickness: 36
|
|
80
|
+
}))
|
|
81
|
+
};
|
|
82
|
+
});
|
|
72
83
|
const chartOptions = computed(
|
|
73
84
|
() => buildCartesianOptions(
|
|
74
85
|
{
|
|
@@ -12,6 +12,8 @@ declare const __VLS_export: import("vue").DefineComponent<{
|
|
|
12
12
|
patterns?: boolean;
|
|
13
13
|
formatValue?: ChartValueFormatter;
|
|
14
14
|
options?: ChartOptions<"bar">;
|
|
15
|
+
colorByPoint?: boolean;
|
|
16
|
+
colors?: string[];
|
|
15
17
|
innerHTML?: string | undefined | undefined;
|
|
16
18
|
class?: import("vue").ClassValue;
|
|
17
19
|
style?: import("vue").StyleValue;
|
|
@@ -216,6 +218,8 @@ declare const __VLS_export: import("vue").DefineComponent<{
|
|
|
216
218
|
patterns?: boolean;
|
|
217
219
|
formatValue?: ChartValueFormatter;
|
|
218
220
|
options?: ChartOptions<"bar">;
|
|
221
|
+
colorByPoint?: boolean;
|
|
222
|
+
colors?: string[];
|
|
219
223
|
innerHTML?: string | undefined | undefined;
|
|
220
224
|
class?: import("vue").ClassValue;
|
|
221
225
|
style?: import("vue").StyleValue;
|
package/dist/chart/chart.css
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
.k-chart-panel-title {
|
|
25
25
|
margin: 0;
|
|
26
26
|
font-size: 0.9375rem;
|
|
27
|
-
font-weight: 600;
|
|
27
|
+
font-weight: var(--k-font-weight-strong, 600);
|
|
28
28
|
letter-spacing: -0.015em;
|
|
29
29
|
color: var(--k-text);
|
|
30
30
|
}
|
|
@@ -156,7 +156,7 @@
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
.k-chart-legend-item:active {
|
|
159
|
-
scale: 0.97;
|
|
159
|
+
scale: var(--k-press-scale, 0.97);
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
.k-chart-legend-item.is-hidden {
|
|
@@ -23,14 +23,14 @@ const fallbackColors = {
|
|
|
23
23
|
fontFamily: "ui-sans-serif, system-ui, sans-serif"
|
|
24
24
|
};
|
|
25
25
|
const seriesExpressions = [
|
|
26
|
-
"var(--k-accent)",
|
|
27
|
-
"var(--k-info)",
|
|
28
|
-
"var(--k-success)",
|
|
29
|
-
"var(--k-warning)",
|
|
30
|
-
"var(--k-danger)",
|
|
31
|
-
"color-mix(in oklab, var(--k-accent) 55%, var(--k-danger))",
|
|
32
|
-
"color-mix(in oklab, var(--k-info) 55%, var(--k-success))",
|
|
33
|
-
"color-mix(in oklab, var(--k-warning) 50%, var(--k-success))"
|
|
26
|
+
"var(--k-chart-1, var(--k-accent))",
|
|
27
|
+
"var(--k-chart-2, var(--k-info))",
|
|
28
|
+
"var(--k-chart-3, var(--k-success))",
|
|
29
|
+
"var(--k-chart-4, var(--k-warning))",
|
|
30
|
+
"var(--k-chart-5, var(--k-danger))",
|
|
31
|
+
"var(--k-chart-6, color-mix(in oklab, var(--k-accent) 55%, var(--k-danger)))",
|
|
32
|
+
"var(--k-chart-7, color-mix(in oklab, var(--k-info) 55%, var(--k-success)))",
|
|
33
|
+
"var(--k-chart-8, color-mix(in oklab, var(--k-warning) 50%, var(--k-success)))"
|
|
34
34
|
];
|
|
35
35
|
let normalizeContext;
|
|
36
36
|
function normalize(color) {
|
|
@@ -459,8 +459,8 @@ declare const __VLS_base: import("vue").DefineComponent<{
|
|
|
459
459
|
popovertarget?: string | undefined;
|
|
460
460
|
popovertargetaction?: import("../../index.ts").PopoverTargetAction | undefined;
|
|
461
461
|
}> & Readonly<{}>, {
|
|
462
|
-
tone: Tone;
|
|
463
462
|
size: ButtonSize;
|
|
463
|
+
tone: Tone;
|
|
464
464
|
type: "submit" | "button" | "reset";
|
|
465
465
|
variant: ButtonVariant;
|
|
466
466
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
<span v-if="isRequired && showRequiredHint" class="k-visually-hidden">{{
|
|
26
26
|
t("form.requiredHint")
|
|
27
27
|
}}</span>
|
|
28
|
+
<span v-if="optional && !isRequired" class="k-form-field-optional">{{
|
|
29
|
+
optionalText ?? t("form.optional")
|
|
30
|
+
}}</span>
|
|
28
31
|
</label>
|
|
29
32
|
<span v-if="hint || $slots.hint" :id="hintId" class="k-form-field-hint">
|
|
30
33
|
<slot name="hint">{{ hint }}</slot>
|
|
@@ -76,6 +79,8 @@ const props = defineProps({
|
|
|
76
79
|
error: { type: [String, Boolean], required: false, default: void 0 },
|
|
77
80
|
warning: { type: [String, Boolean], required: false, default: void 0 },
|
|
78
81
|
required: { type: Boolean, required: false },
|
|
82
|
+
optional: { type: Boolean, required: false },
|
|
83
|
+
optionalText: { type: String, required: false },
|
|
79
84
|
disabled: { type: Boolean, required: false },
|
|
80
85
|
icon: { type: null, required: false },
|
|
81
86
|
id: { type: String, required: false }
|
|
@@ -23,6 +23,8 @@ declare const __VLS_base: import("vue").DefineComponent<{
|
|
|
23
23
|
error?: string | false;
|
|
24
24
|
warning?: string | false;
|
|
25
25
|
required?: boolean;
|
|
26
|
+
optional?: boolean;
|
|
27
|
+
optionalText?: string;
|
|
26
28
|
disabled?: boolean;
|
|
27
29
|
icon?: IconName;
|
|
28
30
|
id?: string | undefined;
|
|
@@ -225,6 +227,8 @@ declare const __VLS_base: import("vue").DefineComponent<{
|
|
|
225
227
|
error?: string | false;
|
|
226
228
|
warning?: string | false;
|
|
227
229
|
required?: boolean;
|
|
230
|
+
optional?: boolean;
|
|
231
|
+
optionalText?: string;
|
|
228
232
|
disabled?: boolean;
|
|
229
233
|
icon?: IconName;
|
|
230
234
|
id?: string | undefined;
|
|
@@ -7,10 +7,14 @@
|
|
|
7
7
|
'has-lead': hasLead,
|
|
8
8
|
'has-trail': hasTrail,
|
|
9
9
|
'is-numeric': numeric,
|
|
10
|
-
'is-disabled': control.disabled.value
|
|
10
|
+
'is-disabled': control.disabled.value,
|
|
11
|
+
'has-counter': counterState().visible
|
|
11
12
|
}
|
|
12
13
|
]"
|
|
13
|
-
:style="
|
|
14
|
+
:style="[
|
|
15
|
+
props.style,
|
|
16
|
+
counterState().visible ? { '--k-input-counter-chars': String(counterState().width) } : {}
|
|
17
|
+
]"
|
|
14
18
|
>
|
|
15
19
|
<span v-if="hasLead" class="k-input-lead">
|
|
16
20
|
<slot name="leading"><Icon v-if="icon" :name="icon" /></slot>
|
|
@@ -29,13 +33,20 @@
|
|
|
29
33
|
:disabled="control.disabled.value || void 0"
|
|
30
34
|
:required="control.required.value || void 0"
|
|
31
35
|
:aria-invalid="control.invalid.value ? 'true' : void 0"
|
|
32
|
-
:aria-describedby="
|
|
36
|
+
:aria-describedby="
|
|
37
|
+
joinIds(
|
|
38
|
+
$attrs['aria-describedby'],
|
|
39
|
+
control.describedBy.value,
|
|
40
|
+
counterState().visible && counterState().id
|
|
41
|
+
)
|
|
42
|
+
"
|
|
33
43
|
:type="type"
|
|
34
44
|
:value="model ?? ''"
|
|
35
45
|
:readonly="readonly"
|
|
36
46
|
:class="['k-field', `k-field-${size}`, { 'is-invalid': control.invalid.value }]"
|
|
37
47
|
@input="onInput"
|
|
38
48
|
@change="onChange"
|
|
49
|
+
@compositionend="onInput"
|
|
39
50
|
/>
|
|
40
51
|
<span v-if="hasTrail" class="k-input-trail">
|
|
41
52
|
<button
|
|
@@ -50,15 +61,28 @@
|
|
|
50
61
|
</button>
|
|
51
62
|
<slot name="trailing" />
|
|
52
63
|
</span>
|
|
64
|
+
<span
|
|
65
|
+
v-if="counterState().visible"
|
|
66
|
+
class="k-field-counter k-tabular"
|
|
67
|
+
:class="{ 'is-over': counterState().atLimit }"
|
|
68
|
+
aria-hidden="true"
|
|
69
|
+
>{{ counterState().text }}</span
|
|
70
|
+
>
|
|
71
|
+
<span v-if="counterState().visible" :id="counterState().id" class="k-visually-hidden">{{
|
|
72
|
+
counterState().label
|
|
73
|
+
}}</span>
|
|
74
|
+
<LiveRegion v-if="counter" :message="counterState().limitMessage" />
|
|
53
75
|
</div>
|
|
54
76
|
</template>
|
|
55
77
|
|
|
56
78
|
<script setup>
|
|
57
|
-
import { computed, useSlots, useTemplateRef } from "vue";
|
|
79
|
+
import { computed, useAttrs, useSlots, useTemplateRef } from "vue";
|
|
58
80
|
import { useFieldControl } from "../../composables/useField";
|
|
59
81
|
import { useLocale } from "../../composables/useLocale";
|
|
60
82
|
import Icon from "./Icon.vue";
|
|
61
83
|
import { joinIds } from "../../internal/forms/control";
|
|
84
|
+
import { useCharacterCounter } from "../../internal/forms/counter";
|
|
85
|
+
import LiveRegion from "../../internal/core/LiveRegion.vue";
|
|
62
86
|
defineOptions({ inheritAttrs: false });
|
|
63
87
|
const props = defineProps({
|
|
64
88
|
type: { type: String, required: false, default: "text" },
|
|
@@ -72,6 +96,8 @@ const props = defineProps({
|
|
|
72
96
|
readonly: { type: Boolean, required: false },
|
|
73
97
|
required: { type: Boolean, required: false },
|
|
74
98
|
noAutofill: { type: Boolean, required: false },
|
|
99
|
+
counter: { type: Boolean, required: false },
|
|
100
|
+
transform: { type: Function, required: false },
|
|
75
101
|
class: { type: null, required: false },
|
|
76
102
|
style: { type: null, required: false }
|
|
77
103
|
});
|
|
@@ -81,6 +107,7 @@ const { t } = useLocale();
|
|
|
81
107
|
const clearText = computed(() => props.clearLabel ?? t("common.clear"));
|
|
82
108
|
const slots = useSlots();
|
|
83
109
|
const inputRef = useTemplateRef("inputRef");
|
|
110
|
+
const attrs = useAttrs();
|
|
84
111
|
const control = useFieldControl({
|
|
85
112
|
invalid: () => props.invalid,
|
|
86
113
|
disabled: () => props.disabled,
|
|
@@ -95,6 +122,11 @@ const showClear = computed(
|
|
|
95
122
|
);
|
|
96
123
|
const hasLead = computed(() => Boolean(props.icon || slots.leading));
|
|
97
124
|
const hasTrail = computed(() => Boolean(props.clearable || slots.trailing));
|
|
125
|
+
const counterState = useCharacterCounter({
|
|
126
|
+
enabled: () => props.counter,
|
|
127
|
+
value: () => model.value,
|
|
128
|
+
attrs
|
|
129
|
+
});
|
|
98
130
|
function parse(raw) {
|
|
99
131
|
if (modifiers.trim) raw = raw.trim();
|
|
100
132
|
if (!parsesNumber.value) return raw;
|
|
@@ -102,8 +134,30 @@ function parse(raw) {
|
|
|
102
134
|
const parsed = Number(raw.replace(",", "."));
|
|
103
135
|
return Number.isNaN(parsed) ? raw : parsed;
|
|
104
136
|
}
|
|
137
|
+
function applyTransform(target) {
|
|
138
|
+
const raw = target.value;
|
|
139
|
+
if (!props.transform) return raw;
|
|
140
|
+
const next = props.transform(raw);
|
|
141
|
+
if (next === raw) return raw;
|
|
142
|
+
const caret = target.selectionEnd;
|
|
143
|
+
target.value = next;
|
|
144
|
+
if (caret !== null) {
|
|
145
|
+
const position = Math.max(0, next.length - (raw.length - caret));
|
|
146
|
+
try {
|
|
147
|
+
target.setSelectionRange(position, position);
|
|
148
|
+
} catch {
|
|
149
|
+
return next;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return next;
|
|
153
|
+
}
|
|
105
154
|
function onInput(event) {
|
|
106
|
-
|
|
155
|
+
const target = event.target;
|
|
156
|
+
if (event.isComposing) {
|
|
157
|
+
model.value = parse(target.value);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
model.value = parse(applyTransform(target));
|
|
107
161
|
}
|
|
108
162
|
function onChange(event) {
|
|
109
163
|
if (!modifiers.trim) return;
|
|
@@ -18,6 +18,8 @@ declare const __VLS_base: import("vue").DefineComponent<{
|
|
|
18
18
|
readonly?: (boolean | "true" | "false") | undefined;
|
|
19
19
|
required?: (boolean | "true" | "false") | undefined;
|
|
20
20
|
noAutofill?: boolean;
|
|
21
|
+
counter?: boolean;
|
|
22
|
+
transform?: (value: string) => string;
|
|
21
23
|
class?: unknown;
|
|
22
24
|
style?: unknown;
|
|
23
25
|
modelValue?: string | number | null | undefined;
|
|
@@ -233,13 +235,13 @@ declare const __VLS_base: import("vue").DefineComponent<{
|
|
|
233
235
|
popovertarget?: string | undefined;
|
|
234
236
|
popovertargetaction?: import("../../index.ts").PopoverTargetAction | undefined;
|
|
235
237
|
autocomplete?: import("vue").InputAutoCompleteAttribute | undefined;
|
|
238
|
+
maxlength?: (string | number) | undefined;
|
|
239
|
+
multiple?: (boolean | "true" | "false") | undefined;
|
|
236
240
|
accept?: string | undefined | undefined;
|
|
237
241
|
alt?: string | undefined | undefined;
|
|
238
242
|
capture?: boolean | "user" | "environment" | undefined | undefined;
|
|
239
243
|
crossorigin?: string | undefined | undefined;
|
|
240
|
-
maxlength?: (string | number) | undefined;
|
|
241
244
|
minlength?: (string | number) | undefined;
|
|
242
|
-
multiple?: (boolean | "true" | "false") | undefined;
|
|
243
245
|
src?: string | undefined | undefined;
|
|
244
246
|
onCancel?: ((payload: Event) => void) | undefined;
|
|
245
247
|
}, {
|
|
@@ -261,6 +263,8 @@ declare const __VLS_base: import("vue").DefineComponent<{
|
|
|
261
263
|
readonly?: (boolean | "true" | "false") | undefined;
|
|
262
264
|
required?: (boolean | "true" | "false") | undefined;
|
|
263
265
|
noAutofill?: boolean;
|
|
266
|
+
counter?: boolean;
|
|
267
|
+
transform?: (value: string) => string;
|
|
264
268
|
class?: unknown;
|
|
265
269
|
style?: unknown;
|
|
266
270
|
modelValue?: string | number | null | undefined;
|
|
@@ -476,21 +480,21 @@ declare const __VLS_base: import("vue").DefineComponent<{
|
|
|
476
480
|
popovertarget?: string | undefined;
|
|
477
481
|
popovertargetaction?: import("../../index.ts").PopoverTargetAction | undefined;
|
|
478
482
|
autocomplete?: import("vue").InputAutoCompleteAttribute | undefined;
|
|
483
|
+
maxlength?: (string | number) | undefined;
|
|
484
|
+
multiple?: (boolean | "true" | "false") | undefined;
|
|
479
485
|
accept?: string | undefined | undefined;
|
|
480
486
|
alt?: string | undefined | undefined;
|
|
481
487
|
capture?: boolean | "user" | "environment" | undefined | undefined;
|
|
482
488
|
crossorigin?: string | undefined | undefined;
|
|
483
|
-
maxlength?: (string | number) | undefined;
|
|
484
489
|
minlength?: (string | number) | undefined;
|
|
485
|
-
multiple?: (boolean | "true" | "false") | undefined;
|
|
486
490
|
src?: string | undefined | undefined;
|
|
487
491
|
onCancel?: ((payload: Event) => void) | undefined;
|
|
488
492
|
}> & Readonly<{
|
|
489
493
|
onClear?: (() => any) | undefined;
|
|
490
494
|
"onUpdate:modelValue"?: ((value: string | number | null | undefined) => any) | undefined;
|
|
491
495
|
}>, {
|
|
492
|
-
invalid: boolean;
|
|
493
496
|
size: FieldSize;
|
|
497
|
+
invalid: boolean;
|
|
494
498
|
type: string;
|
|
495
499
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
496
500
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
3
|
class="k-textarea-box"
|
|
4
|
-
:class="[props.class, { 'has-trail': hasTrail }]"
|
|
4
|
+
:class="[props.class, { 'has-trail': hasTrail, 'has-counter': counterState().visible }]"
|
|
5
5
|
:style="props.style"
|
|
6
6
|
>
|
|
7
7
|
<textarea
|
|
@@ -17,7 +17,13 @@
|
|
|
17
17
|
:disabled="control.disabled.value || void 0"
|
|
18
18
|
:required="control.required.value || void 0"
|
|
19
19
|
:aria-invalid="control.invalid.value ? 'true' : void 0"
|
|
20
|
-
:aria-describedby="
|
|
20
|
+
:aria-describedby="
|
|
21
|
+
joinIds(
|
|
22
|
+
$attrs['aria-describedby'],
|
|
23
|
+
control.describedBy.value,
|
|
24
|
+
counterState().visible && counterState().id
|
|
25
|
+
)
|
|
26
|
+
"
|
|
21
27
|
:rows="rows"
|
|
22
28
|
:value="model ?? ''"
|
|
23
29
|
:readonly="readonly"
|
|
@@ -41,13 +47,26 @@
|
|
|
41
47
|
<span v-if="hasTrail" class="k-textarea-trail">
|
|
42
48
|
<slot name="trailing" />
|
|
43
49
|
</span>
|
|
50
|
+
<span
|
|
51
|
+
v-if="counterState().visible"
|
|
52
|
+
class="k-field-counter k-tabular"
|
|
53
|
+
:class="{ 'is-over': counterState().atLimit }"
|
|
54
|
+
aria-hidden="true"
|
|
55
|
+
>{{ counterState().text }}</span
|
|
56
|
+
>
|
|
57
|
+
<span v-if="counterState().visible" :id="counterState().id" class="k-visually-hidden">{{
|
|
58
|
+
counterState().label
|
|
59
|
+
}}</span>
|
|
60
|
+
<LiveRegion v-if="counter" :message="counterState().limitMessage" />
|
|
44
61
|
</div>
|
|
45
62
|
</template>
|
|
46
63
|
|
|
47
64
|
<script setup>
|
|
48
|
-
import { computed, nextTick, onMounted, useSlots, useTemplateRef, watch } from "vue";
|
|
65
|
+
import { computed, nextTick, onMounted, useAttrs, useSlots, useTemplateRef, watch } from "vue";
|
|
49
66
|
import { useFieldControl } from "../../composables/useField";
|
|
50
67
|
import { joinIds } from "../../internal/forms/control";
|
|
68
|
+
import { useCharacterCounter } from "../../internal/forms/counter";
|
|
69
|
+
import LiveRegion from "../../internal/core/LiveRegion.vue";
|
|
51
70
|
defineOptions({ inheritAttrs: false });
|
|
52
71
|
const props = defineProps({
|
|
53
72
|
size: { type: String, required: false, default: "md" },
|
|
@@ -60,12 +79,18 @@ const props = defineProps({
|
|
|
60
79
|
readonly: { type: Boolean, required: false },
|
|
61
80
|
required: { type: Boolean, required: false },
|
|
62
81
|
noAutofill: { type: Boolean, required: false },
|
|
82
|
+
counter: { type: Boolean, required: false },
|
|
63
83
|
class: { type: null, required: false },
|
|
64
84
|
style: { type: null, required: false }
|
|
65
85
|
});
|
|
66
86
|
const model = defineModel({ type: null });
|
|
67
87
|
const slots = useSlots();
|
|
68
88
|
const hasTrail = computed(() => Boolean(slots.trailing));
|
|
89
|
+
const counterState = useCharacterCounter({
|
|
90
|
+
enabled: () => props.counter,
|
|
91
|
+
value: () => model.value,
|
|
92
|
+
attrs: useAttrs()
|
|
93
|
+
});
|
|
69
94
|
const textareaRef = useTemplateRef("textareaRef");
|
|
70
95
|
const control = useFieldControl({
|
|
71
96
|
invalid: () => props.invalid,
|
|
@@ -14,6 +14,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
|
|
|
14
14
|
readonly?: (boolean | "true" | "false") | undefined;
|
|
15
15
|
required?: (boolean | "true" | "false") | undefined;
|
|
16
16
|
noAutofill?: boolean;
|
|
17
|
+
counter?: boolean;
|
|
17
18
|
class?: unknown;
|
|
18
19
|
style?: unknown;
|
|
19
20
|
modelValue?: string | null | undefined;
|
|
@@ -209,14 +210,14 @@ declare const __VLS_base: import("vue").DefineComponent<{
|
|
|
209
210
|
is?: string | undefined | undefined;
|
|
210
211
|
exportparts?: string | undefined;
|
|
211
212
|
part?: string | undefined;
|
|
212
|
-
popover?: (import("
|
|
213
|
+
popover?: (import("../../index.ts").PopoverMode | "" | boolean) | undefined;
|
|
213
214
|
autofocus?: (boolean | "true" | "false") | undefined;
|
|
214
215
|
autocomplete?: string | undefined | undefined;
|
|
215
216
|
maxlength?: (string | number) | undefined;
|
|
217
|
+
wrap?: string | undefined | undefined;
|
|
216
218
|
minlength?: (string | number) | undefined;
|
|
217
219
|
cols?: (string | number) | undefined;
|
|
218
220
|
dirname?: string | undefined | undefined;
|
|
219
|
-
wrap?: string | undefined | undefined;
|
|
220
221
|
}, {
|
|
221
222
|
focus: () => void | undefined;
|
|
222
223
|
el: Readonly<import("vue").ShallowRef<HTMLTextAreaElement | null>>;
|
|
@@ -233,6 +234,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
|
|
|
233
234
|
readonly?: (boolean | "true" | "false") | undefined;
|
|
234
235
|
required?: (boolean | "true" | "false") | undefined;
|
|
235
236
|
noAutofill?: boolean;
|
|
237
|
+
counter?: boolean;
|
|
236
238
|
class?: unknown;
|
|
237
239
|
style?: unknown;
|
|
238
240
|
modelValue?: string | null | undefined;
|
|
@@ -428,19 +430,19 @@ declare const __VLS_base: import("vue").DefineComponent<{
|
|
|
428
430
|
is?: string | undefined | undefined;
|
|
429
431
|
exportparts?: string | undefined;
|
|
430
432
|
part?: string | undefined;
|
|
431
|
-
popover?: (import("
|
|
433
|
+
popover?: (import("../../index.ts").PopoverMode | "" | boolean) | undefined;
|
|
432
434
|
autofocus?: (boolean | "true" | "false") | undefined;
|
|
433
435
|
autocomplete?: string | undefined | undefined;
|
|
434
436
|
maxlength?: (string | number) | undefined;
|
|
437
|
+
wrap?: string | undefined | undefined;
|
|
435
438
|
minlength?: (string | number) | undefined;
|
|
436
439
|
cols?: (string | number) | undefined;
|
|
437
440
|
dirname?: string | undefined | undefined;
|
|
438
|
-
wrap?: string | undefined | undefined;
|
|
439
441
|
}> & Readonly<{
|
|
440
442
|
"onUpdate:modelValue"?: ((value: string | null | undefined) => any) | undefined;
|
|
441
443
|
}>, {
|
|
442
|
-
invalid: boolean;
|
|
443
444
|
size: FieldSize;
|
|
445
|
+
invalid: boolean;
|
|
444
446
|
rows: number;
|
|
445
447
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
446
448
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg';
|
|
2
2
|
export type Tone = 'neutral' | 'accent' | 'info' | 'success' | 'warning' | 'danger';
|
|
3
|
-
export type ButtonVariant = 'solid' | 'soft' | 'outline' | 'ghost' | 'link';
|
|
3
|
+
export type ButtonVariant = 'solid' | 'soft' | 'subtle' | 'outline' | 'ghost' | 'link';
|
|
4
4
|
export type FieldSize = 'sm' | 'md' | 'lg';
|
|
5
5
|
export type LinkTarget = string | Record<string, unknown>;
|