Improved autocomplete widget
This commit is contained in:
parent
1d68749525
commit
6c263b00dc
@ -1,44 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="autocomplete">
|
<div class="dropdown" :class="{ 'form-floating': label }">
|
||||||
<input type="text" @input="option_open" v-model="xvalue" @keydown.down="option_down" @keydown.up="option_up" @keydown.enter="option_enter" />
|
<input type="text" class="form-control" placeholder=" " @focus="option_open" @input="option_filter" v-model="x_modelValue" @keydown.down="option_down" @keydown.up="option_up" @keydown.enter="option_enter" />
|
||||||
<ul id="autocomplete-results" v-show="open" class="autocomplete-results">
|
<ul class="dropdown-menu shadow" :class="{ show: (open) && (results.length > 0) }">
|
||||||
<li class="loading" v-if="!items">Loading results...</li>
|
<li class="loading" v-if="!items">Loading results...</li>
|
||||||
<li v-else v-for="(result, i) in results" :key="i" @click="option_click(result)" class="autocomplete-result" :class="{ 'is-active': i === index }">{{ result }}</li>
|
<li v-else v-for="(result, i) in results" :key="i" @click="option_click(result)" class="dropdown-item" :class="{ 'is-active': i === index }">{{ result }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<label v-if="label">{{label}}</label>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.autocomplete {
|
.dropdown-menu {
|
||||||
position: relative;
|
width: 100%;
|
||||||
}
|
max-height: 10rem;
|
||||||
|
|
||||||
.autocomplete-results {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
border: 1px solid #eeeeee;
|
|
||||||
height: 120px;
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.autocomplete-result {
|
.dropdown-item {
|
||||||
list-style: none;
|
cursor: default;
|
||||||
text-align: left;
|
|
||||||
padding: 4px 2px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.autocomplete-result.is-active,
|
.dropdown-item.is-active,
|
||||||
.autocomplete-result:hover {
|
.dropdown-item:hover {
|
||||||
background-color: #4AAE9B;
|
background-color: var(--bs-primary);
|
||||||
color: white;
|
color: var(--bs-body-bg);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
value: {
|
modelValue: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
},
|
},
|
||||||
@ -46,26 +38,30 @@
|
|||||||
type: Array,
|
type: Array,
|
||||||
required: false,
|
required: false,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
}
|
},
|
||||||
|
label: String
|
||||||
},
|
},
|
||||||
|
emits: [
|
||||||
|
'update:modelValue'
|
||||||
|
],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
xvalue: '',
|
x_modelValue: this.modelValue,
|
||||||
results: [],
|
results: [],
|
||||||
open: false,
|
open: false,
|
||||||
index: -1,
|
index: -1,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
value(val) {
|
modelValue(val) {
|
||||||
this.xvalue = val;
|
this.x_modelValue = val;
|
||||||
},
|
},
|
||||||
xvalue(val) {
|
x_modelValue(val) {
|
||||||
this.$emit('update:value', val);
|
this.$emit('update:modelValue', val);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.xvalue = this.value;
|
this.x_modelValue = this.modelValue;
|
||||||
document.addEventListener('click', this.option_close)
|
document.addEventListener('click', this.option_close)
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
@ -74,7 +70,16 @@
|
|||||||
methods: {
|
methods: {
|
||||||
option_open() {
|
option_open() {
|
||||||
if(this.items) {
|
if(this.items) {
|
||||||
this.results = this.items.filter((item) => item.toLowerCase().indexOf(this.xvalue.toLowerCase()) > -1);
|
this.results = this.items;
|
||||||
|
this.open = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
option_filter() {
|
||||||
|
if(this.items) {
|
||||||
|
if(this.x_modelValue) {
|
||||||
|
var selection = this.x_modelValue.toLowerCase();
|
||||||
|
this.results = this.items.filter((item) => item.toLowerCase().indexOf(selection) >= 0);
|
||||||
|
} else this.results = this.items;
|
||||||
this.open = true;
|
this.open = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -85,12 +90,12 @@
|
|||||||
if(this.index > 0) this.index--;
|
if(this.index > 0) this.index--;
|
||||||
},
|
},
|
||||||
option_enter() {
|
option_enter() {
|
||||||
this.xvalue = this.results[this.index];
|
this.x_modelValue = this.results[this.index];
|
||||||
this.open = false;
|
this.open = false;
|
||||||
this.index = -1;
|
this.index = -1;
|
||||||
},
|
},
|
||||||
option_click(result) {
|
option_click(result) {
|
||||||
this.xvalue = result;
|
this.x_modelValue = result;
|
||||||
this.open = false;
|
this.open = false;
|
||||||
},
|
},
|
||||||
option_close(evt) {
|
option_close(evt) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<table class="table" style="font-family: monospace;" v-if="appointments && appointments.length > 0">
|
<table class="table" style="font-family: monospace;" v-if="appointments && appointments.length > 0">
|
||||||
<thead>
|
<thead>
|
||||||
<tr><th>Time</th><th>Clinic</th><th>Patient</th><th>Note</th><th>Assignee</th></tr>
|
<tr><th>Time</th><th>Clinic</th><th>Patient</th><th>Note</th><th style="width: 16rem;">Assignee</th></tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="row in appointments" :style="{ backgroundColor: strHashHSL(row.Clinic, '90%') }">
|
<tr v-for="row in appointments" :style="{ backgroundColor: strHashHSL(row.Clinic, '90%') }">
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<td v-if="production"><router-link :to="'/patient/$' + row.HRN">{{row.Name}} <span :title="row.HRN">{{row.HRN.slice(-4)}}</span></router-link></td>
|
<td v-if="production"><router-link :to="'/patient/$' + row.HRN">{{row.Name}} <span :title="row.HRN">{{row.HRN.slice(-4)}}</span></router-link></td>
|
||||||
<td v-else><router-link :title="strtr_unscramble(row.Name)" :to="'/patient/$' + row.Name.charAt(0) + row.HRN.slice(-4) + '?name=' + row.Name">{{row.Name}} ${{row.HRN}}</router-link></td>
|
<td v-else><router-link :title="strtr_unscramble(row.Name)" :to="'/patient/$' + row.Name.charAt(0) + row.HRN.slice(-4) + '?name=' + row.Name">{{row.Name}} ${{row.HRN}}</router-link></td>
|
||||||
<td>{{row.NOTE}} [{{row.APPT_MADE_BY}} on {{row.DATE_APPT_MADE}}]</td>
|
<td>{{row.NOTE}} [{{row.APPT_MADE_BY}} on {{row.DATE_APPT_MADE}}]</td>
|
||||||
<td><Autocomplete :value="practitioner[row.Name]" @update:value="x => practitioner[row.Name] = x" :items="practitioner_list" /></td>
|
<td><Autocomplete :modelValue="practitioner[row.Name]" @update:modelValue="x => practitioner[row.Name] = x" :items="practitioner_list" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -48,7 +48,7 @@
|
|||||||
return this.client.remotestate.practitioner || (this.client.remotestate.practitioner = {});
|
return this.client.remotestate.practitioner || (this.client.remotestate.practitioner = {});
|
||||||
},
|
},
|
||||||
practitioner_list() {
|
practitioner_list() {
|
||||||
return this.practitioner ? uniq(Object.values(this.practitioner)).sort() : [];
|
return this.practitioner ? uniq(Object.values(this.practitioner).filter(x => x)).sort() : [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
Loading…
Reference in New Issue
Block a user