feat(ui): add label prop and slot to textarea component

This commit is contained in:
upsiflu 2024-12-29 18:31:18 +01:00
parent 9e3f2cfb57
commit b2ba7e1e6a
3 changed files with 73 additions and 29 deletions

View File

@ -4,8 +4,9 @@ import { useTextareaAutosize, computedWithControl, useManualRefHistory, watchDeb
import Button from './Button.vue'
import Markdown from './Markdown.vue'
import Layout from '~/components/ui/Layout.vue'
const { max=Infinity, placeholder='' } = defineProps<{ max?:number,placeholder?:string }>()
const { max=Infinity, placeholder='', ...restProps } = defineProps<{ max?:number,placeholder?:string, label?:string }>()
const model = defineModel<string>({ default: '' })
@ -177,38 +178,48 @@ const focus = () => textarea.value.focus()
</script>
<template>
<div :class="{ 'has-preview': preview }" class="funkwhale textarea" @mousedown.prevent="focus" @mouseup.prevent="focus">
<Markdown :md="model" class="preview" />
<textarea ref="textarea" @click="updateLineNumber" @mousedown.stop @mouseup.stop @keydown.left="updateLineNumber"
@keydown.right="updateLineNumber" @keydown.up="updateLineNumber" @keydown.down="updateLineNumber"
@keydown.enter="newline" @keydown.ctrl.shift.z.exact.prevent="redo" @keydown.ctrl.z.exact.prevent="undo"
@keydown.ctrl.b.exact.prevent="bold" @keydown.ctrl.i.exact.prevent="italics"
@keydown.ctrl.shift.x.exact.prevent="strikethrough" @keydown.ctrl.k.exact.prevent="link" :maxlength="max"
:placeholder="placeholder" v-model="model" id="textarea_id" />
<div class="textarea-buttons">
<Button @click="preview = !preview" icon="bi-eye" color="secondary" :aria-pressed="preview || undefined" />
<Layout stack no-gap label
class="funkwhale textarea-label"
>
<span v-if="$slots['label']" class="label">
<slot name="label" />
</span>
<span v-if="restProps.label" class="label">
{{ restProps.label }}
</span>
<div :class="{ 'has-preview': preview }" class="funkwhale textarea" @mousedown.prevent="focus" @mouseup.prevent="focus">
<Markdown :md="model" class="preview" />
<textarea ref="textarea" @click="updateLineNumber" @mousedown.stop @mouseup.stop @keydown.left="updateLineNumber"
@keydown.right="updateLineNumber" @keydown.up="updateLineNumber" @keydown.down="updateLineNumber"
@keydown.enter="newline" @keydown.ctrl.shift.z.exact.prevent="redo" @keydown.ctrl.z.exact.prevent="undo"
@keydown.ctrl.b.exact.prevent="bold" @keydown.ctrl.i.exact.prevent="italics"
@keydown.ctrl.shift.x.exact.prevent="strikethrough" @keydown.ctrl.k.exact.prevent="link" :maxlength="max"
:placeholder="placeholder" v-model="model" id="textarea_id" />
<div class="textarea-buttons">
<Button @click="preview = !preview" icon="bi-eye" color="secondary" :aria-pressed="preview || undefined" />
<div class="separator" />
<div class="separator" />
<Button @click="heading1" icon="bi-type-h1" color="secondary" :aria-pressed="isHeading1 || undefined" :disabled="preview" />
<Button @click="heading2" icon="bi-type-h2" color="secondary" :aria-pressed="isHeading2 || undefined" :disabled="preview" />
<Button @click="paragraph" icon="bi-paragraph" color="secondary" :aria-pressed="isParagraph || undefined" :disabled="preview" />
<Button @click="quote" icon="bi-quote" color="secondary" :aria-pressed="isQuote || undefined" :disabled="preview" />
<Button @click="orderedList" icon="bi-list-ol" color="secondary" :aria-pressed="isOrderedList || undefined"
:disabled="preview" />
<Button @click="unorderedList" icon="bi-list-ul" color="secondary" :aria-pressed="isUnorderedList || undefined"
:disabled="preview" />
<Button @click="heading1" icon="bi-type-h1" color="secondary" :aria-pressed="isHeading1 || undefined" :disabled="preview" />
<Button @click="heading2" icon="bi-type-h2" color="secondary" :aria-pressed="isHeading2 || undefined" :disabled="preview" />
<Button @click="paragraph" icon="bi-paragraph" color="secondary" :aria-pressed="isParagraph || undefined" :disabled="preview" />
<Button @click="quote" icon="bi-quote" color="secondary" :aria-pressed="isQuote || undefined" :disabled="preview" />
<Button @click="orderedList" icon="bi-list-ol" color="secondary" :aria-pressed="isOrderedList || undefined"
:disabled="preview" />
<Button @click="unorderedList" icon="bi-list-ul" color="secondary" :aria-pressed="isUnorderedList || undefined"
:disabled="preview" />
<div class="separator" />
<div class="separator" />
<Button @click="bold" icon="bi-type-bold" color="secondary" :disabled="preview" />
<Button @click="italics" icon="bi-type-italic" color="secondary" :disabled="preview" />
<Button @click="strikethrough" icon="bi-type-strikethrough" color="secondary" :disabled="preview" />
<Button @click="link" icon="bi-link-45deg" color="secondary" :disabled="preview" />
<Button @click="bold" icon="bi-type-bold" color="secondary" :disabled="preview" />
<Button @click="italics" icon="bi-type-italic" color="secondary" :disabled="preview" />
<Button @click="strikethrough" icon="bi-type-strikethrough" color="secondary" :disabled="preview" />
<Button @click="link" icon="bi-link-45deg" color="secondary" :disabled="preview" />
<span v-if="max !== Infinity && typeof max === 'number'" class="letter-count">{{ max - model.length }}</span>
<span v-if="max !== Infinity && typeof max === 'number'" class="letter-count">{{ max - model.length }}</span>
</div>
</div>
</div>
</Layout>
</template>
<style lang="scss">

View File

@ -1,4 +1,12 @@
.funkwhale {
&.textarea-label {
display: block;
> .label {
padding-bottom: 4px;
font-size:14px;
font-weight:600;
}
}
&.textarea {
background-color: var(--fw-bg-color);
box-shadow: inset 0 0 0 4px var(--fw-border-color);

View File

@ -1,5 +1,6 @@
<script setup lang="ts">
import Textarea from '~/components/ui/Textarea.vue'
import { ref } from 'vue'
const text1 = ref('# Funk\nwhale')
@ -27,12 +28,12 @@ Create a textarea and attach its input to a value using a `v-model` directive.
```vue-html{2}
<Textarea
v-model="text"
v-model="text" label="My Area"
/>
```
<ClientOnly>
<Textarea v-model="text1" />
<Textarea v-model="text1" label="My Area"/>
</ClientOnly>
## Textarea max length
@ -64,3 +65,27 @@ Add a placeholder to a textarea to guide users on what they should enter by pass
<ClientOnly>
<Textarea v-model="text3" placeholder="Describe this track here…" />
</ClientOnly>
## Label slot
```vue-html{2-4}
<Textarea>
<template #label>
About my music
</template>
</Textarea>
```
<Textarea>
<template #label>
About my music <span style="color:red; float:right;">*required</span>
</template>
</Textarea>
If you just have a string, we have a convenience prop, so instead you can write:
```vue-html
<Textarea label="About my music" />
```
<Textarea label="About my music" />