123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- 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 { VDefaultsProvider } from "../VDefaultsProvider/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();
- }
- }
- function onKeydownClear(e) {
- if (e.key !== 'Enter' && e.key !== ' ') return;
- e.preventDefault();
- e.stopPropagation();
- props['onClick:clear']?.(new MouseEvent('click'));
- }
- 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()]
- }), hasLabel.value && _createVNode(VFieldLabel, {
- "key": "label",
- "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();
- }
- }, [_createVNode(VDefaultsProvider, {
- "defaults": {
- VIcon: {
- icon: props.clearIcon
- }
- }
- }, {
- default: () => [slots.clear ? slots.clear({
- ...slotProps.value,
- props: {
- onKeydown: onKeydownClear,
- onFocus: focus,
- onBlur: blur,
- onClick: props['onClick:clear']
- }
- }) : _createVNode(InputIcon, {
- "name": "clear",
- "onKeydown": onKeydownClear,
- "onFocus": focus,
- "onBlur": blur
- }, 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
|