import { computed, type ComputedRef, defineComponent, type PropType, ref, type Ref, watch, onMounted, } from 'vue' import { copyTooltip } from '../../helpers' import './styles.scss' import '../../styles/styles.scss' import EyeHide from './assets/eye-hide' import EyeShow from './assets/eye-show' import BlackTooltip from '../tooltips/BlackTooltip.vue' /** * Поле ввода данных. Дает возможность указать значение с помощью клавиатуры. Предназначен для короткого однострочного содержимого. * * */ export default defineComponent({ props: { /** * Помечает поле обязательным \ * Так же добавляет звездочку (`*`) рядом с названием поля */ isRequired: { type: Boolean, default: false, }, /** * Помечает поле заблокированным \ * Так же скрывает ошибки, отображаемые снизу поля */ isDisabled: { type: Boolean, default: false, }, /** * Делает поле числовым типом */ isForNumber: { type: Boolean, default: false, }, /** * Делает поле подходящим для использования паролей \ * Добавляет кнопку показа/скрытия текста в поле */ isForPassword: { type: Boolean, default: false, }, /** * Позволяет копировать текст поля при клике по нему */ isWithCopy: { type: Boolean, default: false, }, /** * Позволяет отображать `tooltip` (подсказку) рядом с полем \ * Подсказка отображается только тогда, когда текст в поле выходит за его рамки */ isShowTooltip: { type: Boolean, default: false, }, /** * Сброс значения */ isResettable: { type: Boolean, default: false, }, /** * Название поля, отображающееся сверху него */ label: { type: String, default: '', }, /** * Позволяет установить максимальную длину символов в тексте поля */ maxLength: { type: Number, }, /** * Полупрозрачный текст, отображающийся, когда значение поля пустое */ placeholder: { type: String, default: '', }, /** * Описание поля, отображающееся под полем */ description: { type: String, default: '', }, /** * Отображение пользовательского элемента слева */ leftSlot: { type: Function as PropType<() => JSX.Element>, default: undefined, }, /** * Отображение пользовательского элемента справа */ rightSlot: { type: Function as PropType<() => JSX.Element>, default: undefined, }, /** * Значение поля */ modelValue: { type: String, default: '', }, /** * Ошибки поля \ * Можно передать массив строк, для отображения нескольких ошибок, либо передать строку, для отображения одной ошибки */ errors: { type: [Array, String] as PropType, default: () => [], }, /** * Хук, запускающийся после ввода символа в поле \ * Принимает в себя событие `DOM` хука `onInput` * * ! Внимание: при его определении, необходимо вручную менять значение поля, потому что пропс `onValueChange` не используется */ onInput: { type: Function as PropType<(e: Event) => void>, }, /** * Хук, запускающийся после активации фокуса в поле \ * Принимает в себя событие `DOM` хука `onFocusin` */ onFocusIn: { type: Function as PropType<(e: Event) => void>, default: () => { // do nothing }, }, /** * Хук, запускающийся после деактивации фокуса в поле \ * Принимает в себя событие `DOM` хука `onFocusout` */ onFocusOut: { type: Function as PropType<(e: Event) => void>, default: () => { // do nothing }, }, /** * `Callback` для изменения значения в поле \ * Принимает в себя новое значение, введенное в поле */ onValueChange: { type: Function as PropType<(v: string) => void>, }, /** * `Callback` для слежения и мутации значения в инпуте * Принимает текущее значение, введенное в поле */ mask: { type: Function as PropType<(v: string) => string>, }, }, setup(props) { const isPassword: Ref = ref(props.isForPassword) const isFieldValueMoreThanFieldWidth = ref(false) const modelValue = ref(props.modelValue) const element = ref(null) as unknown as Ref const type: ComputedRef = computed(() => { if (isPassword.value) return 'password' if (props.isForNumber) return 'number' return 'text' }) const changeIsPassword = (value: boolean) => { isPassword.value = value } const inputHandler = (e: Event) => { const element = (e.target as HTMLInputElement) const text = element.value if (props.mask) element.value = text modelValue.value = props.mask ? props.mask(text) : text } const copy = (text: string, event: MouseEvent) => { if (!props.isWithCopy) return copyTooltip(text, event.clientX + 10, event.clientY - 30) } const onFocusIn = (e: FocusEvent) => { props.onFocusIn(e) setTimeout(() => { const $input = e.target as HTMLInputElement $input.selectionStart = $input.value.length $input.scrollLeft = $input?.scrollWidth }, 100) } const checkInputSize = () => { isFieldValueMoreThanFieldWidth.value = element.value?.scrollWidth > element.value?.clientWidth } const onFocusOut = (e: FocusEvent) => { props.onFocusOut(e) checkInputSize() } watch(() => props.modelValue, (value) => { modelValue.value = value }) watch(modelValue, (value) => { if (props.mask) modelValue.value = props.mask(value) if (props.onValueChange) props.onValueChange(modelValue.value) }) onMounted(checkInputSize) return (): JSX.Element => { const inputElement = () => ( ) return (
{props.label && (
{ props.label }{ props.isRequired ? '*' : '' }
)} { props.isShowTooltip && isFieldValueMoreThanFieldWidth.value && props.modelValue ? ( { inputElement() } ) : inputElement() } { !props.isDisabled && ( <> { typeof props.errors === 'string' && props.errors.length > 0 ?
{ props.errors }
: Array.isArray(props.errors) && props.errors.length > 0 ? props.errors.map((error) =>
{ error }
) : props.description ?
{ props.description }
: '' } ) }
) } }, })