よく見かけるけど自分で作ると難しかった。
仕様
- 表示モードと編集モードがある
- 表示モードの時に文字列が1行を超える場合は...で省略される
- 表示モードの時にダブルクリックすると編集モードに切り替わる
- 編集モードに切り替わると同時に入力欄がフォーカスされる
- 編集モードの時にEnterキーを押すと適切な入力内容の時のみ表示モードに切り替わる
コード
EditableText.vue
<template>
<span
v-if="!isEditing"
@dblclick="startEdit"
>{{ modelValue }}</span>
<input
v-else
v-model="text"
@keydown.enter="endEdit($event.isComposing)"
ref="input"
>
</template>
<style scoped>
span {
border-bottom: 1px solid transparent;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
span:hover {
cursor: pointer;
}
input {
border-bottom: 1px solid;
outline: none;
}
</style>
<script setup lang="ts">
import { nextTick, ref } from 'vue'
const props = defineProps<{
modelValue: string
validator?: (value: string) => boolean
}>()
const emits = defineEmits<{
(e: 'update:modelValue', modelValue: string): void
}>()
const isEditing = ref(false)
const text = ref(props.modelValue)
const input = ref()
async function startEdit() {
isEditing.value = true
await nextTick()
input.value.focus()
}
function endEdit(isComposing: boolean) {
if(isComposing || text.value === "") return
if(props.validator && !props.validator(text.value)) return
emits('update:modelValue', text.value)
isEditing.value = false
}
</script>
使い方
Sample.vue
<template>
<editable-text v-model="text" :validator="isInteger" />
</template>
<script setup>
import { ref } from 'vue'
const text = ref("123")
function isInteger(value: string): boolean {
return Number.isInteger(Number(value))
}
</script>