diff --git a/docs/src/pages/components/Input.vue b/docs/src/pages/components/Input.vue index b7fdddc..888f0a8 100644 --- a/docs/src/pages/components/Input.vue +++ b/docs/src/pages/components/Input.vue @@ -116,6 +116,126 @@ + + + + + Name + Type + Description + + + + + + v-model + String + A required model object to bind the value. + + + + debounce + Number + Sets the debounce time. Default 1000 milliseconds + + + + fetch + Function + Sets the fetch function mdAutocomplete will call after the debounce is reached. Chosing fetch prop disables the use of either list and filterList props. + + + + list + Array + Sets an array of possible values. Default []. MdAutocomplete will only search in this list if it's set. Chosing list prop disables the use of fetch prop. + + + + filter-list + Function + Sets a filter function which will be used to filter the list props. Chosing filterList prop requires the use of list props and disables the use of fetch prop. + + + + min-chars + Number + Sets the minimum number of characters before making opening the autocomplete options or making a request. Default 3 + + + + prepare-response-data + Function + This function will be called once the fetch prop has a response. It can manipulate the data received from the server. The output should always be an Array. + + + + print-attribute + String + This prop will be used to print values on the autocomplete list. It shall match an object key expected on the fetch result list. Default name + + + + query-param + String + Sets the query parameter. Example: http//api.com/q?=SOMETHING. Default q + + + + disabled + Boolean + Disable the input and prevent his actions. Default false + + + + required + Boolean + Apply the required rule to style the label with an "*". Default false + + + + placeholder + String + Sets the placeholder. + + + + maxlength + Number + Sets the maxlength and enable the text counter. + + + + + + + + Name + Value + Description + + + + + + change + The String selected + Triggered when the user selects an item from the autocomplete list + + + input + The String selected + Triggered when the user selects an item from the autocomplete list + + + selected + Emits the Object as well as the String selected. + Triggered when the user selects an item from the autocomplete list + + + + + @@ -187,6 +307,11 @@ + + + + + @@ -227,6 +352,11 @@ <md-input v-model="initialValue"></md-input> </md-input-container> + <md-input-container> + <label>Autocomplete (with fetch)</label> + <md-input v-model="autocompleteValue" :fetch="fetchFunction"></md-input> + </md-input-container> + <md-input-container> <label>With label</label> <md-input placeholder="My nice placeholder"></md-input> @@ -265,7 +395,19 @@ return { initialValue: 'My initial value' }; - } + }, + methods: { + fetchFunction(param) { + // param = { queryParam: query } + + // 'fetchAutocomplete' should return a Promise. + + // md-autocomplete will call fetchAutocomplete and pass + // 'param' as an argument. + // the 'param' is composed by a query param and + // a query. + }, + }, }; @@ -495,8 +637,34 @@ export default { data() { return { - initialValue: 'My initial value' + autocompleteValue: '', + initialValue: 'My initial value', + listAutocomplete: [ + {name: 'oi'}, + {name: 'hello'}, + {name: 'salut'} + ] }; + }, + methods: { + fetchAutocomplete(param) { + const myInit = { + method: 'GET', + headers: new Headers(), + mode: 'cors', + cache: 'default' + }; + const url = 'https://typeahead-js-twitter-api-proxy.herokuapp.com/demo/search'; + const queryParam = Object.keys(param)[0]; + const queryValue = param[queryParam]; + const queryUrl = `${url}?${queryParam}=${queryValue}`; + + return window.fetch(queryUrl, myInit) + .then((res) => res.json()); + }, + filterList(list, query) { + return list.filter((el) => el.name.indexOf(query) !== -1); + } } }; diff --git a/src/components/mdInputContainer/index.js b/src/components/mdInputContainer/index.js index 3194f74..23dbdf6 100644 --- a/src/components/mdInputContainer/index.js +++ b/src/components/mdInputContainer/index.js @@ -1,11 +1,13 @@ import mdInputContainer from './mdInputContainer.vue'; import mdInput from './mdInput.vue'; +import mdAutocomplete from './mdAutocomplete.vue'; import mdTextarea from './mdTextarea.vue'; import mdInputContainerTheme from './mdInputContainer.theme'; export default function install(Vue) { Vue.component('md-input-container', mdInputContainer); Vue.component('md-input', mdInput); + Vue.component('md-autocomplete', mdAutocomplete); Vue.component('md-textarea', mdTextarea); Vue.material.styles.push(mdInputContainerTheme); diff --git a/src/components/mdInputContainer/mdAutocomplete.vue b/src/components/mdInputContainer/mdAutocomplete.vue new file mode 100644 index 0000000..5b57a7e --- /dev/null +++ b/src/components/mdInputContainer/mdAutocomplete.vue @@ -0,0 +1,209 @@ + + + diff --git a/src/components/mdInputContainer/mdInputContainer.scss b/src/components/mdInputContainer/mdInputContainer.scss index ff606db..6ad9548 100644 --- a/src/components/mdInputContainer/mdInputContainer.scss +++ b/src/components/mdInputContainer/mdInputContainer.scss @@ -127,6 +127,18 @@ $input-size: 32px; } } +.md-input-container { + .md-autocomplete, + .md-autocomplete .md-menu, + .md-autocomplete .md-menu .md-input { + width: 100%; + } +} + +.md-theme-default.md-input-container .md-autocomplete .md-icon:not(.md-icon-search):after { + height: 0; +} + .md-input-container { &.md-input-placeholder { label { diff --git a/src/core/utils/autocomplete-common.js b/src/core/utils/autocomplete-common.js new file mode 100644 index 0000000..e856558 --- /dev/null +++ b/src/core/utils/autocomplete-common.js @@ -0,0 +1,58 @@ +export default { + props: { + debounce: { + type: Number, + default: 1E3 + }, + disabled: Boolean, + fetch: { + type: Function + }, + filterList: Function, + list: { + type: Array, + default() { + return []; + } + }, + minChars: { + type: Number, + default: 1 + }, + name: String, + prepareResponseData: Function, + printAttribute: { + type: String, + default: 'name' + }, + queryParam: { + type: String, + default: 'q' + }, + required: Boolean + }, + methods: { + onFocus() { + if (this.parentContainer) { + this.parentContainer.isFocused = true; + } + }, + onBlur() { + this.parentContainer.isFocused = false; + this.setParentValue(); + }, + verifyProps() { + if (!this.parentContainer) { + return this.throwErrorDestroy('You should wrap the md-input in a md-input-container'); + } else if (this.listIsEmpty && this.filterList) { + return this.throwErrorDestroy('You should use a `filterList` function prop with the `list` prop'); + } else if (!this.fetch && this.listIsEmpty) { + return this.throwErrorDestroy('You should use a `fetch` function prop'); + } + }, + throwErrorDestroy(errorMessage) { + this.$destroy(); + throw new Error(errorMessage); + } + } +};