Files
galaxy/client/src/components/ClickToEdit.vue
T

125 lines
3.4 KiB
Vue

<template>
<div class="clickToEdit" :class="{ empty: !value.length }">
<component :is="tagName" class="m-0" v-if="!editing" @click="toggleEdit(true)">
<span class="editable"></span>
<span>{{ displayValue }}</span>
<slot name="tooltip" :editing="editing" :localValue="localValue"></slot>
</component>
<component :is="tagName" v-if="editing">
<slot
:toggleEdit="toggleEdit"
:editing="editing"
:placeholder="placeholder"
:stateValidator="stateValidator">
<debounced-input v-model="localValue" :delay="debounceDelay">
<template v-slot="{ value: debouncedValue, input }">
<b-form-input
ref="clickToEditInput"
:value="debouncedValue"
@input="input"
@blur="toggleEdit(false)"
:autofocus="true"
:placeholder="placeholder"
:state="stateValidator(debouncedValue, localValue)" />
</template>
</debounced-input>
</slot>
</component>
</div>
</template>
<script>
import DebouncedInput from "./DebouncedInput";
const defaultStateValidator = (val, origVal) => {
if (val === origVal) {
return null;
}
return val.length > 0;
};
export default {
components: {
DebouncedInput,
},
props: {
value: { type: String, required: true },
tagName: { type: String, required: false, default: "h2" },
placeholder: { type: String, required: false, default: "" },
stateValidator: { type: Function, required: false, default: defaultStateValidator },
debounceDelay: { type: Number, required: false, default: 1000 },
},
data() {
return {
editing: false,
};
},
computed: {
displayValue() {
return this.localValue || this.placeholder;
},
localValue: {
get() {
return this.value;
},
set(newVal, oldVal) {
if (newVal !== oldVal) {
this.$emit("input", newVal);
// closes click to edit if it didn't close on its own
this.$refs["clickToEditInput"].blur();
}
},
},
},
methods: {
toggleEdit(forceVal) {
this.editing = forceVal !== undefined ? forceVal : !this.editing;
},
},
};
</script>
<style lang="scss" scoped>
@import "theme/blue.scss";
@import "~scss/mixins.scss";
.clickToEdit {
position: relative;
&:hover .editable {
@include fontawesome($fa-var-edit);
position: absolute;
top: 0;
right: 0;
color: $brand-info;
font-size: 0.8rem;
}
/* changes placeholder text, it's a brittle and ugly selector
but bootstrap-vue doesn't give us much to work with */
&.empty > p > span {
color: adjust-color($text-color, $alpha: -0.6);
font-style: italic;
}
}
h1 input {
font-size: $h1-font-size;
font-weight: 500;
}
h2 input {
font-size: $h2-font-size;
font-weight: 500;
}
h3 input {
font-size: $h3-font-size;
font-weight: 500;
}
h4 input {
font-size: $h4-font-size;
font-weight: 500;
}
</style>