UNPKG

vue-generic-autocomplete

Version:

Vue addres search autocomplete and suggestion

394 lines (337 loc) 10.7 kB
/*! * vue-generic-autocomplete v0.0.1 * (c) Mmontsheng Maoto * Released under the MIT License. */ 'use strict'; // // // // // // // // // // // // // // // // // // // // // // // // // var script = { computed: { suggestions: function suggestions() { if (this.resultsData.length > this.suggestionsLimit) { var resData = this.resultsData; return resData.splice(0, this.suggestionsLimit - 1); } return this.resultsData; } }, data: function data() { return { currentFocusedItem: -1, searchText: '', resultsData: this.list }; }, methods: { // adds active class the the current list addActive: function addActive() { var items = document.getElementsByClassName('suggested-item'); // remove class from all nodes this.removeActive(items); // add class to this node items[this.currentFocusedItem].classList.add('suggested-item-active'); }, autocomplete: function autocomplete() { this.$emit('searchword', this.searchText); }, chosenSuggestion: function chosenSuggestion(chosen) { this.searchText = chosen; this.resultsData = []; this.$emit('on-chosen', chosen); this.currentFocusedItem = -1; }, currentFocus: function currentFocus(keyEvent) { if (this.resultsData.length > 0) { if (keyEvent.keyCode === 40) { // If the arrow DOWN key is pressed, increase the currentFocus variable if (this.currentFocusedItem >= this.resultsData.length - 1) { this.currentFocusedItem = 0; } else { this.currentFocusedItem += 1; } this.addActive(); } else if (keyEvent.keyCode === 38) { // If the arrow UP key is pressed, decrease the currentFocus variable if (this.currentFocusedItem === 0 || this.currentFocusedItem === -1) { this.currentFocusedItem = this.resultsData.length - 1; } else { this.currentFocusedItem -= 1; } this.addActive(); } else if (keyEvent.keyCode === 13) { // If the ENTER key is pressed, do a click on the item var item = document.getElementsByClassName('suggested-item-active'); if (item) { item[0].click(); } } } }, displayProperty: function displayProperty(properties) { if (typeof properties === 'string') { return properties; } var display = this.getDisplayProperty(properties); return display; }, getDisplayProperty: function getDisplayProperty(properties) { var key = this.displayAttribute; var display = properties[key]; if (display) { return display; } // eslint-disable-next-line console.error("this display property: '".concat(key, "' is not valid key to list object")); return 'undefined display property'; }, // Remove the "active" class from all suggested items removeActive: function removeActive(items) { for (var i = 0; i < items.length; i += 1) { items[i].classList.remove('suggested-item-active'); } } }, name: 'autocomplete', props: { classes: { type: String }, displayAttribute: { type: String, "default": 'namse' }, list: { type: Array, required: true }, icons: { type: String, "default": null }, placeholder: { type: String, "default": null }, suggestionsLimit: { type: Number, "default": 4 } }, watch: { list: function list(newV) { this.resultsData = newV; } } }; function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier /* server only */ , shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { if (typeof shadowMode !== 'boolean') { createInjectorSSR = createInjector; createInjector = shadowMode; shadowMode = false; } // Vue.extend constructor export interop. var options = typeof script === 'function' ? script.options : script; // render functions if (template && template.render) { options.render = template.render; options.staticRenderFns = template.staticRenderFns; options._compiled = true; // functional template if (isFunctionalTemplate) { options.functional = true; } } // scopedId if (scopeId) { options._scopeId = scopeId; } var hook; if (moduleIdentifier) { // server build hook = function hook(context) { // 2.3 injection context = context || // cached call this.$vnode && this.$vnode.ssrContext || // stateful this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext; // functional // 2.2 with runInNewContext: true if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { context = __VUE_SSR_CONTEXT__; } // inject component styles if (style) { style.call(this, createInjectorSSR(context)); } // register component module identifier for async chunk inference if (context && context._registeredComponents) { context._registeredComponents.add(moduleIdentifier); } }; // used by ssr in case component is cached and beforeCreate // never gets called options._ssrRegister = hook; } else if (style) { hook = shadowMode ? function () { style.call(this, createInjectorShadow(this.$root.$options.shadowRoot)); } : function (context) { style.call(this, createInjector(context)); }; } if (hook) { if (options.functional) { // register for functional component in vue file var originalRender = options.render; options.render = function renderWithStyleInjection(h, context) { hook.call(context); return originalRender(h, context); }; } else { // inject component registration as beforeCreate hook var existing = options.beforeCreate; options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; } } return script; } var normalizeComponent_1 = normalizeComponent; var isOldIE = typeof navigator !== 'undefined' && /msie [6-9]\\b/.test(navigator.userAgent.toLowerCase()); function createInjector(context) { return function (id, style) { return addStyle(id, style); }; } var HEAD; var styles = {}; function addStyle(id, css) { var group = isOldIE ? css.media || 'default' : id; var style = styles[group] || (styles[group] = { ids: new Set(), styles: [] }); if (!style.ids.has(id)) { style.ids.add(id); var code = css.source; if (css.map) { // https://developer.chrome.com/devtools/docs/javascript-debugging // this makes source maps inside style tags work properly in Chrome code += '\n/*# sourceURL=' + css.map.sources[0] + ' */'; // http://stackoverflow.com/a/26603875 code += '\n/*# sourceMappingURL=data:application/json;base64,' + btoa(unescape(encodeURIComponent(JSON.stringify(css.map)))) + ' */'; } if (!style.element) { style.element = document.createElement('style'); style.element.type = 'text/css'; if (css.media) style.element.setAttribute('media', css.media); if (HEAD === undefined) { HEAD = document.head || document.getElementsByTagName('head')[0]; } HEAD.appendChild(style.element); } if ('styleSheet' in style.element) { style.styles.push(code); style.element.styleSheet.cssText = style.styles.filter(Boolean).join('\n'); } else { var index = style.ids.size - 1; var textNode = document.createTextNode(code); var nodes = style.element.childNodes; if (nodes[index]) style.element.removeChild(nodes[index]); if (nodes.length) style.element.insertBefore(textNode, nodes[index]);else style.element.appendChild(textNode); } } } var browser = createInjector; /* script */ var __vue_script__ = script; /* template */ var __vue_render__ = function __vue_render__() { var _vm = this; var _h = _vm.$createElement; var _c = _vm._self._c || _h; return _c('div', { staticClass: "autocomplete" }, [_c('input', { directives: [{ name: "model", rawName: "v-model", value: _vm.searchText, expression: "searchText" }], "class": _vm.classes, attrs: { "type": "text", "placeholder": _vm.placeholder }, domProps: { "value": _vm.searchText }, on: { "input": [function ($event) { if ($event.target.composing) { return; } _vm.searchText = $event.target.value; }, _vm.autocomplete], "keydown": function keydown($event) { return _vm.currentFocus($event); }, "focus": function focus($event) { return _vm.$emit('onFocus'); } } }), _vm._v(" "), _c('ul', { staticClass: "suggestions" }, _vm._l(_vm.suggestions, function (suggestion) { return _c('li', { key: "sugge" + suggestion.code, staticClass: "suggested-item", on: { "click": function click($event) { _vm.chosenSuggestion(_vm.displayProperty(suggestion)); } } }, [_vm.icons ? _c('i', { "class": _vm.icons }) : _vm._e(), _vm._v("\n " + _vm._s(_vm.displayProperty(suggestion)) + "\n ")]); }), 0)]); }; var __vue_staticRenderFns__ = []; /* style */ var __vue_inject_styles__ = function __vue_inject_styles__(inject) { if (!inject) return; inject("data-v-310c3ede_0", { source: "*[data-v-310c3ede]{padding:0;margin:0}.autocomplete[data-v-310c3ede]{position:relative;display:inline-block;width:100%}input[data-v-310c3ede]{padding:0 10px;width:100%}.suggested-item[data-v-310c3ede]{width:100%;list-style:none;padding:10px 0 10px 5px;cursor:pointer;user-select:none}.suggested-item-active[data-v-310c3ede],.suggested-item[data-v-310c3ede]:hover{background-color:#e9e9e9}ul li i[data-v-310c3ede]{padding:0 15px 0 0}", map: undefined, media: undefined }); }; /* scoped */ var __vue_scope_id__ = "data-v-310c3ede"; /* module identifier */ var __vue_module_identifier__ = undefined; /* functional template */ var __vue_is_functional_template__ = false; /* style inject SSR */ var autocomplete = normalizeComponent_1({ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, __vue_inject_styles__, __vue_script__, __vue_scope_id__, __vue_is_functional_template__, __vue_module_identifier__, browser, undefined); var index = { install: function install(Vue) { Vue.component('autocomplete', autocomplete); } }; module.exports = index;