wip bsky client for the web & android bbell.vt3e.cat
at main 99 lines 2.1 kB view raw
1<script setup lang="ts"> 2import { onMounted, ref, useId } from 'vue' 3 4const props = defineProps<{ 5 label?: string 6 placeholder?: string 7 rows?: number 8 error?: string 9 autoresize?: boolean 10}>() 11 12const model = defineModel<string>() 13const id = useId() 14 15const height = ref() 16 17const textareaRef = ref<HTMLTextAreaElement | null>(null) 18function resize(target: HTMLTextAreaElement) { 19 target.style.height = 'auto' 20 target.style.height = `${target.scrollHeight}px` 21 height.value = target.scrollHeight 22} 23 24function onInput(e: Event) { 25 if (!props.autoresize) return 26 resize(e.target as HTMLTextAreaElement) 27} 28 29onMounted(() => { 30 if (props.autoresize && textareaRef.value) { 31 resize(textareaRef.value) 32 } 33}) 34</script> 35 36<template> 37 <div class="input-group"> 38 <label v-if="label" :for="id" class="label">{{ label }}</label> 39 <textarea 40 ref="textareaRef" 41 :id="id" 42 v-model="model" 43 v-bind="props" 44 rows="1" 45 :style="{ height: height + 'px' }" 46 :placeholder="placeholder" 47 class="input textarea" 48 :oninput="onInput" 49 :class="{ 'has-error': error }" 50 ></textarea> 51 <span v-if="error" class="error-text">{{ error }}</span> 52 </div> 53</template> 54 55<style scoped> 56.input-group { 57 display: flex; 58 flex-direction: column; 59 gap: 0.25rem; 60 width: 100%; 61 margin-bottom: 1rem; 62} 63 64.label { 65 font-size: 0.875rem; 66 font-weight: 600; 67 color: hsl(var(--subtext1)); 68 margin-left: 0.25rem; 69} 70 71textarea { 72 width: 100%; 73 border-radius: 0.75rem; 74 border: 1px solid transparent; 75 background-color: hsla(var(--surface0) / 0.3); 76 border: 1px solid hsla(var(--surface2) / 0.5); 77 color: hsl(var(--text)); 78 font-size: 1rem; 79 font-family: inherit; 80 resize: none; 81 82 box-sizing: border-box; /* usually default, but good to be sure */ 83 line-height: 1.5; /* or whatever fits your design system */ 84 min-height: 0; /* overrides some user-agent defaults */ 85 overflow: hidden; /* hides the scrollbar jumping */ 86 87 &:focus-visible { 88 background-color: hsla(var(--base) / 0.9); 89 } 90 &.has-error { 91 border-color: hsl(var(--red)); 92 } 93} 94.error-text { 95 font-size: 0.75rem; 96 color: hsl(var(--red)); 97 margin-left: 0.25rem; 98} 99</style>