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">
import { ref } from 'vue'
import { type ColorProps, type PastelProps, type VariantProps, type RaisedProps, color } from '~/composables/color'
const input = ref();
const emit = defineEmits<{
click: [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 model = defineModel<string>()
@ -22,7 +28,16 @@ const model = defineModel<string>()
<div v-if="!!$slots.image" class="pill-image">
<slot name="image" />
</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">
<slot />
</div>
@ -32,10 +47,15 @@ const model = defineModel<string>()
<style lang="scss">
@import './pill.scss';
label.funkwhale.pill.pill {
height: 28px;
>input {
min-width: 40px;
min-height: 28px;
white-space: pre;
cursor: text;
> span[contenteditable] {
// 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>

View File

@ -156,24 +156,32 @@ Image pills contain a small circular image on their left. These can be used for
## 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
import { computed, ref } from "vue";
const customTag = ref(" ");
const isDeleted = computed(() => customTag.value === "");
import { ref } from "vue";
const customTag = ref("Custom Tag");
```
```vue-html
<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">
<template #image>
<div style="background-color: #0004" />
</template>
</Pill>
<Pill no-underline v-model="customTag" />
<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>