nuVistA/htdocs/ViewDocTitleLookup.vue

124 lines
3.5 KiB
Vue

<template>
<div v-if="label" class="input-group">
<span class="input-group-text">{{label}}</span>
<input class="form-control" placeholder="Filter..." v-model="x_query" />
</div>
<input v-else class="form-control" placeholder="Filter..." v-model="x_query" />
<div class="scroller" ref="scroller">
<table class="table table-striped">
<tbody>
<tr v-for="item in resultset" :class="{ 'table-active': item.IEN == x_modelValue }" @click="x_modelValue = item.IEN">
<td>{{item.name}}</td>
<td style="text-align: right;">#{{item.IEN}}</td>
</tr>
<tr ref="bottom" />
</tbody>
</table>
</div>
</template>
<style scoped>
div.scroller {
max-height: 25vh;
overflow-y: auto;
}
td {
cursor: default;
}
.table-active, .table-active:nth-of-type(odd) > * {
color: #fff;
background-color: #0d6efd;
}
</style>
<script>
import { debounce } from './util.mjs';
export default {
props: {
client: Object,
label: String,
query: String,
modelValue: String
},
emits: {
'update:query': String,
'update:modelValue': String
},
data() {
return {
resultset: [],
has_more: false,
is_loading: true,
observer_bottom: null,
x_query: this.query,
x_modelValue: this.modelValue
};
},
computed: {
query_view() {
return this.x_query ? this.x_query.replace(/^\s+|\s+$/g, '').replace(/\s+/g, ' ').toUpperCase() : '';
}
},
watch: {
modelValue(value) { this.x_modelValue = value; },
x_modelValue(value) { this.$emit('update:modelValue', value); },
query(value) { this.x_query = value; },
x_query(value) { this.$emit('update:query', value); }
},
methods: {
async handle_bottom([entry]) {
if((entry.isIntersecting) && (this.has_more) && (!this.is_loading)) {
this.is_loading = true;
this.has_more = false;
try {
var batch = await this.client.TIU_LONG_LIST_OF_TITLES(3, this.resultset[this.resultset.length - 1].name, 1);
if(this.query_view.length >= 1) batch = batch.filter(x => x.name.startsWith(this.query_view));
if(batch.length > 0) {
Array.prototype.push.apply(this.resultset, batch);
this.has_more = true;
} else this.has_more = false;
} catch(ex) {
this.has_more = false;
} finally {
this.is_loading = false;
}
}
}
},
created() {
this.$watch(
() => (this.client, this.query_view, {}),
debounce(async function() {
if(this.client) {
this.is_loading = true;
this.has_more = false;
try {
var query = this.query_view;
if(query.length >= 1) {
var batch = await this.client.TIU_LONG_LIST_OF_TITLES(3, query.slice(0, -1) + String.fromCharCode(query.charCodeAt(query.length - 1) - 1) + '~', 1);
this.resultset = batch.filter(x => x.name.startsWith(query));
} else this.resultset = await this.client.TIU_LONG_LIST_OF_TITLES(3, '', 1);
this.has_more = this.resultset.length > 0;
} catch(ex) {
this.resultset = [];
this.has_more = false;
} finally {
this.is_loading = false;
if(this.$refs.scroller) this.$refs.scroller.scrollTo(0, 0);
}
}
}, 500),
{ immediate: true }
);
},
mounted() {
this.observer_bottom = new IntersectionObserver(this.handle_bottom, { root: this.$refs.scroller, rootMargin: '25%' });
this.observer_bottom.observe(this.$refs.bottom);
},
destroyed() {
if(this.observer_bottom) this.observer_bottom.disconnect();
}
};
</script>