import '../../styles/styles.scss' import { defineComponent, onMounted, onUnmounted, type PropType, ref, type Ref, } from 'vue' /** * Многострочное поле ввода данных * * */ export default defineComponent({ props: { /** * Позволяет автоматически изменять высоту компонента, если контент в нем не помещается */ autoresize: { type: Boolean, default: false, }, /** * Помечает поле заблокированным \ * Так же скрывает ошибки, отображаемые снизу поля */ isDisabled: { type: Boolean, default: false, }, /** * Помечает поле обязательным \ * Так же добавляет звездочку (`*`) рядом с названием поля */ isRequired: { type: Boolean, default: false, }, /** * Название поля, отображающееся сверху него */ label: { type: String, default: '', }, /** * Полупрозрачный текст, отображающийся, когда значение поля пустое */ placeholder: { type: String, default: '', }, /** * Описание поля, отображающееся под полем */ description: { type: String, default: '', }, /** * Отключает возможность изменения размера компонента */ disableResize: { type: Boolean, default: false, }, /** * Значение поля */ modelValue: { type: String, default: '', }, /** * Ошибки поля \ * Можно передать массив строк, для отображения нескольких ошибок, либо передать строку, для отображения одной ошибки */ errors: { type: [Array, String] as PropType, default: () => [], }, /** * Хук, запускающийся после ввода символа в поле \ * Принимает в себя событие `DOM` хука `onInput` * * ! Внимание: при его определении, необходимо вручную менять значение поля, потому что пропс `onValueChange` не используется */ onInput: { type: Function as PropType<(e: Event) => void>, }, /** * Хук, запускающийся после деактивации фокуса в поле \ * Принимает в себя событие `DOM` хука `onFocusout` */ onFocusOut: { type: Function as PropType<(e: Event) => void>, default: () => { // do nothing }, }, /** * `Callback` для изменения значения в поле \ * Принимает в себя новое значение, введенное в поле */ onValueChange: { type: Function as PropType<(v: string) => void>, }, }, setup(props) { let isResizing = false const isWithScroll = ref(false) let startResizeX = 0 let startResizeY = 0 let startResizeWidth = 0 let startResizeHeight = 0 const element: Ref = ref(undefined) const resizerElement: Ref = ref(undefined) const checkScroll = () => { if (!element.value) return isWithScroll.value = element.value.scrollHeight > element.value.clientHeight } const doResize = () => { if (element.value && props.autoresize) { if (element.value.scrollHeight > element.value.clientHeight) element.value.style.height = `${element.value.scrollHeight + 10}px` } } const inputHandler = (e: Event) => { if (props.onValueChange) { props.onValueChange((e.target as HTMLInputElement).value) } doResize() } const onInput = (e: Event) => { if (props.onInput) props.onInput(e) else inputHandler(e) checkScroll() } const resizeStart = (e: MouseEvent) => { e.preventDefault() if (!element.value) return isResizing = true startResizeX = e.clientX startResizeY = e.clientY startResizeWidth = element.value.offsetWidth startResizeHeight = element.value.offsetHeight } const resize = (e: MouseEvent) => { if (!isResizing || !element.value) return const deltaX = e.clientX - startResizeX const deltaY = e.clientY - startResizeY element.value.style.width = `${startResizeWidth + deltaX}px` element.value.style.height = `${startResizeHeight + deltaY}px` checkScroll() } const resizeEnd = () => { isResizing = false } onMounted(() => { doResize() checkScroll() window.addEventListener('resize', checkScroll) window.addEventListener('mousemove', resize) window.addEventListener('mouseup', resizeEnd) // На случай, если курсор мыши вышел из области `resizerElement` }) onUnmounted(() => { window.removeEventListener('resize', checkScroll) window.removeEventListener('mousemove', resize) window.removeEventListener('mouseup', resizeEnd) }) return (): JSX.Element => (
{props.label && (
{ props.label }{ props.isRequired ? '*' : '' }
)}