From cfd60ed7096d112edd99a4d3dce6a73d21e61875 Mon Sep 17 00:00:00 2001 From: Pablo Henrique Penha Silva Date: Tue, 14 Mar 2017 12:41:54 -0300 Subject: [PATCH 01/19] chips autocomplete --- .../mdChips/mdChipsAutocomplete.vue | 390 ++++++++++++++++++ 1 file changed, 390 insertions(+) create mode 100644 src/components/mdChips/mdChipsAutocomplete.vue diff --git a/src/components/mdChips/mdChipsAutocomplete.vue b/src/components/mdChips/mdChipsAutocomplete.vue new file mode 100644 index 0000000..ebb9a7e --- /dev/null +++ b/src/components/mdChips/mdChipsAutocomplete.vue @@ -0,0 +1,390 @@ + + + + From 2c1b8ecbecb053d92249d4acda8b0f1859e88536 Mon Sep 17 00:00:00 2001 From: Pablo Henrique Penha Silva Date: Sun, 26 Mar 2017 22:19:13 -0300 Subject: [PATCH 02/19] fixes md-autocomplete --- docs/src/pages/components/Input.vue | 5 + src/components/mdInputContainer/index.js | 2 + .../mdInputContainer/mdAutocomplete.vue | 236 ++++++++++++++++++ 3 files changed, 243 insertions(+) create mode 100644 src/components/mdInputContainer/mdAutocomplete.vue diff --git a/docs/src/pages/components/Input.vue b/docs/src/pages/components/Input.vue index 8bc3c70..54a346f 100644 --- a/docs/src/pages/components/Input.vue +++ b/docs/src/pages/components/Input.vue @@ -152,6 +152,11 @@ + + + + + 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..a733262 --- /dev/null +++ b/src/components/mdInputContainer/mdAutocomplete.vue @@ -0,0 +1,236 @@ + + + + + From 13616b91336127463621eeefd7b54caf6b9ffe49 Mon Sep 17 00:00:00 2001 From: Pablo Henrique Penha Silva Date: Sun, 26 Mar 2017 22:41:19 -0300 Subject: [PATCH 03/19] autocomplete --- .../mdInputContainer/mdAutocomplete.vue | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/components/mdInputContainer/mdAutocomplete.vue b/src/components/mdInputContainer/mdAutocomplete.vue index a733262..abbf9fa 100644 --- a/src/components/mdInputContainer/mdAutocomplete.vue +++ b/src/components/mdInputContainer/mdAutocomplete.vue @@ -22,7 +22,7 @@ {{ item.name }} + @click.native="hit(item)">{{ item[printAttribute] }} @@ -47,6 +47,10 @@ type: String, default: 'q' }, + printAttribute: { + type: String, + default: 'name' + }, minChars: { type: Number, default: 1 @@ -122,15 +126,16 @@ this.fetchUrl; }, hit(item) { - // util.warn('You need to implement the `onHit` method', this); - this.query = item.name; - this.$refs.input.value = item.name; + debugger; + this.query = item[this.printAttribute]; + this.$refs.input.value = item[this.printAttribute]; this.selected = item; this.onInput(); }, onInput() { this.updateValues(); - this.$emit('input', this.selected, this.$refs.input.value); + this.$emit('change', this.$refs.input.value); + this.$emit('input', this.$refs.input.value); this.$emit('selected', this.selected, this.$refs.input.value); }, reset() { From e9f5e83b3b941a9f5b9279a8a601e3975922d8ca Mon Sep 17 00:00:00 2001 From: Pablo Henrique Penha Silva Date: Mon, 27 Mar 2017 14:32:45 -0300 Subject: [PATCH 04/19] remove chips autocomplete --- .../mdChips/mdChipsAutocomplete.vue | 390 ------------------ 1 file changed, 390 deletions(-) delete mode 100644 src/components/mdChips/mdChipsAutocomplete.vue diff --git a/src/components/mdChips/mdChipsAutocomplete.vue b/src/components/mdChips/mdChipsAutocomplete.vue deleted file mode 100644 index ebb9a7e..0000000 --- a/src/components/mdChips/mdChipsAutocomplete.vue +++ /dev/null @@ -1,390 +0,0 @@ - - - - From 6393481d54d89bde79b96d22625334fbf64ce9a7 Mon Sep 17 00:00:00 2001 From: Pablo Henrique Penha Silva Date: Mon, 27 Mar 2017 14:36:58 -0300 Subject: [PATCH 05/19] removed debugger --- src/components/mdInputContainer/mdAutocomplete.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/mdInputContainer/mdAutocomplete.vue b/src/components/mdInputContainer/mdAutocomplete.vue index abbf9fa..4a70db9 100644 --- a/src/components/mdInputContainer/mdAutocomplete.vue +++ b/src/components/mdInputContainer/mdAutocomplete.vue @@ -126,7 +126,6 @@ this.fetchUrl; }, hit(item) { - debugger; this.query = item[this.printAttribute]; this.$refs.input.value = item[this.printAttribute]; this.selected = item; From a0795af2eac165ddcdb160042977a638e962dc8b Mon Sep 17 00:00:00 2001 From: Pablo Henrique Penha Silva Date: Mon, 27 Mar 2017 16:16:09 -0300 Subject: [PATCH 06/19] autocomplete --- docs/src/pages/components/Input.vue | 5 ++- .../mdInputContainer/mdAutocomplete.vue | 43 +++++-------------- .../mdInputContainer/mdInputContainer.scss | 8 ++++ 3 files changed, 21 insertions(+), 35 deletions(-) diff --git a/docs/src/pages/components/Input.vue b/docs/src/pages/components/Input.vue index 54a346f..adafd39 100644 --- a/docs/src/pages/components/Input.vue +++ b/docs/src/pages/components/Input.vue @@ -153,8 +153,8 @@ - - + + @@ -455,6 +455,7 @@ export default { data() { return { + autocompleteValue: '', initialValue: 'My initial value' }; } diff --git a/src/components/mdInputContainer/mdAutocomplete.vue b/src/components/mdInputContainer/mdAutocomplete.vue index 4a70db9..cf94454 100644 --- a/src/components/mdInputContainer/mdAutocomplete.vue +++ b/src/components/mdInputContainer/mdAutocomplete.vue @@ -1,5 +1,5 @@ - - diff --git a/src/components/mdInputContainer/mdInputContainer.scss b/src/components/mdInputContainer/mdInputContainer.scss index 50eceef..027051a 100644 --- a/src/components/mdInputContainer/mdInputContainer.scss +++ b/src/components/mdInputContainer/mdInputContainer.scss @@ -127,6 +127,14 @@ $input-size: 32px; } } +.md-input-container { + .md-autocomplete, + .md-autocomplete .md-menu, + .md-autocomplete .md-menu .md-input { + width: 100%; + } +} + .md-input-container { &.md-input-placeholder { label { From bdf9e5b66f64aa41b643b674f627b510b79f9370 Mon Sep 17 00:00:00 2001 From: Pablo Henrique Penha Silva Date: Mon, 27 Mar 2017 16:20:34 -0300 Subject: [PATCH 07/19] removing dep on http --- .../mdInputContainer/mdAutocomplete.vue | 30 ++----------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/src/components/mdInputContainer/mdAutocomplete.vue b/src/components/mdInputContainer/mdAutocomplete.vue index cf94454..1ae3105 100644 --- a/src/components/mdInputContainer/mdAutocomplete.vue +++ b/src/components/mdInputContainer/mdAutocomplete.vue @@ -41,7 +41,8 @@ default: 1E3 }, fetch: { - type: Function + type: Function, + required: true }, queryParam: { type: String, @@ -63,7 +64,6 @@ loading: false, query: '', selected: null, - src: 'https://typeahead-js-twitter-api-proxy.herokuapp.com/demo/search', timeout: 0 }; }, @@ -94,30 +94,6 @@ this.update(); }, this.debounce); }, - fetchUrl() { - if (!this.$http) { - throw new Error('You need to provide a HTTP client'); - } - - if (!this.src || this.fetch) { - throw new Error('You need to set the `src` or a `fetch` property'); - } - - const src = this.queryParam ? - this.src : - `${this.src}${this.queryParam}=${this.query}`; - - const params = this.queryParam ? - Object.assign({ [this.queryParam]: this.query }, this.data) : - this.data; - - return this.$http.get(src, { params }); - }, - getResource() { - return this.fetch ? - this.fetch : - this.fetchUrl; - }, hit(item) { this.query = item[this.printAttribute]; this.$refs.input.value = item[this.printAttribute]; @@ -165,7 +141,7 @@ this.loading = true; - this.getResource()() + this.fetch() .then((response) => { if (this.query) { let data = response.data; From e51dfaf548894c8a0bd07d91d857b62882719844 Mon Sep 17 00:00:00 2001 From: Pablo Henrique Penha Silva Date: Mon, 27 Mar 2017 20:16:39 -0300 Subject: [PATCH 08/19] correcoes --- docs/src/pages/components/Input.vue | 36 ++++++- .../mdInputContainer/mdAutocomplete.vue | 93 +++++++++++++------ 2 files changed, 99 insertions(+), 30 deletions(-) diff --git a/docs/src/pages/components/Input.vue b/docs/src/pages/components/Input.vue index adafd39..8c39e53 100644 --- a/docs/src/pages/components/Input.vue +++ b/docs/src/pages/components/Input.vue @@ -153,8 +153,13 @@ - - + + + + + + + @@ -456,8 +461,33 @@ data() { return { autocompleteValue: '', - initialValue: 'My initial value' + 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/mdAutocomplete.vue b/src/components/mdInputContainer/mdAutocomplete.vue index 1ae3105..1761721 100644 --- a/src/components/mdInputContainer/mdAutocomplete.vue +++ b/src/components/mdInputContainer/mdAutocomplete.vue @@ -41,25 +41,31 @@ default: 1E3 }, fetch: { - type: Function, - required: true + type: Function }, - queryParam: { - type: String, - default: 'q' - }, - printAttribute: { - type: String, - default: 'name' + filterList: Function, + list: { + type: Array, + default() { + return []; + } }, minChars: { type: Number, default: 1 + }, + prepareResponseData: Function, + printAttribute: { + type: String, + default: 'name' + }, + queryParam: { + type: String, + default: 'q' } }, data() { return { - current: -1, items: [], loading: false, query: '', @@ -67,7 +73,15 @@ timeout: 0 }; }, + computed: { + listIsEmpty() { + return this.list.length === 0; + } + }, watch: { + list(value) { + this.items = Object.assign([], value); + }, query(value) { this.$refs.input.value = value; this.setParentUpdateValue(value); @@ -78,11 +92,6 @@ } }, methods: { - activeClass(index) { - return { - active: this.current === index - }; - }, debounceUpdate() { this.onInput(); @@ -91,6 +100,10 @@ } this.timeout = window.setTimeout(() => { + if (!this.listIsEmpty) { + this.renderFilteredList(); + return; + } this.update(); }, this.debounce); }, @@ -106,14 +119,17 @@ this.$emit('input', this.$refs.input.value); this.$emit('selected', this.selected, this.$refs.input.value); }, + renderFilteredList() { + if (this.filterList) { + this.items = this.filterList(Object.assign([], this.list), this.query); + } + this.toggleMenu(); + }, reset() { this.items = []; this.query = ''; this.loading = false; }, - setActive(index) { - this.current = index; - }, setParentValue(value) { this.parentContainer.setValue(value || this.$refs.input.value); }, @@ -131,7 +147,7 @@ this.updateValues(value); }, update() { - if (!this.query) { + if (!this.query && !this.list.length) { return this.reset(); } @@ -141,10 +157,12 @@ this.loading = true; - this.fetch() + const queryObject = { [this.queryParam]: this.query }; + + this.fetch(queryObject) .then((response) => { if (this.query) { - let data = response.data; + let data = response || response.data || response.body; data = this.prepareResponseData ? this.prepareResponseData(data) : @@ -152,13 +170,33 @@ this.items = this.limit ? data.slice(0, this.limit) : data; - this.current = -1; + this.loading = false; this.toggleMenu(); } }); }, + verifyProps() { + let errorMessage = ''; + + if (!this.parentContainer) { + errorMessage = 'You should wrap the md-input in a md-input-container'; + } + + if (!this.listIsEmpty && !this.filterList) { + errorMessage = 'You should use a `filterList` function prop with the `list` prop'; + } + + if (!fetch) { + errorMessage = 'You should use a `fetch` function prop'; + } + + if (errorMessage) { + this.$destroy(); + throw new Error(errorMessage); + } + }, toggleMenu() { if (this.items.length) { this.$refs.menu.toggle(); @@ -174,13 +212,14 @@ mounted() { this.$nextTick(() => { this.parentContainer = getClosestVueParent(this.$parent, 'md-input-container'); + + if (!this.listIsEmpty) { + this.items = Object.assign([], this.list); + } + this.query = this.value; - if (!this.parentContainer) { - this.$destroy(); - - throw new Error('You should wrap the md-input in a md-input-container'); - } + this.verifyProps(); this.setParentDisabled(); this.setParentRequired(); From 00123e5d4209569f9b126b062e355cc8de559841 Mon Sep 17 00:00:00 2001 From: Pablo Henrique Penha Silva Date: Mon, 27 Mar 2017 23:49:43 -0300 Subject: [PATCH 09/19] documentation --- docs/src/pages/components/Input.vue | 146 ++++++++++++++++++++++++++-- 1 file changed, 139 insertions(+), 7 deletions(-) diff --git a/docs/src/pages/components/Input.vue b/docs/src/pages/components/Input.vue index 8c39e53..c6eeba0 100644 --- a/docs/src/pages/components/Input.vue +++ b/docs/src/pages/components/Input.vue @@ -98,6 +98,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 + + + + + @@ -154,12 +274,7 @@ - - - - - - + @@ -197,6 +312,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> @@ -230,7 +350,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. + }, + }, }; From b15f0a03a31fe7cbadaef2f78cdaada367bf3bc4 Mon Sep 17 00:00:00 2001 From: Pablo Henrique Penha Silva Date: Mon, 3 Apr 2017 11:29:23 -0300 Subject: [PATCH 10/19] fix on line 191 mdAutocomplete --- src/components/mdInputContainer/mdAutocomplete.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/mdInputContainer/mdAutocomplete.vue b/src/components/mdInputContainer/mdAutocomplete.vue index 1761721..b48fa9a 100644 --- a/src/components/mdInputContainer/mdAutocomplete.vue +++ b/src/components/mdInputContainer/mdAutocomplete.vue @@ -188,7 +188,7 @@ errorMessage = 'You should use a `filterList` function prop with the `list` prop'; } - if (!fetch) { + if (!this.fetch) { errorMessage = 'You should use a `fetch` function prop'; } From 9df33453e2b753268a181b8766a7184a75b670be Mon Sep 17 00:00:00 2001 From: Pablo Henrique Penha Silva Date: Mon, 3 Apr 2017 13:11:09 -0300 Subject: [PATCH 11/19] fix on selected event --- src/components/mdInputContainer/mdAutocomplete.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/mdInputContainer/mdAutocomplete.vue b/src/components/mdInputContainer/mdAutocomplete.vue index b48fa9a..50fb233 100644 --- a/src/components/mdInputContainer/mdAutocomplete.vue +++ b/src/components/mdInputContainer/mdAutocomplete.vue @@ -112,12 +112,12 @@ this.$refs.input.value = item[this.printAttribute]; this.selected = item; this.onInput(); + this.$emit('selected', this.selected, this.$refs.input.value); }, onInput() { this.updateValues(); this.$emit('change', this.$refs.input.value); this.$emit('input', this.$refs.input.value); - this.$emit('selected', this.selected, this.$refs.input.value); }, renderFilteredList() { if (this.filterList) { From 287c0c9db53187f5d9c2f111334aaf59f94a2631 Mon Sep 17 00:00:00 2001 From: Pablo Henrique Penha Silva Date: Mon, 3 Apr 2017 15:18:56 -0300 Subject: [PATCH 12/19] added name prop --- docs/src/pages/components/Input.vue | 2 +- src/components/mdInputContainer/mdAutocomplete.vue | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/src/pages/components/Input.vue b/docs/src/pages/components/Input.vue index c6eeba0..16b4566 100644 --- a/docs/src/pages/components/Input.vue +++ b/docs/src/pages/components/Input.vue @@ -274,7 +274,7 @@ - + diff --git a/src/components/mdInputContainer/mdAutocomplete.vue b/src/components/mdInputContainer/mdAutocomplete.vue index 50fb233..b400781 100644 --- a/src/components/mdInputContainer/mdAutocomplete.vue +++ b/src/components/mdInputContainer/mdAutocomplete.vue @@ -13,6 +13,7 @@ :required="required" :placeholder="placeholder" :maxlength="maxlength" + :name="name" @focus="onFocus" @blur="onBlur" @input="debounceUpdate"/> @@ -54,6 +55,7 @@ type: Number, default: 1 }, + name: String, prepareResponseData: Function, printAttribute: { type: String, From c82fd9c41abb658889281b2c616ffcc4d737f4f4 Mon Sep 17 00:00:00 2001 From: pablohpsilva Date: Mon, 3 Apr 2017 23:00:42 -0300 Subject: [PATCH 13/19] modularizing code --- .../mdInputContainer/mdAutocomplete.vue | 53 +---------------- src/core/utils/autocomplete-commons.js | 59 +++++++++++++++++++ 2 files changed, 61 insertions(+), 51 deletions(-) create mode 100644 src/core/utils/autocomplete-commons.js diff --git a/src/components/mdInputContainer/mdAutocomplete.vue b/src/components/mdInputContainer/mdAutocomplete.vue index b400781..fbca3d8 100644 --- a/src/components/mdInputContainer/mdAutocomplete.vue +++ b/src/components/mdInputContainer/mdAutocomplete.vue @@ -31,41 +31,12 @@