多维表格

187 lines
5.2 KiB

<template>
<div>
<v-text-field
v-bind="$attrs"
v-model="autocomplete"
@input="onInput"
ref="input"
:style="styles"
@keydown.native="onKeyup"
:class="[classNames,{'env-valid' : isEnvFound && isEnvUsageValid, 'env-invalid' : isEnvFound && !isEnvUsageValid}]"
>
</v-text-field>
<v-menu
ref="autoMenu"
:position-x="x"
:position-y="y"
v-model="show"
dense
:min-width="width"
>
<v-list dense>
<v-list-item
v-for="(item, index) in envValues"
:key="index"
@click="onSelect(item)"
v-on:keyup.enter="onSelect(item)"
>
<v-list-item-title>{{ item }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</div>
</template>
<script>
// http://jsfiddle.net/pranavcbalan/qg5xyko6/
export default {
data() {
return {
show: false,
x: 0,
y: 0,
activeValue: '',
curPos: 0,
input: null,
el: null
}
},
name: "xAutoComplete",
props: {
value: String,
env: String,
styles: [Array, Object, String]
},
computed: {
// filtered list based on input
items() {
return this.envValues.filter(s => s.indexOf(this.activeValue) > -1);
},
// for setting menu width to textfield width
width() {
return this.input && this.input.clientWidth;
},
// extracting class names from root element
classNames() {
return this.el && this.el.className;
},
// v-model for the text filed
autocomplete: {
get() {
return this.value;
}, set(val) {
this.$emit('input', val);
}
},
// extracting current env keys
envValues() {
const envObj = this.isDashboard ? this.$store.getters['project/GtrApiEnvironment'] : this.$store.getters['project/GtrDefaultApiEnvironment'];
return envObj && envObj[this.env] ? Object.keys(envObj[this.env]) : [];
},
isEnvFound() {
return /{{\s*\w+\s*}}/.test(this.value);
},
isEnvUsageValid() {
const re = /{{\s*(\w+)\s*}}/g;
let m;
while (m = re.exec(this.value)) {
if (!this.envValues.includes(m[1])) return false;
}
return true;
}
},
mounted() {
this.el = this.$el;
// getting input element reference
this.input = this.$refs.input && this.$refs.input.$el.querySelector('input');
},
methods: {
// handling input event
onInput(v) {
this.curPos = this.input.selectionStart || 0;
// extracting string from beginning to caret position
// then using regex to check and extract certain pattern like
// eg: {{ , {{ someWord,...
let m = v.slice(0, this.curPos).match(/{{\s?(\w*)$/);
if (m) {
this.activeValue = m[1] || '';
this.show = true;
// calculate menu position relative to input element
this.x = this.input.getBoundingClientRect().left;// + (this.curPos * 8) % this.$refs.input.$el.clientWidth;
this.y = this.input.getBoundingClientRect().top + this.input.clientHeight;
} else {
this.show = false;
}
},
onKeyup(e) {
const menu = this.$refs.autoMenu
// handlig up and down keys
if (this.show && (e.which === 40 || e.which === 38)) {
e.preventDefault()
menu.onKeyDown(e)
return;
}
// handling enter and space
if (this.show && (e.which === 13 || e.which === 32)) {
menu.onKeyDown(e)
}
},
onSelect(item) {
// appending menu value to the input
this.input.setRangeText(
// add closing only when its necessary
item.slice(this.activeValue.length) + (this.autocomplete.substr(this.input.selectionEnd, 2) === '}}' ? '' : '}}'),
this.input.selectionStart,
this.input.selectionEnd,
'end'
);
// trigger input event to sync with vue model
this.input.dispatchEvent(new Event('input'))
this.show = false;
}
},
created() {
}
}
</script>
<style scoped>
/deep/ .env-valid input {
color: var(--v-success-base) !important;
}
/deep/ .env-invalid input {
color: var(--v-error-base) !important;
}
</style>
<!--
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-->