-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Expand file tree
/
Copy pathClickToEdit.vue
More file actions
127 lines (111 loc) · 2.96 KB
/
ClickToEdit.vue
File metadata and controls
127 lines (111 loc) · 2.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
<script setup lang="ts">
import { faLevelDownAlt } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { BFormInput } from "bootstrap-vue";
import { computed, ref, watch } from "vue";
interface Props {
value: string;
title?: string;
component?: string;
noSaveOnBlur?: boolean;
}
const props = defineProps<Props>();
const emit = defineEmits<{
(e: "input", value: string): void;
}>();
const clickToEditInput = ref<HTMLInputElement | null>(null);
const editable = ref(false);
const localValue = ref(props.value);
const computedValue = computed(() => props.value);
watch(
() => editable.value,
(value) => {
if (!value) {
emit("input", localValue.value);
} else {
setTimeout(() => {
clickToEditInput.value?.focus();
});
}
},
);
watch(
() => props.value,
(value) => {
if (!editable.value) {
localValue.value = value;
}
},
);
function onBlur() {
if (props.noSaveOnBlur) {
revertToOriginal();
} else {
editable.value = false;
}
}
function revertToOriginal() {
localValue.value = props.value;
editable.value = false;
}
</script>
<template>
<div v-if="editable" class="d-flex flex-gapx-1 input-icon-wrapper">
<BFormInput
id="click-to-edit-input"
ref="clickToEditInput"
v-model="localValue"
class="w-100 input-with-icon"
tabindex="0"
title="Press enter/return to save, esc to revert changes"
contenteditable
max-rows="4"
aria-label="Press enter/return to save, esc to revert changes"
@blur.prevent.stop="onBlur"
@keyup.prevent.stop.enter="editable = false"
@keyup.prevent.stop.escape="revertToOriginal"
@click.prevent.stop />
<div class="input-icon">
<FontAwesomeIcon :icon="faLevelDownAlt" class="enter-icon" />
</div>
</div>
<component
:is="props.component || 'label'"
v-else
v-g-tooltip.onoverflow
role="button"
for="click-to-edit-input"
class="click-to-edit-label text-break"
tabindex="0"
:title="computedValue || title"
@keyup.enter="editable = true"
@click.stop="editable = true">
<span v-if="computedValue">{{ computedValue }}</span>
<i v-else>{{ title }}</i>
</component>
</template>
<style scoped lang="scss">
.click-to-edit-label {
cursor: text;
&:hover > * {
text-decoration: underline;
}
}
.input-icon-wrapper {
position: relative;
}
.input-with-icon {
padding-right: 15px;
}
.input-icon {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
pointer-events: none;
}
.enter-icon {
transform: rotate(90deg); // Rotates the arrow to look like the enter/return key
opacity: 0.7;
}
</style>