vue-generic-autocomplete
Version:
Vue addres search autocomplete and suggestion
394 lines (337 loc) • 10.7 kB
JavaScript
/*!
* 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;