Files
moeka-project/packages/ui/src/components/form/textarea/basic-text-area.vue
T
2026-09-06 16:28:01 +00:00

74 lines
1.8 KiB
Vue

<script setup lang="ts">
import { ref, watch } from 'vue'
const props = withDefaults(defineProps<{
defaultHeight?: string
submitOnEnter?: boolean
}>(), {
submitOnEnter: true,
})
const events = defineEmits<{
(event: 'submit', message: string): void
(event: 'pasteFile', files: File[]): void
}>()
const input = defineModel<string>({
default: '',
})
const textareaRef = ref<HTMLTextAreaElement>()
const textareaHeight = ref('auto')
function onKeyDown(e: KeyboardEvent) {
if (!props.submitOnEnter)
return
if (e.code === 'Enter' && !e.shiftKey) { // just block Enter is enough, Shift+Enter by default generates a newline
e.preventDefault()
events('submit', input.value)
}
}
function onPaste(e: ClipboardEvent) {
if (!e.clipboardData)
return
const { files } = e.clipboardData
if (files.length > 0) {
e.preventDefault()
events('pasteFile', Array.from(files))
}
}
// javascript - Creating a textarea with auto-resize - Stack Overflow
// https://stackoverflow.com/questions/454202/creating-a-textarea-with-auto-resize
watch(input, () => {
// An explicit baseline prevents a flex parent's minimum height from
// stretching the textarea before scrollHeight measures its content.
textareaHeight.value = props.defaultHeight || 'auto'
requestAnimationFrame(() => {
if (!textareaRef.value)
return
if (input.value === '') {
textareaHeight.value = props.defaultHeight || 'fit-content'
return
}
// scrollHeight includes padding but excludes the two 2px borders.
textareaHeight.value = `${textareaRef.value.scrollHeight + 4}px`
})
}, { immediate: true })
</script>
<template>
<textarea
ref="textareaRef"
v-model="input"
rows="1"
:style="{ height: textareaHeight }"
@keydown="onKeyDown"
@paste="onPaste"
/>
</template>