82b7215dca
Preparation for #42
77 lines
2.3 KiB
Vue
77 lines
2.3 KiB
Vue
<script setup lang="ts">
|
|
import { watch, type HTMLAttributes, useTemplateRef, onMounted } from "vue"
|
|
import { useVModel } from "@vueuse/core"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
const props = defineProps<{
|
|
class?: HTMLAttributes["class"]
|
|
defaultValue?: string
|
|
modelValue?: string
|
|
placeholder?: string
|
|
}>()
|
|
|
|
const emits = defineEmits<{
|
|
(e: "update:modelValue", payload: string | number): void
|
|
}>()
|
|
|
|
const wrapper = useTemplateRef('grow-wrap')
|
|
|
|
const modelValue = useVModel(props, "modelValue", emits, {
|
|
passive: true,
|
|
defaultValue: props.defaultValue,
|
|
})
|
|
|
|
watch(modelValue, (value) => {
|
|
if (!wrapper.value) return
|
|
wrapper.value.dataset.replicatedValue = value as string
|
|
})
|
|
|
|
onMounted(() => {
|
|
if (!wrapper.value) return
|
|
wrapper.value.dataset.replicatedValue = modelValue.value as string
|
|
})
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<div ref="grow-wrap" class="grow-wrap">
|
|
<textarea rows="1" name="text" id="text" v-model="modelValue" data-slot="textarea" :placeholder="placeholder"
|
|
:class="cn('border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm', props.class)" />
|
|
</div>
|
|
</template>
|
|
|
|
<style>
|
|
.grow-wrap {
|
|
/* easy way to plop the elements on top of each other and have them both sized based on the tallest one's height */
|
|
display: grid;
|
|
}
|
|
|
|
.grow-wrap::after {
|
|
/* Note the weird space! Needed to preventy jumpy behavior */
|
|
content: attr(data-replicated-value) " ";
|
|
|
|
/* This is how textarea text behaves */
|
|
white-space: pre-wrap;
|
|
|
|
/* Hidden from view, clicks, and screen readers */
|
|
visibility: hidden;
|
|
}
|
|
|
|
.grow-wrap>textarea {
|
|
/* You could leave this, but after a user resizes, then it ruins the auto sizing */
|
|
resize: none;
|
|
|
|
/* Firefox shows scrollbar on growth, you can hide like this. */
|
|
overflow: hidden;
|
|
}
|
|
|
|
.grow-wrap>textarea,
|
|
.grow-wrap::after {
|
|
/* Identical styling required!! */
|
|
padding: calc(var(--spacing) * 1);
|
|
font: inherit;
|
|
|
|
/* Place on top of each other */
|
|
grid-area: 1 / 1 / 2 / 2;
|
|
}
|
|
</style> |