fix: add basic <wutta-autocomplete> component
				
					
				
			this probably needs improvement yet but we'll see
This commit is contained in:
		
							parent
							
								
									517928320b
								
							
						
					
					
						commit
						e674db86be
					
				
					 1 changed files with 145 additions and 0 deletions
				
			
		|  | @ -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" | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue