refactor(ui): user can edit pill content

This commit is contained in:
upsiflu 2025-01-29 22:21:14 +01:00
parent f48b7c59d5
commit e5371cddaf
2 changed files with 44 additions and 16 deletions

View File

@ -1,11 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'
import { type ColorProps, type PastelProps, type VariantProps, type RaisedProps, color } from '~/composables/color' import { type ColorProps, type PastelProps, type VariantProps, type RaisedProps, color } from '~/composables/color'
const input = ref();
const emit = defineEmits<{ const emit = defineEmits<{
click: [event: MouseEvent] click: [event: MouseEvent]
}>() }>()
const handleClick = (event: MouseEvent) => { const handleClick = (event: MouseEvent) => {
emit('click', event) emit('click', event);
if (model.value !== undefined) {
(input.value as HTMLInputElement).focus();
}
} }
const props = defineProps<{ noUnderline?:true } & (PastelProps | ColorProps) & VariantProps & RaisedProps>() const props = defineProps<{ noUnderline?:true } & (PastelProps | ColorProps) & VariantProps & RaisedProps>()
const model = defineModel<string>() const model = defineModel<string>()
@ -22,7 +28,16 @@ const model = defineModel<string>()
<div v-if="!!$slots.image" class="pill-image"> <div v-if="!!$slots.image" class="pill-image">
<slot name="image" /> <slot name="image" />
</div> </div>
<input v-if="model" class="pill-content" v-model="model" style="border-radius:16px; min-width: max-content; width:min-content;" /> <!-- TODO: Sanitize text on blur? -->
<span contenteditable
v-if="model !== undefined"
ref="input"
class="pill-content"
@keydown.enter="(e) => (e.target as HTMLInputElement).blur()"
@blur="(e) => model = (e.target as HTMLInputElement).innerText.trim()"
>
{{ model }}
</span>
<div class="pill-content" v-if="!!$slots.default"> <div class="pill-content" v-if="!!$slots.default">
<slot /> <slot />
</div> </div>
@ -32,10 +47,15 @@ const model = defineModel<string>()
<style lang="scss"> <style lang="scss">
@import './pill.scss'; @import './pill.scss';
label.funkwhale.pill.pill { label.funkwhale.pill.pill {
height: 28px; min-width: 40px;
>input { min-height: 28px;
white-space: pre;
cursor: text;
> span[contenteditable] {
// Note that <Input>s can't be styled with `min-width` or `width` directly. // Note that <Input>s can't be styled with `min-width` or `width` directly.
width: 120px; // SOLUTION: Contenteditable. Everything else is a dirty hack.
border-radius:16px;
outline: 1px solid transparent;
} }
} }
</style> </style>

View File

@ -156,24 +156,32 @@ Image pills contain a small circular image on their left. These can be used for
## Editable pill ## Editable pill
Add `v-model="..."` to link the pill content to a `ref`. Note that the pill is not rendered if v-model is ''. Add `v-model="..."` to link the pill content to a `ref`.
```ts ```ts
import { computed, ref } from "vue"; import { ref } from "vue";
const customTag = ref(" "); const customTag = ref("Custom Tag");
const isDeleted = computed(() => customTag.value === "");
``` ```
```vue-html ```vue-html
<Pill v-model="customTag" /> <Pill v-model="customTag" />
<Button primary v-if="isDeleted" :onClick="() => customTag=' '">New Pill</Button> <Button primary low-height
:disabled="customTag === ''"
:onClick="() => customTag = ''"
>
Reset: {{ customTag }}
</Button>
``` ```
<Pill v-model="customTag"> <Pill no-underline v-model="customTag" />
<template #image>
<div style="background-color: #0004" />
</template>
</Pill>
<Button primary v-if="isDeleted" :onClick="() => customTag=' '">New Pill</Button> Edit the text, then hit Enter or click outside. The button will show the updated text.
<Button primary low-height
:disabled="customTag === ''"
:onClick="() => customTag = ''"
>
Reset: {{ customTag }}
</Button>