@nuxt/ui 4.10.0 → 4.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.nuxt/ui.static.css +11 -11
- package/README.md +1 -0
- package/cli/commands/make/locale.mjs +40 -11
- package/cli/templates.mjs +2 -2
- package/dist/module.d.mts +1 -1
- package/dist/module.json +2 -2
- package/dist/module.mjs +5 -2
- package/dist/runtime/components/AuthForm.d.vue.ts +7 -7
- package/dist/runtime/components/AuthForm.vue.d.ts +7 -7
- package/dist/runtime/components/Button.vue +11 -2
- package/dist/runtime/components/Carousel.d.vue.ts +6 -6
- package/dist/runtime/components/Carousel.vue.d.ts +6 -6
- package/dist/runtime/components/ChatMessages.d.vue.ts +2 -2
- package/dist/runtime/components/ChatMessages.vue.d.ts +2 -2
- package/dist/runtime/components/ChatPromptSubmit.d.vue.ts +2 -2
- package/dist/runtime/components/ChatPromptSubmit.vue.d.ts +2 -2
- package/dist/runtime/components/Checkbox.d.vue.ts +1 -1
- package/dist/runtime/components/Checkbox.vue +12 -6
- package/dist/runtime/components/Checkbox.vue.d.ts +1 -1
- package/dist/runtime/components/CheckboxGroup.d.vue.ts +6 -0
- package/dist/runtime/components/CheckboxGroup.vue +10 -4
- package/dist/runtime/components/CheckboxGroup.vue.d.ts +6 -0
- package/dist/runtime/components/Editor.vue +4 -1
- package/dist/runtime/components/Error.d.vue.ts +1 -1
- package/dist/runtime/components/Error.vue.d.ts +1 -1
- package/dist/runtime/components/FileUpload.vue +17 -10
- package/dist/runtime/components/Form.vue +1 -1
- package/dist/runtime/components/Input.vue +12 -7
- package/dist/runtime/components/InputDate.vue +12 -8
- package/dist/runtime/components/InputMenu.vue +25 -11
- package/dist/runtime/components/InputNumber.vue +22 -14
- package/dist/runtime/components/InputRating.vue +10 -7
- package/dist/runtime/components/InputTags.vue +12 -7
- package/dist/runtime/components/InputTime.vue +12 -7
- package/dist/runtime/components/Link.vue +61 -7
- package/dist/runtime/components/Listbox.vue +10 -6
- package/dist/runtime/components/Modal.vue +12 -5
- package/dist/runtime/components/NavigationMenu.vue +10 -10
- package/dist/runtime/components/PinInput.vue +13 -8
- package/dist/runtime/components/Popover.vue +0 -1
- package/dist/runtime/components/PricingTable.d.vue.ts +1 -1
- package/dist/runtime/components/PricingTable.vue.d.ts +1 -1
- package/dist/runtime/components/Progress.d.vue.ts +2 -1
- package/dist/runtime/components/Progress.vue +5 -6
- package/dist/runtime/components/Progress.vue.d.ts +2 -1
- package/dist/runtime/components/ProgressGroup.d.vue.ts +91 -0
- package/dist/runtime/components/ProgressGroup.vue +99 -0
- package/dist/runtime/components/ProgressGroup.vue.d.ts +91 -0
- package/dist/runtime/components/RadioGroup.d.vue.ts +7 -1
- package/dist/runtime/components/RadioGroup.vue +14 -5
- package/dist/runtime/components/RadioGroup.vue.d.ts +7 -1
- package/dist/runtime/components/Select.d.vue.ts +2 -0
- package/dist/runtime/components/Select.vue +23 -8
- package/dist/runtime/components/Select.vue.d.ts +2 -0
- package/dist/runtime/components/SelectMenu.d.vue.ts +3 -0
- package/dist/runtime/components/SelectMenu.vue +35 -14
- package/dist/runtime/components/SelectMenu.vue.d.ts +3 -0
- package/dist/runtime/components/Slider.vue +15 -8
- package/dist/runtime/components/Splitter.d.vue.ts +266 -0
- package/dist/runtime/components/Splitter.vue +99 -0
- package/dist/runtime/components/Splitter.vue.d.ts +266 -0
- package/dist/runtime/components/Switch.vue +8 -4
- package/dist/runtime/components/Table.vue +5 -5
- package/dist/runtime/components/Textarea.vue +17 -7
- package/dist/runtime/components/Theme.d.vue.ts +1 -1
- package/dist/runtime/components/Theme.vue +4 -3
- package/dist/runtime/components/Theme.vue.d.ts +1 -1
- package/dist/runtime/components/Tree.vue +1 -1
- package/dist/runtime/components/color-mode/ColorModeSelect.vue +1 -0
- package/dist/runtime/components/content/ContentNavigation.vue +8 -1
- package/dist/runtime/components/content/ContentToc.vue +4 -1
- package/dist/runtime/components/locale/LocaleSelect.vue +1 -0
- package/dist/runtime/components/prose/CodeTree.vue +31 -18
- package/dist/runtime/composables/useComponentProps.d.ts +1 -1
- package/dist/runtime/composables/useComponentProps.js +6 -0
- package/dist/runtime/composables/useFormField.d.ts +10 -4
- package/dist/runtime/composables/useFormField.js +10 -3
- package/dist/runtime/keyframes.css +1 -1
- package/dist/runtime/locale/da.js +1 -1
- package/dist/runtime/locale/de_at.js +7 -0
- package/dist/runtime/locale/de_ch.js +1 -1
- package/dist/runtime/locale/eu.js +1 -1
- package/dist/runtime/locale/fi.js +1 -1
- package/dist/runtime/locale/he.js +1 -1
- package/dist/runtime/locale/hi.js +1 -1
- package/dist/runtime/locale/index.d.ts +2 -1
- package/dist/runtime/locale/index.js +2 -1
- package/dist/runtime/locale/is.js +1 -1
- package/dist/runtime/locale/lb.js +1 -1
- package/dist/runtime/locale/tg.d.ts +3 -0
- package/dist/runtime/locale/{tj.js → tg.js} +1 -1
- package/dist/runtime/locale/ur.js +1 -1
- package/dist/runtime/types/form.d.ts +1 -1
- package/dist/runtime/types/html.d.ts +3 -1
- package/dist/runtime/types/index.d.ts +2 -0
- package/dist/runtime/types/index.js +2 -0
- package/dist/runtime/types/theme.d.ts +2 -0
- package/dist/runtime/types/tv.d.ts +6 -2
- package/dist/runtime/utils/form.d.ts +1 -1
- package/dist/runtime/utils/form.js +3 -1
- package/dist/runtime/utils/link-keys.d.ts +1 -0
- package/dist/runtime/utils/link-keys.js +40 -0
- package/dist/runtime/utils/link.d.ts +2 -1
- package/dist/runtime/utils/link.js +2 -40
- package/dist/runtime/utils/prefetch.d.ts +5 -0
- package/dist/runtime/utils/prefetch.js +45 -0
- package/dist/runtime/utils/search.js +11 -18
- package/dist/runtime/utils/tv.d.ts +1 -1
- package/dist/runtime/utils/tv.js +103 -25
- package/dist/runtime/vue/components/Icon.vue +5 -2
- package/dist/runtime/vue/components/color-mode/ColorModeSelect.vue +1 -0
- package/dist/shared/{ui.XkBrgXFP.mjs → ui.DCJ9twx8.mjs} +597 -220
- package/dist/unplugin.d.mts +1 -1
- package/dist/unplugin.mjs +22 -6
- package/dist/vite.mjs +4 -4
- package/package.json +54 -53
- /package/dist/runtime/locale/{tj.d.ts → de_at.d.ts} +0 -0
|
@@ -12,6 +12,7 @@ import { useForwardProps } from "../composables/useForwardProps";
|
|
|
12
12
|
import { useFormField } from "../composables/useFormField";
|
|
13
13
|
import { get } from "../utils";
|
|
14
14
|
import { tv } from "../utils/tv";
|
|
15
|
+
import UIcon from "./Icon.vue";
|
|
15
16
|
const _props = defineProps({
|
|
16
17
|
as: { type: null, required: false },
|
|
17
18
|
legend: { type: String, required: false },
|
|
@@ -39,12 +40,16 @@ const slots = defineSlots();
|
|
|
39
40
|
const props = useComponentProps("radioGroup", _props);
|
|
40
41
|
const appConfig = useAppConfig();
|
|
41
42
|
const rootProps = useForwardProps(reactivePick(props, "as", "loop", "required"), emits);
|
|
42
|
-
const { emitFormChange, emitFormInput, color, name, size, highlight, id: _id, disabled, ariaAttrs } = useFormField(_props, { bind: false });
|
|
43
|
+
const { emitFormChange, emitFormInput, color: formFieldColor, name, size: formFieldSize, highlight: formFieldHighlight, id: _id, disabled: formFieldDisabled, ariaAttrs } = useFormField(_props, { bind: false });
|
|
43
44
|
const id = _id.value ?? useId();
|
|
45
|
+
const color = computed(() => formFieldColor.value ?? props.color);
|
|
46
|
+
const highlight = computed(() => formFieldHighlight.value ?? props.highlight);
|
|
47
|
+
const size = computed(() => formFieldSize.value ?? props.size);
|
|
48
|
+
const disabled = computed(() => formFieldDisabled.value ?? props.disabled);
|
|
44
49
|
const ui = computed(() => tv({ extend: theme, ...appConfig.ui?.radioGroup || {} })({
|
|
45
|
-
size: size.value
|
|
46
|
-
color: color.value
|
|
47
|
-
highlight: highlight.value
|
|
50
|
+
size: size.value,
|
|
51
|
+
color: color.value,
|
|
52
|
+
highlight: highlight.value,
|
|
48
53
|
disabled: disabled.value,
|
|
49
54
|
required: props.required,
|
|
50
55
|
orientation: props.orientation,
|
|
@@ -83,6 +88,9 @@ const normalizedItems = computed(() => {
|
|
|
83
88
|
}
|
|
84
89
|
return props.items.map(normalizeItem);
|
|
85
90
|
});
|
|
91
|
+
function labelIcon(item) {
|
|
92
|
+
return props.indicator === "hidden" ? item.icon : void 0;
|
|
93
|
+
}
|
|
86
94
|
function onUpdate(value) {
|
|
87
95
|
const event = new Event("change", { target: { value } });
|
|
88
96
|
emits("change", event);
|
|
@@ -124,7 +132,8 @@ function onUpdate(value) {
|
|
|
124
132
|
</RRadioGroupItem>
|
|
125
133
|
</div>
|
|
126
134
|
|
|
127
|
-
<div v-if="item.label || !!slots.label || (item.description || !!slots.description)" data-slot="wrapper" :class="ui.wrapper({ class: [props.ui?.wrapper, item.ui?.wrapper] })">
|
|
135
|
+
<div v-if="labelIcon(item) || (item.label || !!slots.label) || (item.description || !!slots.description)" data-slot="wrapper" :class="ui.wrapper({ class: [props.ui?.wrapper, item.ui?.wrapper] })">
|
|
136
|
+
<UIcon v-if="labelIcon(item)" :name="labelIcon(item)" data-slot="icon" :class="ui.icon({ class: [props.ui?.icon, item.ui?.icon] })" />
|
|
128
137
|
<component :is="!props.variant || props.variant === 'list' ? Label : 'p'" v-if="item.label || !!slots.label" :for="item.id" data-slot="label" :class="ui.label({ class: [props.ui?.label, item.ui?.label], disabled: item.disabled || disabled })">
|
|
129
138
|
<slot name="label" :item="item" :model-value="props.modelValue">
|
|
130
139
|
{{ item.label }}
|
|
@@ -2,6 +2,7 @@ import type { RadioGroupRootProps, RadioGroupRootEmits } from 'reka-ui';
|
|
|
2
2
|
import type { VNode } from 'vue';
|
|
3
3
|
import type { AppConfig } from '@nuxt/schema';
|
|
4
4
|
import theme from '#build/ui/radio-group';
|
|
5
|
+
import type { IconProps } from './Icon.vue';
|
|
5
6
|
import type { AcceptableValue, GetItemKeys, GetModelValue, GetModelValueEmits } from '../types/utils';
|
|
6
7
|
import type { ComponentConfig } from '../types/tv';
|
|
7
8
|
type RadioGroup = ComponentConfig<typeof theme, AppConfig, 'radioGroup'>;
|
|
@@ -11,8 +12,13 @@ export type RadioGroupItem = RadioGroupValue | {
|
|
|
11
12
|
description?: string;
|
|
12
13
|
disabled?: boolean;
|
|
13
14
|
value?: RadioGroupValue;
|
|
15
|
+
/**
|
|
16
|
+
* The icon displayed above the label when `indicator` is `hidden`.
|
|
17
|
+
* @IconifyIcon
|
|
18
|
+
*/
|
|
19
|
+
icon?: IconProps['name'];
|
|
14
20
|
class?: any;
|
|
15
|
-
ui?: Pick<RadioGroup['slots'], 'item' | 'container' | 'base' | 'indicator' | 'wrapper' | 'label' | 'description'>;
|
|
21
|
+
ui?: Pick<RadioGroup['slots'], 'item' | 'container' | 'base' | 'indicator' | 'wrapper' | 'label' | 'icon' | 'description'>;
|
|
16
22
|
[key: string]: any;
|
|
17
23
|
};
|
|
18
24
|
export interface RadioGroupProps<T extends RadioGroupItem[] = RadioGroupItem[], VK extends GetItemKeys<T> = 'value'> extends Pick<RadioGroupRootProps, 'disabled' | 'loop' | 'name' | 'required'> {
|
|
@@ -105,6 +105,8 @@ export interface SelectProps<T extends ArrayOrNested<SelectItem> = ArrayOrNested
|
|
|
105
105
|
multiple?: M & boolean;
|
|
106
106
|
/** Highlight the ring color like a focus state. */
|
|
107
107
|
highlight?: boolean;
|
|
108
|
+
/** Keep the mobile text size on all breakpoints. */
|
|
109
|
+
fixed?: boolean;
|
|
108
110
|
autofocus?: boolean;
|
|
109
111
|
autofocusDelay?: number;
|
|
110
112
|
class?: any;
|
|
@@ -3,7 +3,7 @@ import theme from "#build/ui/select";
|
|
|
3
3
|
</script>
|
|
4
4
|
|
|
5
5
|
<script setup>
|
|
6
|
-
import { useTemplateRef, computed, onMounted, toRef } from "vue";
|
|
6
|
+
import { useTemplateRef, computed, onMounted, onScopeDispose, toRef } from "vue";
|
|
7
7
|
import { SelectRoot, SelectArrow, SelectTrigger, SelectPortal, SelectContent, SelectViewport, SelectValue as RSelectValue, SelectLabel, SelectGroup, SelectItem as RSelectItem, SelectItemIndicator, SelectItemText, SelectSeparator } from "reka-ui";
|
|
8
8
|
import { defu } from "defu";
|
|
9
9
|
import { reactivePick } from "@vueuse/core";
|
|
@@ -40,6 +40,7 @@ const _props = defineProps({
|
|
|
40
40
|
modelModifiers: { type: null, required: false },
|
|
41
41
|
multiple: { type: Boolean, required: false },
|
|
42
42
|
highlight: { type: Boolean, required: false },
|
|
43
|
+
fixed: { type: Boolean, required: false },
|
|
43
44
|
autofocus: { type: Boolean, required: false },
|
|
44
45
|
autofocusDelay: { type: Number, required: false, default: 0 },
|
|
45
46
|
class: { type: null, required: false },
|
|
@@ -68,17 +69,29 @@ const portalProps = usePortal(toRef(() => props.portal));
|
|
|
68
69
|
const position = computed(() => props.content?.position ?? appConfig.ui?.select?.defaultVariants?.position ?? theme.defaultVariants?.position);
|
|
69
70
|
const contentProps = toRef(() => defu(props.content, { side: "bottom", sideOffset: 8, collisionPadding: 8, position: position.value }));
|
|
70
71
|
const arrowProps = toRef(() => defu(props.arrow, { rounded: true }));
|
|
71
|
-
const { emitFormChange, emitFormInput, emitFormBlur, emitFormFocus, size: formFieldSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField(_props);
|
|
72
|
+
const { emitFormChange, emitFormInput, emitFormBlur, emitFormFocus, size: formFieldSize, color: formFieldColor, id, name, highlight: formFieldHighlight, disabled: formFieldDisabled, ariaAttrs } = useFormField(_props);
|
|
72
73
|
const { orientation, size: fieldGroupSize } = useFieldGroup(_props);
|
|
73
|
-
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(
|
|
74
|
-
|
|
74
|
+
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(computed(() => ({
|
|
75
|
+
icon: props.icon,
|
|
76
|
+
leading: props.leading,
|
|
77
|
+
leadingIcon: props.leadingIcon,
|
|
78
|
+
trailing: props.trailing,
|
|
79
|
+
trailingIcon: props.trailingIcon ?? appConfig.ui.icons.chevronDown,
|
|
80
|
+
loading: props.loading,
|
|
81
|
+
loadingIcon: props.loadingIcon
|
|
82
|
+
})));
|
|
83
|
+
const color = computed(() => formFieldColor.value ?? props.color);
|
|
84
|
+
const highlight = computed(() => formFieldHighlight.value ?? props.highlight);
|
|
85
|
+
const size = computed(() => fieldGroupSize.value ?? formFieldSize.value ?? props.size);
|
|
86
|
+
const disabled = computed(() => formFieldDisabled.value ?? props.disabled);
|
|
75
87
|
const isItemAligned = computed(() => position.value === "item-aligned");
|
|
76
88
|
const ui = computed(() => tv({ extend: theme, ...appConfig.ui?.select || {} })({
|
|
77
|
-
color: color.value
|
|
89
|
+
color: color.value,
|
|
78
90
|
variant: props.variant,
|
|
79
|
-
size:
|
|
91
|
+
size: size.value,
|
|
80
92
|
loading: props.loading,
|
|
81
|
-
highlight: highlight.value
|
|
93
|
+
highlight: highlight.value,
|
|
94
|
+
fixed: props.fixed,
|
|
82
95
|
leading: isLeading.value || !!props.avatar || !!slots.leading,
|
|
83
96
|
trailing: isTrailing.value || !!slots.trailing,
|
|
84
97
|
fieldGroup: orientation.value,
|
|
@@ -110,11 +123,13 @@ function autoFocus() {
|
|
|
110
123
|
});
|
|
111
124
|
}
|
|
112
125
|
}
|
|
126
|
+
let autofocusTimeoutId;
|
|
113
127
|
onMounted(() => {
|
|
114
|
-
setTimeout(() => {
|
|
128
|
+
autofocusTimeoutId = setTimeout(() => {
|
|
115
129
|
autoFocus();
|
|
116
130
|
}, props.autofocusDelay);
|
|
117
131
|
});
|
|
132
|
+
onScopeDispose(() => clearTimeout(autofocusTimeoutId));
|
|
118
133
|
function onUpdate(value) {
|
|
119
134
|
if (props.modelModifiers?.trim && (typeof value === "string" || value === null || value === void 0)) {
|
|
120
135
|
value = value?.trim() ?? null;
|
|
@@ -105,6 +105,8 @@ export interface SelectProps<T extends ArrayOrNested<SelectItem> = ArrayOrNested
|
|
|
105
105
|
multiple?: M & boolean;
|
|
106
106
|
/** Highlight the ring color like a focus state. */
|
|
107
107
|
highlight?: boolean;
|
|
108
|
+
/** Keep the mobile text size on all breakpoints. */
|
|
109
|
+
fixed?: boolean;
|
|
108
110
|
autofocus?: boolean;
|
|
109
111
|
autofocusDelay?: number;
|
|
110
112
|
class?: any;
|
|
@@ -47,6 +47,7 @@ export interface SelectMenuProps<T extends ArrayOrNested<SelectMenuItem> = Array
|
|
|
47
47
|
* Whether to display the search input or not.
|
|
48
48
|
* Can be an object to pass additional props to the input.
|
|
49
49
|
* `{ placeholder: 'Search...', variant: 'none' }`{lang="ts-type"}
|
|
50
|
+
* Set `autofocus: false` to prevent the search input from being focused when the menu opens (e.g. to avoid opening the virtual keyboard on touch devices).
|
|
50
51
|
* @defaultValue true
|
|
51
52
|
*/
|
|
52
53
|
searchInput?: boolean | Omit<InputProps, 'modelValue' | 'defaultValue'>;
|
|
@@ -145,6 +146,8 @@ export interface SelectMenuProps<T extends ArrayOrNested<SelectMenuItem> = Array
|
|
|
145
146
|
multiple?: M & boolean;
|
|
146
147
|
/** Highlight the ring color like a focus state. */
|
|
147
148
|
highlight?: boolean;
|
|
149
|
+
/** Keep the mobile text size on all breakpoints. */
|
|
150
|
+
fixed?: boolean;
|
|
148
151
|
/**
|
|
149
152
|
* Determines if custom user input that does not exist in options can be added.
|
|
150
153
|
* @defaultValue false
|
|
@@ -3,7 +3,7 @@ import theme from "#build/ui/select-menu";
|
|
|
3
3
|
</script>
|
|
4
4
|
|
|
5
5
|
<script setup>
|
|
6
|
-
import { useTemplateRef, computed, ref, onMounted, toRef, toRaw, watch, nextTick } from "vue";
|
|
6
|
+
import { useTemplateRef, computed, ref, onMounted, onScopeDispose, toRef, toRaw, watch, nextTick } from "vue";
|
|
7
7
|
import { ComboboxRoot, ComboboxArrow, ComboboxAnchor, ComboboxInput, ComboboxTrigger, ComboboxCancel, ComboboxPortal, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxVirtualizer, ComboboxLabel, ComboboxSeparator, ComboboxItem, ComboboxItemIndicator, FocusScope } from "reka-ui";
|
|
8
8
|
import { useForwardProps } from "../composables/useForwardProps";
|
|
9
9
|
import { defu } from "defu";
|
|
@@ -50,6 +50,7 @@ const _props = defineProps({
|
|
|
50
50
|
modelModifiers: { type: null, required: false },
|
|
51
51
|
multiple: { type: Boolean, required: false },
|
|
52
52
|
highlight: { type: Boolean, required: false },
|
|
53
|
+
fixed: { type: Boolean, required: false },
|
|
53
54
|
createItem: { type: [Boolean, String, Object], required: false },
|
|
54
55
|
filterFields: { type: Array, required: false },
|
|
55
56
|
ignoreFilter: { type: Boolean, required: false },
|
|
@@ -89,14 +90,25 @@ const clearProps = computed(() => typeof props.clear === "object" ? props.clear
|
|
|
89
90
|
const virtualizerProps = toRef(() => {
|
|
90
91
|
if (!props.virtualize) return false;
|
|
91
92
|
return defu(typeof props.virtualize === "boolean" ? {} : props.virtualize, {
|
|
92
|
-
estimateSize: getEstimateSize(filteredItems.value,
|
|
93
|
+
estimateSize: getEstimateSize(filteredItems.value, size.value ?? "md", props.descriptionKey, !!slots["item-description"])
|
|
93
94
|
});
|
|
94
95
|
});
|
|
95
|
-
const searchInputProps = toRef(() => defu(props.searchInput, { placeholder: t("selectMenu.search"), variant: "none" }));
|
|
96
|
-
const { emitFormBlur, emitFormFocus, emitFormInput, emitFormChange, size: formFieldSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField(_props);
|
|
96
|
+
const searchInputProps = toRef(() => defu(props.searchInput, { placeholder: t("selectMenu.search"), variant: "none", fixed: props.fixed }));
|
|
97
|
+
const { emitFormBlur, emitFormFocus, emitFormInput, emitFormChange, size: formFieldSize, color: formFieldColor, id, name, highlight: formFieldHighlight, disabled: formFieldDisabled, ariaAttrs } = useFormField(_props);
|
|
97
98
|
const { orientation, size: fieldGroupSize } = useFieldGroup(_props);
|
|
98
|
-
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(
|
|
99
|
-
|
|
99
|
+
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(computed(() => ({
|
|
100
|
+
icon: props.icon,
|
|
101
|
+
leading: props.leading,
|
|
102
|
+
leadingIcon: props.leadingIcon,
|
|
103
|
+
trailing: props.trailing,
|
|
104
|
+
trailingIcon: props.trailingIcon ?? appConfig.ui.icons.chevronDown,
|
|
105
|
+
loading: props.loading,
|
|
106
|
+
loadingIcon: props.loadingIcon
|
|
107
|
+
})));
|
|
108
|
+
const color = computed(() => formFieldColor.value ?? props.color);
|
|
109
|
+
const highlight = computed(() => formFieldHighlight.value ?? props.highlight);
|
|
110
|
+
const size = computed(() => fieldGroupSize.value ?? formFieldSize.value ?? props.size);
|
|
111
|
+
const disabled = computed(() => formFieldDisabled.value ?? props.disabled);
|
|
100
112
|
const [DefineCreateItemTemplate, ReuseCreateItemTemplate] = createReusableTemplate();
|
|
101
113
|
const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate({
|
|
102
114
|
props: {
|
|
@@ -111,11 +123,12 @@ const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate({
|
|
|
111
123
|
}
|
|
112
124
|
});
|
|
113
125
|
const ui = computed(() => tv({ extend: theme, ...appConfig.ui?.selectMenu || {} })({
|
|
114
|
-
color: color.value
|
|
126
|
+
color: color.value,
|
|
115
127
|
variant: props.variant,
|
|
116
|
-
size:
|
|
128
|
+
size: size.value,
|
|
117
129
|
loading: props.loading,
|
|
118
|
-
highlight: highlight.value
|
|
130
|
+
highlight: highlight.value,
|
|
131
|
+
fixed: props.fixed,
|
|
119
132
|
leading: isLeading.value || !!props.avatar || !!slots.leading,
|
|
120
133
|
trailing: isTrailing.value || !!slots.trailing,
|
|
121
134
|
fieldGroup: orientation.value,
|
|
@@ -171,11 +184,13 @@ function autoFocus() {
|
|
|
171
184
|
});
|
|
172
185
|
}
|
|
173
186
|
}
|
|
187
|
+
let autofocusTimeoutId;
|
|
174
188
|
onMounted(() => {
|
|
175
|
-
setTimeout(() => {
|
|
189
|
+
autofocusTimeoutId = setTimeout(() => {
|
|
176
190
|
autoFocus();
|
|
177
191
|
}, props.autofocusDelay);
|
|
178
192
|
});
|
|
193
|
+
onScopeDispose(() => clearTimeout(autofocusTimeoutId));
|
|
179
194
|
function onUpdate(value) {
|
|
180
195
|
if (toRaw(props.modelValue) === value) {
|
|
181
196
|
return;
|
|
@@ -201,9 +216,10 @@ function onUpdate(value) {
|
|
|
201
216
|
}
|
|
202
217
|
}
|
|
203
218
|
const isOpen = ref(false);
|
|
219
|
+
let timeoutId;
|
|
220
|
+
onScopeDispose(() => clearTimeout(timeoutId));
|
|
204
221
|
function onUpdateOpen(value) {
|
|
205
222
|
isOpen.value = value;
|
|
206
|
-
let timeoutId;
|
|
207
223
|
if (!value) {
|
|
208
224
|
const event = new FocusEvent("blur");
|
|
209
225
|
emits("blur", event);
|
|
@@ -248,6 +264,11 @@ function isModelValueEmpty(modelValue) {
|
|
|
248
264
|
function onClear() {
|
|
249
265
|
emits("clear");
|
|
250
266
|
}
|
|
267
|
+
function onMountAutoFocus(event) {
|
|
268
|
+
if (searchInputProps.value.autofocus === false) {
|
|
269
|
+
event.preventDefault();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
251
272
|
const viewportRef = useTemplateRef("viewportRef");
|
|
252
273
|
const comboboxRootRef = useTemplateRef("comboboxRootRef");
|
|
253
274
|
watch(() => props.items, async () => {
|
|
@@ -378,7 +399,7 @@ defineExpose({
|
|
|
378
399
|
<UButton
|
|
379
400
|
as="span"
|
|
380
401
|
:icon="props.clearIcon || appConfig.ui.icons.close"
|
|
381
|
-
:size="
|
|
402
|
+
:size="size"
|
|
382
403
|
variant="link"
|
|
383
404
|
color="neutral"
|
|
384
405
|
tabindex="-1"
|
|
@@ -398,14 +419,14 @@ defineExpose({
|
|
|
398
419
|
<ComboboxPortal v-bind="portalProps">
|
|
399
420
|
<FieldGroupReset>
|
|
400
421
|
<ComboboxContent data-slot="content" :class="ui.content({ class: props.ui?.content })" v-bind="contentProps">
|
|
401
|
-
<FocusScope trapped data-slot="focusScope" :class="ui.focusScope({ class: props.ui?.focusScope })">
|
|
422
|
+
<FocusScope trapped data-slot="focusScope" :class="ui.focusScope({ class: props.ui?.focusScope })" @mount-auto-focus="onMountAutoFocus">
|
|
402
423
|
<slot name="content-top" />
|
|
403
424
|
|
|
404
425
|
<ComboboxInput v-if="!!props.searchInput" v-model="searchTerm" :display-value="() => searchTerm" as-child>
|
|
405
426
|
<UInput
|
|
406
427
|
autofocus
|
|
407
428
|
autocomplete="off"
|
|
408
|
-
:size="
|
|
429
|
+
:size="size"
|
|
409
430
|
v-bind="searchInputProps"
|
|
410
431
|
:model-modifiers="{
|
|
411
432
|
trim: props.modelModifiers?.trim
|
|
@@ -47,6 +47,7 @@ export interface SelectMenuProps<T extends ArrayOrNested<SelectMenuItem> = Array
|
|
|
47
47
|
* Whether to display the search input or not.
|
|
48
48
|
* Can be an object to pass additional props to the input.
|
|
49
49
|
* `{ placeholder: 'Search...', variant: 'none' }`{lang="ts-type"}
|
|
50
|
+
* Set `autofocus: false` to prevent the search input from being focused when the menu opens (e.g. to avoid opening the virtual keyboard on touch devices).
|
|
50
51
|
* @defaultValue true
|
|
51
52
|
*/
|
|
52
53
|
searchInput?: boolean | Omit<InputProps, 'modelValue' | 'defaultValue'>;
|
|
@@ -145,6 +146,8 @@ export interface SelectMenuProps<T extends ArrayOrNested<SelectMenuItem> = Array
|
|
|
145
146
|
multiple?: M & boolean;
|
|
146
147
|
/** Highlight the ring color like a focus state. */
|
|
147
148
|
highlight?: boolean;
|
|
149
|
+
/** Keep the mobile text size on all breakpoints. */
|
|
150
|
+
fixed?: boolean;
|
|
148
151
|
/**
|
|
149
152
|
* Determines if custom user input that does not exist in options can be added.
|
|
150
153
|
* @defaultValue false
|
|
@@ -10,6 +10,7 @@ import { reactivePick } from "@vueuse/core";
|
|
|
10
10
|
import { useAppConfig } from "#imports";
|
|
11
11
|
import { useComponentProps } from "../composables/useComponentProps";
|
|
12
12
|
import { useFormField } from "../composables/useFormField";
|
|
13
|
+
import { pick, omit } from "../utils";
|
|
13
14
|
import { tv } from "../utils/tv";
|
|
14
15
|
import UTooltip from "./Tooltip.vue";
|
|
15
16
|
const _props = defineProps({
|
|
@@ -30,11 +31,15 @@ const _props = defineProps({
|
|
|
30
31
|
minStepsBetweenThumbs: { type: Number, required: false }
|
|
31
32
|
});
|
|
32
33
|
const emits = defineEmits(["change"]);
|
|
34
|
+
defineOptions({ inheritAttrs: false });
|
|
33
35
|
const props = useComponentProps("slider", _props);
|
|
34
36
|
const modelValue = defineModel({ type: null });
|
|
35
37
|
const appConfig = useAppConfig();
|
|
36
|
-
const rootProps = useForwardProps(reactivePick(props, "as", "orientation", "min", "max", "step", "minStepsBetweenThumbs", "inverted")
|
|
37
|
-
const { id, emitFormChange, emitFormInput, size, color, name, disabled, ariaAttrs } = useFormField(_props);
|
|
38
|
+
const rootProps = useForwardProps(reactivePick(props, "as", "orientation", "min", "max", "step", "minStepsBetweenThumbs", "inverted"));
|
|
39
|
+
const { id, emitFormChange, emitFormInput, size: formFieldSize, color: formFieldColor, name, disabled: formFieldDisabled, ariaAttrs } = useFormField(_props);
|
|
40
|
+
const color = computed(() => formFieldColor.value ?? props.color);
|
|
41
|
+
const size = computed(() => formFieldSize.value ?? props.size);
|
|
42
|
+
const disabled = computed(() => formFieldDisabled.value ?? props.disabled);
|
|
38
43
|
const defaultSliderValue = computed(() => {
|
|
39
44
|
if (typeof props.defaultValue === "number") {
|
|
40
45
|
return [props.defaultValue];
|
|
@@ -53,10 +58,11 @@ const sliderValue = computed({
|
|
|
53
58
|
}
|
|
54
59
|
});
|
|
55
60
|
const thumbs = computed(() => sliderValue.value?.length ?? 1);
|
|
61
|
+
const thumbAttrs = ["aria-label", "aria-labelledby", "aria-describedby", "aria-valuetext", "aria-invalid", "aria-errormessage"];
|
|
56
62
|
const ui = computed(() => tv({ extend: theme, ...appConfig.ui?.slider || {} })({
|
|
57
63
|
disabled: disabled.value,
|
|
58
|
-
size: size.value
|
|
59
|
-
color: color.value
|
|
64
|
+
size: size.value,
|
|
65
|
+
color: color.value,
|
|
60
66
|
orientation: props.orientation
|
|
61
67
|
}));
|
|
62
68
|
function onChange(value) {
|
|
@@ -68,12 +74,13 @@ function onChange(value) {
|
|
|
68
74
|
|
|
69
75
|
<template>
|
|
70
76
|
<SliderRoot
|
|
71
|
-
v-bind="{ ...rootProps, ...ariaAttrs }"
|
|
72
77
|
:id="id"
|
|
73
78
|
v-model="sliderValue"
|
|
79
|
+
data-slot="root"
|
|
80
|
+
:role="thumbs > 1 && ($attrs['aria-label'] || $attrs['aria-labelledby']) ? 'group' : void 0"
|
|
81
|
+
v-bind="{ ...rootProps, ...thumbs > 1 ? $attrs : omit($attrs, thumbAttrs) }"
|
|
74
82
|
:name="name"
|
|
75
83
|
:disabled="disabled"
|
|
76
|
-
data-slot="root"
|
|
77
84
|
:class="ui.root({ class: [props.ui?.root, props.class] })"
|
|
78
85
|
:default-value="defaultSliderValue"
|
|
79
86
|
@update:model-value="emitFormInput()"
|
|
@@ -90,9 +97,9 @@ function onChange(value) {
|
|
|
90
97
|
disable-closing-trigger
|
|
91
98
|
v-bind="typeof props.tooltip === 'object' ? props.tooltip : {}"
|
|
92
99
|
>
|
|
93
|
-
<SliderThumb data-slot="thumb" :class="ui.thumb({ class: props.ui?.thumb })" :aria-label="thumbs
|
|
100
|
+
<SliderThumb data-slot="thumb" :class="ui.thumb({ class: props.ui?.thumb })" v-bind="{ ...thumbs === 1 ? pick($attrs, thumbAttrs) : {}, ...ariaAttrs }" :aria-label="thumbs > 1 || $attrs['aria-labelledby'] ? void 0 : $attrs['aria-label'] ?? 'Thumb'" />
|
|
94
101
|
</UTooltip>
|
|
95
|
-
<SliderThumb v-else data-slot="thumb" :class="ui.thumb({ class: props.ui?.thumb })" :aria-label="thumbs
|
|
102
|
+
<SliderThumb v-else data-slot="thumb" :class="ui.thumb({ class: props.ui?.thumb })" v-bind="{ ...thumbs === 1 ? pick($attrs, thumbAttrs) : {}, ...ariaAttrs }" :aria-label="thumbs > 1 || $attrs['aria-labelledby'] ? void 0 : $attrs['aria-label'] ?? 'Thumb'" />
|
|
96
103
|
</template>
|
|
97
104
|
</SliderRoot>
|
|
98
105
|
</template>
|