import { mergeProps as _mergeProps, Fragment as _Fragment, withDirectives as _withDirectives, vShow as _vShow, resolveDirective as _resolveDirective, createVNode as _createVNode } from "vue"; // Styles import "./VField.css"; // Components import { VFieldLabel } from "./VFieldLabel.mjs"; import { VExpandXTransition } from "../transitions/index.mjs"; import { useInputIcon } from "../VInput/InputIcon.mjs"; // Composables import { useBackgroundColor, useTextColor } from "../../composables/color.mjs"; import { makeComponentProps } from "../../composables/component.mjs"; import { makeFocusProps, useFocus } from "../../composables/focus.mjs"; import { IconValue } from "../../composables/icons.mjs"; import { LoaderSlot, makeLoaderProps, useLoader } from "../../composables/loader.mjs"; import { useRtl } from "../../composables/locale.mjs"; import { makeRoundedProps, useRounded } from "../../composables/rounded.mjs"; import { makeThemeProps, provideTheme } from "../../composables/theme.mjs"; // Utilities import { computed, ref, toRef, watch } from 'vue'; import { animate, convertToUnit, EventProp, genericComponent, getUid, isOn, nullifyTransforms, pick, propsFactory, standardEasing, useRender } from "../../util/index.mjs"; // Types const allowedVariants = ['underlined', 'outlined', 'filled', 'solo', 'solo-inverted', 'solo-filled', 'plain']; export const makeVFieldProps = propsFactory({ appendInnerIcon: IconValue, bgColor: String, clearable: Boolean, clearIcon: { type: IconValue, default: '$clear' }, active: Boolean, centerAffix: { type: Boolean, default: undefined }, color: String, baseColor: String, dirty: Boolean, disabled: { type: Boolean, default: null }, error: Boolean, flat: Boolean, label: String, persistentClear: Boolean, prependInnerIcon: IconValue, reverse: Boolean, singleLine: Boolean, variant: { type: String, default: 'filled', validator: v => allowedVariants.includes(v) }, 'onClick:clear': EventProp(), 'onClick:appendInner': EventProp(), 'onClick:prependInner': EventProp(), ...makeComponentProps(), ...makeLoaderProps(), ...makeRoundedProps(), ...makeThemeProps() }, 'VField'); export const VField = genericComponent()({ name: 'VField', inheritAttrs: false, props: { id: String, ...makeFocusProps(), ...makeVFieldProps() }, emits: { 'update:focused': focused => true, 'update:modelValue': value => true }, setup(props, _ref) { let { attrs, emit, slots } = _ref; const { themeClasses } = provideTheme(props); const { loaderClasses } = useLoader(props); const { focusClasses, isFocused, focus, blur } = useFocus(props); const { InputIcon } = useInputIcon(props); const { roundedClasses } = useRounded(props); const { rtlClasses } = useRtl(); const isActive = computed(() => props.dirty || props.active); const hasLabel = computed(() => !props.singleLine && !!(props.label || slots.label)); const uid = getUid(); const id = computed(() => props.id || `input-${uid}`); const messagesId = computed(() => `${id.value}-messages`); const labelRef = ref(); const floatingLabelRef = ref(); const controlRef = ref(); const isPlainOrUnderlined = computed(() => ['plain', 'underlined'].includes(props.variant)); const { backgroundColorClasses, backgroundColorStyles } = useBackgroundColor(toRef(props, 'bgColor')); const { textColorClasses, textColorStyles } = useTextColor(computed(() => { return props.error || props.disabled ? undefined : isActive.value && isFocused.value ? props.color : props.baseColor; })); watch(isActive, val => { if (hasLabel.value) { const el = labelRef.value.$el; const targetEl = floatingLabelRef.value.$el; requestAnimationFrame(() => { const rect = nullifyTransforms(el); const targetRect = targetEl.getBoundingClientRect(); const x = targetRect.x - rect.x; const y = targetRect.y - rect.y - (rect.height / 2 - targetRect.height / 2); const targetWidth = targetRect.width / 0.75; const width = Math.abs(targetWidth - rect.width) > 1 ? { maxWidth: convertToUnit(targetWidth) } : undefined; const style = getComputedStyle(el); const targetStyle = getComputedStyle(targetEl); const duration = parseFloat(style.transitionDuration) * 1000 || 150; const scale = parseFloat(targetStyle.getPropertyValue('--v-field-label-scale')); const color = targetStyle.getPropertyValue('color'); el.style.visibility = 'visible'; targetEl.style.visibility = 'hidden'; animate(el, { transform: `translate(${x}px, ${y}px) scale(${scale})`, color, ...width }, { duration, easing: standardEasing, direction: val ? 'normal' : 'reverse' }).finished.then(() => { el.style.removeProperty('visibility'); targetEl.style.removeProperty('visibility'); }); }); } }, { flush: 'post' }); const slotProps = computed(() => ({ isActive, isFocused, controlRef, blur, focus })); function onClick(e) { if (e.target !== document.activeElement) { e.preventDefault(); } } useRender(() => { const isOutlined = props.variant === 'outlined'; const hasPrepend = slots['prepend-inner'] || props.prependInnerIcon; const hasClear = !!(props.clearable || slots.clear); const hasAppend = !!(slots['append-inner'] || props.appendInnerIcon || hasClear); const label = () => slots.label ? slots.label({ ...slotProps.value, label: props.label, props: { for: id.value } }) : props.label; return _createVNode("div", _mergeProps({ "class": ['v-field', { 'v-field--active': isActive.value, 'v-field--appended': hasAppend, 'v-field--center-affix': props.centerAffix ?? !isPlainOrUnderlined.value, 'v-field--disabled': props.disabled, 'v-field--dirty': props.dirty, 'v-field--error': props.error, 'v-field--flat': props.flat, 'v-field--has-background': !!props.bgColor, 'v-field--persistent-clear': props.persistentClear, 'v-field--prepended': hasPrepend, 'v-field--reverse': props.reverse, 'v-field--single-line': props.singleLine, 'v-field--no-label': !label(), [`v-field--variant-${props.variant}`]: true }, themeClasses.value, backgroundColorClasses.value, focusClasses.value, loaderClasses.value, roundedClasses.value, rtlClasses.value, props.class], "style": [backgroundColorStyles.value, props.style], "onClick": onClick }, attrs), [_createVNode("div", { "class": "v-field__overlay" }, null), _createVNode(LoaderSlot, { "name": "v-field", "active": !!props.loading, "color": props.error ? 'error' : typeof props.loading === 'string' ? props.loading : props.color }, { default: slots.loader }), hasPrepend && _createVNode("div", { "key": "prepend", "class": "v-field__prepend-inner" }, [props.prependInnerIcon && _createVNode(InputIcon, { "key": "prepend-icon", "name": "prependInner" }, null), slots['prepend-inner']?.(slotProps.value)]), _createVNode("div", { "class": "v-field__field", "data-no-activator": "" }, [['filled', 'solo', 'solo-inverted', 'solo-filled'].includes(props.variant) && hasLabel.value && _createVNode(VFieldLabel, { "key": "floating-label", "ref": floatingLabelRef, "class": [textColorClasses.value], "floating": true, "for": id.value, "style": textColorStyles.value }, { default: () => [label()] }), _createVNode(VFieldLabel, { "ref": labelRef, "for": id.value }, { default: () => [label()] }), slots.default?.({ ...slotProps.value, props: { id: id.value, class: 'v-field__input', 'aria-describedby': messagesId.value }, focus, blur })]), hasClear && _createVNode(VExpandXTransition, { "key": "clear" }, { default: () => [_withDirectives(_createVNode("div", { "class": "v-field__clearable", "onMousedown": e => { e.preventDefault(); e.stopPropagation(); } }, [slots.clear ? slots.clear() : _createVNode(InputIcon, { "name": "clear" }, null)]), [[_vShow, props.dirty]])] }), hasAppend && _createVNode("div", { "key": "append", "class": "v-field__append-inner" }, [slots['append-inner']?.(slotProps.value), props.appendInnerIcon && _createVNode(InputIcon, { "key": "append-icon", "name": "appendInner" }, null)]), _createVNode("div", { "class": ['v-field__outline', textColorClasses.value], "style": textColorStyles.value }, [isOutlined && _createVNode(_Fragment, null, [_createVNode("div", { "class": "v-field__outline__start" }, null), hasLabel.value && _createVNode("div", { "class": "v-field__outline__notch" }, [_createVNode(VFieldLabel, { "ref": floatingLabelRef, "floating": true, "for": id.value }, { default: () => [label()] })]), _createVNode("div", { "class": "v-field__outline__end" }, null)]), isPlainOrUnderlined.value && hasLabel.value && _createVNode(VFieldLabel, { "ref": floatingLabelRef, "floating": true, "for": id.value }, { default: () => [label()] })])]); }); return { controlRef }; } }); // TODO: this is kinda slow, might be better to implicitly inherit props instead export function filterFieldProps(attrs) { const keys = Object.keys(VField.props).filter(k => !isOn(k) && k !== 'class' && k !== 'style'); return pick(attrs, keys); } //# sourceMappingURL=VField.mjs.map