3
0
Fork 0

fix: add basic <wutta-autocomplete> component

this probably needs improvement yet but we'll see
This commit is contained in:
Lance Edgar 2025-01-08 12:29:53 -06:00
parent 517928320b
commit e674db86be

View file

@ -1,6 +1,7 @@
<%def name="make_wutta_components()"> <%def name="make_wutta_components()">
${self.make_wutta_request_mixin()} ${self.make_wutta_request_mixin()}
${self.make_wutta_autocomplete_component()}
${self.make_wutta_button_component()} ${self.make_wutta_button_component()}
${self.make_wutta_datepicker_component()} ${self.make_wutta_datepicker_component()}
${self.make_wutta_timepicker_component()} ${self.make_wutta_timepicker_component()}
@ -86,6 +87,150 @@
</script> </script>
</%def> </%def>
<%def name="make_wutta_autocomplete_component()">
<script type="text/x-template" id="wutta-autocomplete-template">
<div>
<b-autocomplete ref="autocomplete"
v-show="!value"
v-model="entry"
:data="data"
@typing="getAsyncData"
@select="selectionMade"
keep-first>
<template slot-scope="props">
{{ props.option.label }}
</template>
</b-autocomplete>
<b-button v-if="value"
@click="clearValue(true, true)">
{{ recordLabel }} (click to change)
</b-button>
</div>
</script>
<script>
const WuttaAutocomplete = {
template: '#wutta-autocomplete-template',
props: {
// callers do not specify this directly but rather by way
// of the `v-model` directive. the component will emit
// `input` events when this value changes
value: String,
// caller must specify initial display string, if the
// (v-model) value is not empty when component loads
display: String,
// the url from which search results are obtained. the
// endpoint should expect a GET with single `term` param
// in query string, and return list of objects, each with
// (at least) `value` and `label` properties.
serviceUrl: String,
},
data() {
return {
// user search input
entry: null,
// display label for button, when value is set
recordLabel: this.display,
// this contains the latest search results; it will
// change over time as user types. when an option is
// selected, it will be an element from this list.
data: [],
}
},
watch: {
value(val) {
// reset ourself when model value is cleared
if (!val) {
this.clearValue()
}
},
},
methods: {
focus() {
this.$refs.autocomplete.focus()
},
// convenience for parent component to fetch current label
getLabel() {
return this.recordLabel
},
// fetch new search results from server. this is invoked
// when user types new input
getAsyncData(entry) {
// nb. skip search until we have at least 3 chars of input
if (entry.length < 3) {
this.data = []
return
}
// search results become autocomplete options
this.$http.get(this.serviceUrl + '?term=' + encodeURIComponent(entry))
.then(({ data }) => {
this.data = data
})
.catch((error) => {
this.data = []
throw error
})
},
// handle selection change. this is invoked when user
// chooses an autocomplete option
selectionMade(option) {
// reset user input
this.entry = null
// nb. this method can be triggered when a selection
// is made *or cleared* - if the latter then we do not
// want to emit event for the empty value; that part
// is handled in clearValue()
if (option) {
this.recordLabel = option.label
this.$emit('input', option.value)
} else {
this.recordLabel = null
}
},
// clear the component value
clearValue(emit, focus) {
// clear autocomplete selection
this.$refs.autocomplete.setSelected(null)
// maybe emit event for new value
if (emit) {
this.$emit('input', null)
}
// maybe set focus to autocomplete
if (focus) {
this.$nextTick(function() {
this.focus()
})
}
},
},
}
Vue.component('wutta-autocomplete', WuttaAutocomplete)
</script>
</%def>
<%def name="make_wutta_button_component()"> <%def name="make_wutta_button_component()">
<script type="text/x-template" id="wutta-button-template"> <script type="text/x-template" id="wutta-button-template">
<b-button :type="type" <b-button :type="type"