diff --git a/README.md b/README.md index be1eb5b..ff8e6f8 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,10 @@ autoComplete.js is a simple, pure vanilla Javascript library progressively desig - Pure Vanilla Javascript - Zero Dependencies - Simple & Lightweight +- Powerful Search Engine with two different modes +- Diacritics Support +- Debounce Support +- Life Cycle Events - Useful plugin API - WAI-ARIA Compliant - Highly Customizable @@ -47,13 +51,13 @@ autoComplete.js is a simple, pure vanilla Javascript library progressively desig `JS` ```html - + ``` `CSS` ```html - + ``` #### Package Manager diff --git a/dist/autoComplete.js b/dist/autoComplete.js index 8d151db..b2ac08a 100644 --- a/dist/autoComplete.js +++ b/dist/autoComplete.js @@ -229,11 +229,11 @@ var checkTrigger = function checkTrigger(query, condition, threshold) { return condition ? condition(query) : query.length >= threshold; }; - var mark = function mark(value, klass) { + var mark = function mark(value, cls) { return create("mark", _objectSpread2({ innerHTML: value - }, typeof klass === "string" && { - "class": klass + }, typeof cls === "string" && { + "class": cls })).outerHTML; }; @@ -349,7 +349,6 @@ eventEmitter("results", ctx); }; - var classes; var Expand = "aria-expanded"; var Active = "aria-activedescendant"; var Selected = "aria-selected"; @@ -404,7 +403,10 @@ eventEmitter("close", ctx); }; var goTo = function goTo(index, ctx) { - var results = ctx.list.getElementsByTagName(ctx.resultItem.tag); + var list = ctx.list, + resultItem = ctx.resultItem; + var results = list.getElementsByTagName(resultItem.tag); + var cls = resultItem.selected ? resultItem.selected.split(" ") : false; if (ctx.isOpen && results.length) { var _results$index$classL; var state = ctx.cursor; @@ -414,12 +416,12 @@ if (state > -1) { var _results$state$classL; results[state].removeAttribute(Selected); - if (classes) (_results$state$classL = results[state].classList).remove.apply(_results$state$classL, _toConsumableArray(classes)); + if (cls) (_results$state$classL = results[state].classList).remove.apply(_results$state$classL, _toConsumableArray(cls)); } results[index].setAttribute(Selected, true); - if (classes) (_results$index$classL = results[index].classList).add.apply(_results$index$classL, _toConsumableArray(classes)); + if (cls) (_results$index$classL = results[index].classList).add.apply(_results$index$classL, _toConsumableArray(cls)); ctx.input.setAttribute(Active, results[ctx.cursor].id); - ctx.list.scrollTop = results[index].offsetTop - ctx.list.clientHeight + results[index].clientHeight + 5; + list.scrollTop = results[index].offsetTop - list.clientHeight + results[index].clientHeight + 5; ctx.feedback.cursor = ctx.cursor; feedback(ctx, index); eventEmitter("navigate", ctx); @@ -446,44 +448,35 @@ var items = Array.from(ctx.list.querySelectorAll(itemTag)); var item = event.target.closest(itemTag); if (item && item.nodeName === itemTag) { - event.preventDefault(); var index = items.indexOf(item); select(ctx, event, index); } }; var navigate = function navigate(event, ctx) { - var key = event.keyCode; - var selected = ctx.resultItem.selected; - if (selected) classes = selected.split(" "); - switch (key) { + switch (event.keyCode) { case 40: case 38: event.preventDefault(); - key === 40 ? next(ctx) : previous(ctx); + event.keyCode === 40 ? next(ctx) : previous(ctx); break; case 13: - event.preventDefault(); - if (ctx.cursor >= 0) { - select(ctx, event); - } + if (!ctx.submit) event.preventDefault(); + if (ctx.cursor >= 0) select(ctx, event); break; case 9: if (ctx.resultsList.tabSelect && ctx.cursor >= 0) { event.preventDefault(); select(ctx, event); - } else { - close(ctx); } break; case 27: - event.preventDefault(); ctx.input.value = ""; close(ctx); break; } }; - function start (ctx) { + function start (ctx, q) { var _this = this; return new Promise(function ($return, $error) { var input, query, trigger, threshold, resultsList, queryVal, condition; @@ -492,7 +485,7 @@ trigger = ctx.trigger; threshold = ctx.threshold; resultsList = ctx.resultsList; - queryVal = getQuery(input); + queryVal = q || getQuery(input); queryVal = query ? query(queryVal) : queryVal; condition = checkTrigger(queryVal, trigger, threshold); if (condition) { @@ -519,7 +512,7 @@ var eventsManager = function eventsManager(events, callback) { for (var element in events) { for (var event in events[element]) { - callback(event, element); + callback(element, event); } } }; @@ -557,17 +550,17 @@ } } }; - eventsManager(privateEvents, function (event, element) { - if (!resultsList && element === "list") return; + eventsManager(privateEvents, function (element, event) { + if (!resultsList && event !== "input") return; if (publicEvents[element][event]) return; publicEvents[element][event] = privateEvents[element][event]; }); - eventsManager(publicEvents, function (event, element) { + eventsManager(publicEvents, function (element, event) { ctx[element].addEventListener(event, publicEvents[element][event]); }); }; var removeEvents = function removeEvents(ctx) { - eventsManager(ctx.events, function (event, element) { + eventsManager(ctx.events, function (element, event) { ctx[element].removeEventListener(event, ctx.events[element][event]); }); }; @@ -628,8 +621,8 @@ prototype.init = function () { init(this); }; - prototype.start = function () { - start(this); + prototype.start = function (query) { + start(this, query); }; prototype.unInit = function () { if (this.wrapper) { diff --git a/dist/autoComplete.js.gz b/dist/autoComplete.js.gz index b885ba1..3640c18 100644 Binary files a/dist/autoComplete.js.gz and b/dist/autoComplete.js.gz differ diff --git a/dist/autoComplete.min.js b/dist/autoComplete.min.js index ca6a951..cc5edec 100644 --- a/dist/autoComplete.min.js +++ b/dist/autoComplete.min.js @@ -1 +1 @@ -var e,t;e=this,t=function(){"use strict";function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function t(t){for(var n=1;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var s,a=!0,u=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){u=!0,s=e},f:function(){try{a||null==n.return||n.return()}finally{if(u)throw s}}}}(n.keys);try{for(f.s();!(l=f.n()).done;)c(l.value)}catch(e){f.e(e)}finally{f.f()}}else c()})),n.filter&&(u=n.filter(u));var c=u.slice(0,s.maxResults);t.feedback={query:e,matches:u,results:c},p("results",t)},v="aria-expanded",y="aria-activedescendant",b="aria-selected",g=function(e,n){e.feedback.selection=t({index:n},e.feedback.results[n])},w=function(e){e.isOpen||((e.wrapper||e.input).setAttribute(v,!0),e.list.removeAttribute("hidden"),e.isOpen=!0,p("open",e))},O=function(e){e.isOpen&&((e.wrapper||e.input).setAttribute(v,!1),e.input.setAttribute(y,""),e.list.setAttribute("hidden",""),e.isOpen=!1,p("close",e))},A=function(e,t){var n=t.list.getElementsByTagName(t.resultItem.tag);if(t.isOpen&&n.length){var r,o,s=t.cursor;e>=n.length&&(e=0),e<0&&(e=n.length-1),t.cursor=e,s>-1&&(n[s].removeAttribute(b),a&&(o=n[s].classList).remove.apply(o,i(a))),n[e].setAttribute(b,!0),a&&(r=n[e].classList).add.apply(r,i(a)),t.input.setAttribute(y,n[t.cursor].id),t.list.scrollTop=n[e].offsetTop-t.list.clientHeight+n[e].clientHeight+5,t.feedback.cursor=t.cursor,g(t,e),p("navigate",t)}},S=function(e){var t=e.cursor+1;A(t,e)},j=function(e){var t=e.cursor-1;A(t,e)},k=function(e,t,n){(n=n>=0?n:e.cursor)<0||(e.feedback.event=t,g(e,n),p("selection",e),O(e))};function L(e){var n=this;return new Promise((function(r,i){var o,s,a,u,l,f,p;return o=e.input,s=e.query,a=e.trigger,u=e.threshold,l=e.resultsList,f=(p=o)instanceof HTMLInputElement||p instanceof HTMLTextAreaElement?p.value:p.innerHTML,function(e,t,n){return t?t(e):e.length>=n}(f=s?s(f):f,a,u)?h(e).then((function(o){try{return e.feedback instanceof Error?r():(m(f,e),l&&function(e){var n=e.resultsList,r=e.list,i=e.resultItem,o=e.feedback;o.query;var s=o.matches,a=o.results;if(e.cursor=-1,r.innerHTML="",s.length||n.noResults){var u=document.createDocumentFragment();a.forEach((function(e,n){var r=c(i.tag,t({id:"".concat(i.id,"_").concat(n),role:"option",innerHTML:e.match,inside:u},i.class&&{class:i.class}));i.element&&i.element(r,e)})),r.append(u),n.element&&n.element(r,o),w(e)}else O(e)}(e),d.call(n))}catch(e){return i(e)}}),i):(O(e),d.call(n));function d(){return r()}}))}var T=function(e,t){for(var n in e)for(var r in e[n])t(r,n)},E=function(e){var n=e.events;e.trigger;var r=e.debounce,i=e.resultsList,o=function(e,t){var n;return function(){clearTimeout(n),n=setTimeout((function(){return e()}),t)}}((function(){return L(e)}),r),s=e.events=t({input:t({},n&&n.input)},i&&{list:n?t({},n.list):{}}),u={input:{input:function(){o()},keydown:function(t){!function(e,t){var n=e.keyCode,r=t.resultItem.selected;switch(r&&(a=r.split(" ")),n){case 40:case 38:e.preventDefault(),40===n?S(t):j(t);break;case 13:e.preventDefault(),t.cursor>=0&&k(t,e);break;case 9:t.resultsList.tabSelect&&t.cursor>=0?(e.preventDefault(),k(t,e)):O(t);break;case 27:e.preventDefault(),t.input.value="",O(t)}}(t,e)},blur:function(){O(e)}},list:{mousedown:function(e){e.preventDefault()},click:function(t){!function(e,t){var n=t.resultItem.tag.toUpperCase(),r=Array.from(t.list.querySelectorAll(n)),i=e.target.closest(n);if(i&&i.nodeName===n){e.preventDefault();var o=r.indexOf(i);k(t,e,o)}}(t,e)}}};T(u,(function(e,t){(i||"list"!==t)&&(s[t][e]||(s[t][e]=u[t][e]))})),T(s,(function(t,n){e[n].addEventListener(t,s[n][t])}))};function x(e){var n=this;return new Promise((function(r,i){var o,s,a,u,l,f;if(o=e.name,s=e.input,a=e.placeHolder,u=e.resultsList,l=e.data,f={role:"combobox","aria-owns":u.id,"aria-haspopup":!0,"aria-expanded":!1},c(s,t(t({"aria-controls":u.id,"aria-autocomplete":"both"},a&&{placeholder:a}),!e.wrapper&&t({},f))),e.wrapper&&(e.wrapper=c("div",t({around:s,class:o+"_wrapper"},f))),u&&(e.list=c(u.tag,t({dest:["string"==typeof u.destination?document.querySelector(u.destination):u.destination(),u.position],id:u.id,role:"listbox",hidden:"hidden"},u.class&&{class:u.class}))),l.cache)return h(e).then((function(e){try{return d.call(n)}catch(e){return i(e)}}),i);function d(){return E(e),p("init",e),r()}return d.call(n)}))}function I(e){var t=e.prototype;t.init=function(){x(this)},t.start=function(){L(this)},t.unInit=function(){if(this.wrapper){var e=this.wrapper.parentNode;e.insertBefore(this.input,this.wrapper),e.removeChild(this.wrapper)}var t;T((t=this).events,(function(e,n){t[n].removeEventListener(e,t.events[n][e])}))},t.open=function(){w(this)},t.close=function(){O(this)},t.goTo=function(e){A(e,this)},t.next=function(){S(this)},t.previous=function(){j(this)},t.select=function(e){k(this,null,e)},e.search=t.search=function(e,t,n){d(e,t,n)}}return function e(t){this.options=t,this.id=e.instances=(e.instances||0)+1,this.name="autoComplete",this.wrapper=1,this.threshold=1,this.debounce=0,this.resultsList={position:"afterend",tag:"ul",maxResults:5},this.resultItem={tag:"li"},function(e){var t=e.id,r=e.name,i=e.options,o=e.resultsList,s=e.resultItem;for(var a in i)if("object"===n(i[a]))for(var u in e[a]||(e[a]={}),i[a])e[a][u]=i[a][u];else e[a]=i[a];e.selector=e.selector||"#"+r,o.destination=o.destination||e.selector,o.id=o.id||r+"_list_"+t,s.id=s.id||r+"_result",e.input="string"==typeof e.selector?document.querySelector(e.selector):e.selector()}(this),I.call(this,e),x(this)}},"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).autoComplete=t(); +var e,t;e=this,t=function(){"use strict";function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function t(t){for(var n=1;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var s,a=!0,u=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){u=!0,s=e},f:function(){try{a||null==n.return||n.return()}finally{if(u)throw s}}}}(n.keys);try{for(f.s();!(l=f.n()).done;)c(l.value)}catch(e){f.e(e)}finally{f.f()}}else c()})),n.filter&&(u=n.filter(u));var c=u.slice(0,s.maxResults);t.feedback={query:e,matches:u,results:c},f("results",t)},m="aria-expanded",v="aria-activedescendant",y="aria-selected",b=function(e,n){e.feedback.selection=t({index:n},e.feedback.results[n])},g=function(e){e.isOpen||((e.wrapper||e.input).setAttribute(m,!0),e.list.removeAttribute("hidden"),e.isOpen=!0,f("open",e))},w=function(e){e.isOpen&&((e.wrapper||e.input).setAttribute(m,!1),e.input.setAttribute(v,""),e.list.setAttribute("hidden",""),e.isOpen=!1,f("close",e))},O=function(e,t){var n=t.list,r=t.resultItem,o=n.getElementsByTagName(r.tag),s=!!r.selected&&r.selected.split(" ");if(t.isOpen&&o.length){var a,u,c=t.cursor;e>=o.length&&(e=0),e<0&&(e=o.length-1),t.cursor=e,c>-1&&(o[c].removeAttribute(y),s&&(u=o[c].classList).remove.apply(u,i(s))),o[e].setAttribute(y,!0),s&&(a=o[e].classList).add.apply(a,i(s)),t.input.setAttribute(v,o[t.cursor].id),n.scrollTop=o[e].offsetTop-n.clientHeight+o[e].clientHeight+5,t.feedback.cursor=t.cursor,b(t,e),f("navigate",t)}},A=function(e){var t=e.cursor+1;O(t,e)},S=function(e){var t=e.cursor-1;O(t,e)},k=function(e,t,n){(n=n>=0?n:e.cursor)<0||(e.feedback.event=t,b(e,n),f("selection",e),w(e))};function j(e,n){var r=this;return new Promise((function(i,o){var s,a,c,l,f,p,m;return s=e.input,a=e.query,c=e.trigger,l=e.threshold,f=e.resultsList,p=n||((m=s)instanceof HTMLInputElement||m instanceof HTMLTextAreaElement?m.value:m.innerHTML),function(e,t,n){return t?t(e):e.length>=n}(p=a?a(p):p,c,l)?d(e).then((function(n){try{return e.feedback instanceof Error?i():(h(p,e),f&&function(e){var n=e.resultsList,r=e.list,i=e.resultItem,o=e.feedback;o.query;var s=o.matches,a=o.results;if(e.cursor=-1,r.innerHTML="",s.length||n.noResults){var c=document.createDocumentFragment();a.forEach((function(e,n){var r=u(i.tag,t({id:"".concat(i.id,"_").concat(n),role:"option",innerHTML:e.match,inside:c},i.class&&{class:i.class}));i.element&&i.element(r,e)})),r.append(c),n.element&&n.element(r,o),g(e)}else w(e)}(e),v.call(r))}catch(e){return o(e)}}),o):(w(e),v.call(r));function v(){return i()}}))}var L=function(e,t){for(var n in e)for(var r in e[n])t(n,r)},T=function(e){var n=e.events;e.trigger;var r=e.debounce,i=e.resultsList,o=function(e,t){var n;return function(){clearTimeout(n),n=setTimeout((function(){return e()}),t)}}((function(){return j(e)}),r),s=e.events=t({input:t({},n&&n.input)},i&&{list:n?t({},n.list):{}}),a={input:{input:function(){o()},keydown:function(t){!function(e,t){switch(e.keyCode){case 40:case 38:e.preventDefault(),40===e.keyCode?A(t):S(t);break;case 13:t.submit||e.preventDefault(),t.cursor>=0&&k(t,e);break;case 9:t.resultsList.tabSelect&&t.cursor>=0&&(e.preventDefault(),k(t,e));break;case 27:t.input.value="",w(t)}}(t,e)},blur:function(){w(e)}},list:{mousedown:function(e){e.preventDefault()},click:function(t){!function(e,t){var n=t.resultItem.tag.toUpperCase(),r=Array.from(t.list.querySelectorAll(n)),i=e.target.closest(n);if(i&&i.nodeName===n){var o=r.indexOf(i);k(t,e,o)}}(t,e)}}};L(a,(function(e,t){(i||"input"===t)&&(s[e][t]||(s[e][t]=a[e][t]))})),L(s,(function(t,n){e[t].addEventListener(n,s[t][n])}))};function E(e){var n=this;return new Promise((function(r,i){var o,s,a,c,l,p;if(o=e.name,s=e.input,a=e.placeHolder,c=e.resultsList,l=e.data,p={role:"combobox","aria-owns":c.id,"aria-haspopup":!0,"aria-expanded":!1},u(s,t(t({"aria-controls":c.id,"aria-autocomplete":"both"},a&&{placeholder:a}),!e.wrapper&&t({},p))),e.wrapper&&(e.wrapper=u("div",t({around:s,class:o+"_wrapper"},p))),c&&(e.list=u(c.tag,t({dest:["string"==typeof c.destination?document.querySelector(c.destination):c.destination(),c.position],id:c.id,role:"listbox",hidden:"hidden"},c.class&&{class:c.class}))),l.cache)return d(e).then((function(e){try{return h.call(n)}catch(e){return i(e)}}),i);function h(){return T(e),f("init",e),r()}return h.call(n)}))}function x(e){var t=e.prototype;t.init=function(){E(this)},t.start=function(e){j(this,e)},t.unInit=function(){if(this.wrapper){var e=this.wrapper.parentNode;e.insertBefore(this.input,this.wrapper),e.removeChild(this.wrapper)}var t;L((t=this).events,(function(e,n){t[e].removeEventListener(n,t.events[e][n])}))},t.open=function(){g(this)},t.close=function(){w(this)},t.goTo=function(e){O(e,this)},t.next=function(){A(this)},t.previous=function(){S(this)},t.select=function(e){k(this,null,e)},e.search=t.search=function(e,t,n){p(e,t,n)}}return function e(t){this.options=t,this.id=e.instances=(e.instances||0)+1,this.name="autoComplete",this.wrapper=1,this.threshold=1,this.debounce=0,this.resultsList={position:"afterend",tag:"ul",maxResults:5},this.resultItem={tag:"li"},function(e){var t=e.id,r=e.name,i=e.options,o=e.resultsList,s=e.resultItem;for(var a in i)if("object"===n(i[a]))for(var u in e[a]||(e[a]={}),i[a])e[a][u]=i[a][u];else e[a]=i[a];e.selector=e.selector||"#"+r,o.destination=o.destination||e.selector,o.id=o.id||r+"_list_"+t,s.id=s.id||r+"_result",e.input="string"==typeof e.selector?document.querySelector(e.selector):e.selector()}(this),x.call(this,e),E(this)}},"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).autoComplete=t(); diff --git a/dist/autoComplete.min.js.gz b/dist/autoComplete.min.js.gz index 278cd34..0d94ffc 100644 Binary files a/dist/autoComplete.min.js.gz and b/dist/autoComplete.min.js.gz differ diff --git a/docs/configuration.md b/docs/configuration.md index 3ab0c90..8983d3c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -10,7 +10,7 @@ API configuration options, methods and events ### name (optional) -> Global instance name where all elements inherit their class & id names +> Responsible for the global instance naming where all elements inherit their class & id names - Type: `String` - Default: `autoComplete` @@ -26,9 +26,9 @@ name: "autoComplete", ### selector (optional) -> Input field selector +> Responsible for the input, textarea, or contentEditable element selection -- Type: `String` of [selector](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Selectors) | `Function` +- Type: `String` of [selector](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Selectors) | `Function` returns `Element` - Default: `#autoComplete` ##### Example @@ -54,7 +54,7 @@ selector: () => { ### wrapper (optional) -> Wrapper div container +> Responsible for rendering the `div` that wraps the `input` and the `list` element - Type: `Boolean` - Default: `true` @@ -69,7 +69,7 @@ wrapper: false, ### data (required) -> Data Source +> Responsible for the data source selection - Type: `Object` @@ -154,7 +154,7 @@ data: { ### trigger (optional) -> Event & Condition rules that trigger autoComplete.js engine to start +> Responsible for Event & Condition rules that trigger autoComplete.js engine to start - Type: `Function` returns `Boolean` - Parameters: (`query`) @@ -172,7 +172,7 @@ trigger: (query) => { ### query (optional) -> Query interceptor +> Responsible for Query interception & manipulation - Type: `Function` returns `String` - Parameters: (`input`) @@ -190,7 +190,7 @@ query: (input) => { ### placeHolder (optional) -> Input field placeholder value +> Responsible for the input field placeholder value setting - Type: `String` - Default: `Blank/Empty` @@ -205,7 +205,7 @@ placeHolder: "Search...", ### threshold (optional) -> Minimum characters length for autoComplete.js engine to start +> Responsible for setting threshold value of the minimum characters length where autoComplete.js engine starts - Type: `Integer` - Default: `1` @@ -220,7 +220,7 @@ threshold: 2, ### debounce (optional) -> Delay duration after done typing for autoComplete.js engine to start +> Responsible for setting delay time duration that counts after typing is done for autoComplete.js engine to start - Type: `Integer` - Default: `0` @@ -235,7 +235,7 @@ debounce: 300, // Milliseconds value ### searchEngine (optional) -> Search engine Type/Mode +> Responsible for setting the Search engine Type/Mode or custom engine - Type: `String` | `Function` - `String` lowerCase `"strict"` | `"loose"` @@ -252,7 +252,7 @@ searchEngine: "strict", ### diacritics (optional) -> Search engine diacritics handler +> Responsible for turning on/off language diacritics supported search - Type: `Boolean` - Default: `false` @@ -267,7 +267,7 @@ diacritics: true, ### resultsList (optional) -> Rendered results list element interceptor and customizer +> Responsible for the results list element rendering, interception, and customizing - Type: `Object` | `Boolean` for disabling list rendering @@ -331,7 +331,7 @@ resultsList: { ### resultItem (optional) -> Rendered result item element customizer and interceptor +> Responsible for the result item element rendering, interception, and customizing - Type: `Object` @@ -378,9 +378,24 @@ resultItem: { *** +### submit (optional) + +> Responsible for the `Enter` button default behavior + +- Type: `Boolean` +- Default: `false` + +##### Example: + +```js +submit: true, +``` + +*** + ### events (optional) -> Input field & Results list events additions or overriding +> Responsible for the input field and results list events additions or overriding - Type: `Object` - input: `Object` of functions with the [event](https://developer.mozilla.org/en-US/docs/Web/Events) type name @@ -432,21 +447,27 @@ autoCompleteJS.init(); *** -### start() +### start(query) -> Runs `start()` core function which is responsible for the following tasks in order: +> Runs `start(query)` core function which is responsible for the following tasks in order: -1. Getting the `input` query value +1. Getting the `input` query value if NOT passed as an argument 2. Manipulating `query` value 3. Checking `trigger` condition validity to proceed 4. Fetching `data` from `src` or `store` if cached 5. Start matching results 6. Rendering `list` if enabled +Arguments: +- query: `String` (optional) + +Defaults: +- query: `input` query value + ##### Example: ```js -autoCompleteJS.start(); +autoCompleteJS.start("tea"); ``` *** @@ -455,10 +476,10 @@ autoCompleteJS.start(); > autoComplete.js powerful search engine -Parameters: +Arguments: - query: `String` - record: `String` -- options: `Object` +- options: `Object` (optional) - mode: `String` - `"strict"` search mode - `"loose"` search mode @@ -523,7 +544,7 @@ autoCompleteJS.previous(); > Navigates to a specific `resultItem` on the list by its `index` number -Parameters: +Arguments: - index: `Number` Defaults: @@ -542,8 +563,8 @@ autoCompleteJS.gotTo(1); > Selects a `resultItem` from the list by its `index` number -Parameters: -- index: `Number` +Arguments: +- index: `Number` (optional) Defaults: - index: current cursor position @@ -570,7 +591,7 @@ autoCompleteJS.close(); ### unInit() -> Removes all the event listeners on the `_events` list +> Removes all the event listeners on the `events` list ##### Example: diff --git a/docs/demo/index.html b/docs/demo/index.html index d5d5f7b..c6e74dc 100644 --- a/docs/demo/index.html +++ b/docs/demo/index.html @@ -72,7 +72,7 @@ + href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.0/dist/css/autoComplete.min.css"> @@ -150,7 +150,7 @@

mode

- + diff --git a/docs/demo/js/autoComplete.js b/docs/demo/js/autoComplete.js index 8d151db..b2ac08a 100644 --- a/docs/demo/js/autoComplete.js +++ b/docs/demo/js/autoComplete.js @@ -229,11 +229,11 @@ var checkTrigger = function checkTrigger(query, condition, threshold) { return condition ? condition(query) : query.length >= threshold; }; - var mark = function mark(value, klass) { + var mark = function mark(value, cls) { return create("mark", _objectSpread2({ innerHTML: value - }, typeof klass === "string" && { - "class": klass + }, typeof cls === "string" && { + "class": cls })).outerHTML; }; @@ -349,7 +349,6 @@ eventEmitter("results", ctx); }; - var classes; var Expand = "aria-expanded"; var Active = "aria-activedescendant"; var Selected = "aria-selected"; @@ -404,7 +403,10 @@ eventEmitter("close", ctx); }; var goTo = function goTo(index, ctx) { - var results = ctx.list.getElementsByTagName(ctx.resultItem.tag); + var list = ctx.list, + resultItem = ctx.resultItem; + var results = list.getElementsByTagName(resultItem.tag); + var cls = resultItem.selected ? resultItem.selected.split(" ") : false; if (ctx.isOpen && results.length) { var _results$index$classL; var state = ctx.cursor; @@ -414,12 +416,12 @@ if (state > -1) { var _results$state$classL; results[state].removeAttribute(Selected); - if (classes) (_results$state$classL = results[state].classList).remove.apply(_results$state$classL, _toConsumableArray(classes)); + if (cls) (_results$state$classL = results[state].classList).remove.apply(_results$state$classL, _toConsumableArray(cls)); } results[index].setAttribute(Selected, true); - if (classes) (_results$index$classL = results[index].classList).add.apply(_results$index$classL, _toConsumableArray(classes)); + if (cls) (_results$index$classL = results[index].classList).add.apply(_results$index$classL, _toConsumableArray(cls)); ctx.input.setAttribute(Active, results[ctx.cursor].id); - ctx.list.scrollTop = results[index].offsetTop - ctx.list.clientHeight + results[index].clientHeight + 5; + list.scrollTop = results[index].offsetTop - list.clientHeight + results[index].clientHeight + 5; ctx.feedback.cursor = ctx.cursor; feedback(ctx, index); eventEmitter("navigate", ctx); @@ -446,44 +448,35 @@ var items = Array.from(ctx.list.querySelectorAll(itemTag)); var item = event.target.closest(itemTag); if (item && item.nodeName === itemTag) { - event.preventDefault(); var index = items.indexOf(item); select(ctx, event, index); } }; var navigate = function navigate(event, ctx) { - var key = event.keyCode; - var selected = ctx.resultItem.selected; - if (selected) classes = selected.split(" "); - switch (key) { + switch (event.keyCode) { case 40: case 38: event.preventDefault(); - key === 40 ? next(ctx) : previous(ctx); + event.keyCode === 40 ? next(ctx) : previous(ctx); break; case 13: - event.preventDefault(); - if (ctx.cursor >= 0) { - select(ctx, event); - } + if (!ctx.submit) event.preventDefault(); + if (ctx.cursor >= 0) select(ctx, event); break; case 9: if (ctx.resultsList.tabSelect && ctx.cursor >= 0) { event.preventDefault(); select(ctx, event); - } else { - close(ctx); } break; case 27: - event.preventDefault(); ctx.input.value = ""; close(ctx); break; } }; - function start (ctx) { + function start (ctx, q) { var _this = this; return new Promise(function ($return, $error) { var input, query, trigger, threshold, resultsList, queryVal, condition; @@ -492,7 +485,7 @@ trigger = ctx.trigger; threshold = ctx.threshold; resultsList = ctx.resultsList; - queryVal = getQuery(input); + queryVal = q || getQuery(input); queryVal = query ? query(queryVal) : queryVal; condition = checkTrigger(queryVal, trigger, threshold); if (condition) { @@ -519,7 +512,7 @@ var eventsManager = function eventsManager(events, callback) { for (var element in events) { for (var event in events[element]) { - callback(event, element); + callback(element, event); } } }; @@ -557,17 +550,17 @@ } } }; - eventsManager(privateEvents, function (event, element) { - if (!resultsList && element === "list") return; + eventsManager(privateEvents, function (element, event) { + if (!resultsList && event !== "input") return; if (publicEvents[element][event]) return; publicEvents[element][event] = privateEvents[element][event]; }); - eventsManager(publicEvents, function (event, element) { + eventsManager(publicEvents, function (element, event) { ctx[element].addEventListener(event, publicEvents[element][event]); }); }; var removeEvents = function removeEvents(ctx) { - eventsManager(ctx.events, function (event, element) { + eventsManager(ctx.events, function (element, event) { ctx[element].removeEventListener(event, ctx.events[element][event]); }); }; @@ -628,8 +621,8 @@ prototype.init = function () { init(this); }; - prototype.start = function () { - start(this); + prototype.start = function (query) { + start(this, query); }; prototype.unInit = function () { if (this.wrapper) { diff --git a/docs/demo/js/autoComplete.js.gz b/docs/demo/js/autoComplete.js.gz index b885ba1..3640c18 100644 Binary files a/docs/demo/js/autoComplete.js.gz and b/docs/demo/js/autoComplete.js.gz differ diff --git a/docs/demo/js/autoComplete.min.js b/docs/demo/js/autoComplete.min.js index ca6a951..cc5edec 100644 --- a/docs/demo/js/autoComplete.min.js +++ b/docs/demo/js/autoComplete.min.js @@ -1 +1 @@ -var e,t;e=this,t=function(){"use strict";function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function t(t){for(var n=1;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var s,a=!0,u=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){u=!0,s=e},f:function(){try{a||null==n.return||n.return()}finally{if(u)throw s}}}}(n.keys);try{for(f.s();!(l=f.n()).done;)c(l.value)}catch(e){f.e(e)}finally{f.f()}}else c()})),n.filter&&(u=n.filter(u));var c=u.slice(0,s.maxResults);t.feedback={query:e,matches:u,results:c},p("results",t)},v="aria-expanded",y="aria-activedescendant",b="aria-selected",g=function(e,n){e.feedback.selection=t({index:n},e.feedback.results[n])},w=function(e){e.isOpen||((e.wrapper||e.input).setAttribute(v,!0),e.list.removeAttribute("hidden"),e.isOpen=!0,p("open",e))},O=function(e){e.isOpen&&((e.wrapper||e.input).setAttribute(v,!1),e.input.setAttribute(y,""),e.list.setAttribute("hidden",""),e.isOpen=!1,p("close",e))},A=function(e,t){var n=t.list.getElementsByTagName(t.resultItem.tag);if(t.isOpen&&n.length){var r,o,s=t.cursor;e>=n.length&&(e=0),e<0&&(e=n.length-1),t.cursor=e,s>-1&&(n[s].removeAttribute(b),a&&(o=n[s].classList).remove.apply(o,i(a))),n[e].setAttribute(b,!0),a&&(r=n[e].classList).add.apply(r,i(a)),t.input.setAttribute(y,n[t.cursor].id),t.list.scrollTop=n[e].offsetTop-t.list.clientHeight+n[e].clientHeight+5,t.feedback.cursor=t.cursor,g(t,e),p("navigate",t)}},S=function(e){var t=e.cursor+1;A(t,e)},j=function(e){var t=e.cursor-1;A(t,e)},k=function(e,t,n){(n=n>=0?n:e.cursor)<0||(e.feedback.event=t,g(e,n),p("selection",e),O(e))};function L(e){var n=this;return new Promise((function(r,i){var o,s,a,u,l,f,p;return o=e.input,s=e.query,a=e.trigger,u=e.threshold,l=e.resultsList,f=(p=o)instanceof HTMLInputElement||p instanceof HTMLTextAreaElement?p.value:p.innerHTML,function(e,t,n){return t?t(e):e.length>=n}(f=s?s(f):f,a,u)?h(e).then((function(o){try{return e.feedback instanceof Error?r():(m(f,e),l&&function(e){var n=e.resultsList,r=e.list,i=e.resultItem,o=e.feedback;o.query;var s=o.matches,a=o.results;if(e.cursor=-1,r.innerHTML="",s.length||n.noResults){var u=document.createDocumentFragment();a.forEach((function(e,n){var r=c(i.tag,t({id:"".concat(i.id,"_").concat(n),role:"option",innerHTML:e.match,inside:u},i.class&&{class:i.class}));i.element&&i.element(r,e)})),r.append(u),n.element&&n.element(r,o),w(e)}else O(e)}(e),d.call(n))}catch(e){return i(e)}}),i):(O(e),d.call(n));function d(){return r()}}))}var T=function(e,t){for(var n in e)for(var r in e[n])t(r,n)},E=function(e){var n=e.events;e.trigger;var r=e.debounce,i=e.resultsList,o=function(e,t){var n;return function(){clearTimeout(n),n=setTimeout((function(){return e()}),t)}}((function(){return L(e)}),r),s=e.events=t({input:t({},n&&n.input)},i&&{list:n?t({},n.list):{}}),u={input:{input:function(){o()},keydown:function(t){!function(e,t){var n=e.keyCode,r=t.resultItem.selected;switch(r&&(a=r.split(" ")),n){case 40:case 38:e.preventDefault(),40===n?S(t):j(t);break;case 13:e.preventDefault(),t.cursor>=0&&k(t,e);break;case 9:t.resultsList.tabSelect&&t.cursor>=0?(e.preventDefault(),k(t,e)):O(t);break;case 27:e.preventDefault(),t.input.value="",O(t)}}(t,e)},blur:function(){O(e)}},list:{mousedown:function(e){e.preventDefault()},click:function(t){!function(e,t){var n=t.resultItem.tag.toUpperCase(),r=Array.from(t.list.querySelectorAll(n)),i=e.target.closest(n);if(i&&i.nodeName===n){e.preventDefault();var o=r.indexOf(i);k(t,e,o)}}(t,e)}}};T(u,(function(e,t){(i||"list"!==t)&&(s[t][e]||(s[t][e]=u[t][e]))})),T(s,(function(t,n){e[n].addEventListener(t,s[n][t])}))};function x(e){var n=this;return new Promise((function(r,i){var o,s,a,u,l,f;if(o=e.name,s=e.input,a=e.placeHolder,u=e.resultsList,l=e.data,f={role:"combobox","aria-owns":u.id,"aria-haspopup":!0,"aria-expanded":!1},c(s,t(t({"aria-controls":u.id,"aria-autocomplete":"both"},a&&{placeholder:a}),!e.wrapper&&t({},f))),e.wrapper&&(e.wrapper=c("div",t({around:s,class:o+"_wrapper"},f))),u&&(e.list=c(u.tag,t({dest:["string"==typeof u.destination?document.querySelector(u.destination):u.destination(),u.position],id:u.id,role:"listbox",hidden:"hidden"},u.class&&{class:u.class}))),l.cache)return h(e).then((function(e){try{return d.call(n)}catch(e){return i(e)}}),i);function d(){return E(e),p("init",e),r()}return d.call(n)}))}function I(e){var t=e.prototype;t.init=function(){x(this)},t.start=function(){L(this)},t.unInit=function(){if(this.wrapper){var e=this.wrapper.parentNode;e.insertBefore(this.input,this.wrapper),e.removeChild(this.wrapper)}var t;T((t=this).events,(function(e,n){t[n].removeEventListener(e,t.events[n][e])}))},t.open=function(){w(this)},t.close=function(){O(this)},t.goTo=function(e){A(e,this)},t.next=function(){S(this)},t.previous=function(){j(this)},t.select=function(e){k(this,null,e)},e.search=t.search=function(e,t,n){d(e,t,n)}}return function e(t){this.options=t,this.id=e.instances=(e.instances||0)+1,this.name="autoComplete",this.wrapper=1,this.threshold=1,this.debounce=0,this.resultsList={position:"afterend",tag:"ul",maxResults:5},this.resultItem={tag:"li"},function(e){var t=e.id,r=e.name,i=e.options,o=e.resultsList,s=e.resultItem;for(var a in i)if("object"===n(i[a]))for(var u in e[a]||(e[a]={}),i[a])e[a][u]=i[a][u];else e[a]=i[a];e.selector=e.selector||"#"+r,o.destination=o.destination||e.selector,o.id=o.id||r+"_list_"+t,s.id=s.id||r+"_result",e.input="string"==typeof e.selector?document.querySelector(e.selector):e.selector()}(this),I.call(this,e),x(this)}},"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).autoComplete=t(); +var e,t;e=this,t=function(){"use strict";function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function t(t){for(var n=1;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var s,a=!0,u=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){u=!0,s=e},f:function(){try{a||null==n.return||n.return()}finally{if(u)throw s}}}}(n.keys);try{for(f.s();!(l=f.n()).done;)c(l.value)}catch(e){f.e(e)}finally{f.f()}}else c()})),n.filter&&(u=n.filter(u));var c=u.slice(0,s.maxResults);t.feedback={query:e,matches:u,results:c},f("results",t)},m="aria-expanded",v="aria-activedescendant",y="aria-selected",b=function(e,n){e.feedback.selection=t({index:n},e.feedback.results[n])},g=function(e){e.isOpen||((e.wrapper||e.input).setAttribute(m,!0),e.list.removeAttribute("hidden"),e.isOpen=!0,f("open",e))},w=function(e){e.isOpen&&((e.wrapper||e.input).setAttribute(m,!1),e.input.setAttribute(v,""),e.list.setAttribute("hidden",""),e.isOpen=!1,f("close",e))},O=function(e,t){var n=t.list,r=t.resultItem,o=n.getElementsByTagName(r.tag),s=!!r.selected&&r.selected.split(" ");if(t.isOpen&&o.length){var a,u,c=t.cursor;e>=o.length&&(e=0),e<0&&(e=o.length-1),t.cursor=e,c>-1&&(o[c].removeAttribute(y),s&&(u=o[c].classList).remove.apply(u,i(s))),o[e].setAttribute(y,!0),s&&(a=o[e].classList).add.apply(a,i(s)),t.input.setAttribute(v,o[t.cursor].id),n.scrollTop=o[e].offsetTop-n.clientHeight+o[e].clientHeight+5,t.feedback.cursor=t.cursor,b(t,e),f("navigate",t)}},A=function(e){var t=e.cursor+1;O(t,e)},S=function(e){var t=e.cursor-1;O(t,e)},k=function(e,t,n){(n=n>=0?n:e.cursor)<0||(e.feedback.event=t,b(e,n),f("selection",e),w(e))};function j(e,n){var r=this;return new Promise((function(i,o){var s,a,c,l,f,p,m;return s=e.input,a=e.query,c=e.trigger,l=e.threshold,f=e.resultsList,p=n||((m=s)instanceof HTMLInputElement||m instanceof HTMLTextAreaElement?m.value:m.innerHTML),function(e,t,n){return t?t(e):e.length>=n}(p=a?a(p):p,c,l)?d(e).then((function(n){try{return e.feedback instanceof Error?i():(h(p,e),f&&function(e){var n=e.resultsList,r=e.list,i=e.resultItem,o=e.feedback;o.query;var s=o.matches,a=o.results;if(e.cursor=-1,r.innerHTML="",s.length||n.noResults){var c=document.createDocumentFragment();a.forEach((function(e,n){var r=u(i.tag,t({id:"".concat(i.id,"_").concat(n),role:"option",innerHTML:e.match,inside:c},i.class&&{class:i.class}));i.element&&i.element(r,e)})),r.append(c),n.element&&n.element(r,o),g(e)}else w(e)}(e),v.call(r))}catch(e){return o(e)}}),o):(w(e),v.call(r));function v(){return i()}}))}var L=function(e,t){for(var n in e)for(var r in e[n])t(n,r)},T=function(e){var n=e.events;e.trigger;var r=e.debounce,i=e.resultsList,o=function(e,t){var n;return function(){clearTimeout(n),n=setTimeout((function(){return e()}),t)}}((function(){return j(e)}),r),s=e.events=t({input:t({},n&&n.input)},i&&{list:n?t({},n.list):{}}),a={input:{input:function(){o()},keydown:function(t){!function(e,t){switch(e.keyCode){case 40:case 38:e.preventDefault(),40===e.keyCode?A(t):S(t);break;case 13:t.submit||e.preventDefault(),t.cursor>=0&&k(t,e);break;case 9:t.resultsList.tabSelect&&t.cursor>=0&&(e.preventDefault(),k(t,e));break;case 27:t.input.value="",w(t)}}(t,e)},blur:function(){w(e)}},list:{mousedown:function(e){e.preventDefault()},click:function(t){!function(e,t){var n=t.resultItem.tag.toUpperCase(),r=Array.from(t.list.querySelectorAll(n)),i=e.target.closest(n);if(i&&i.nodeName===n){var o=r.indexOf(i);k(t,e,o)}}(t,e)}}};L(a,(function(e,t){(i||"input"===t)&&(s[e][t]||(s[e][t]=a[e][t]))})),L(s,(function(t,n){e[t].addEventListener(n,s[t][n])}))};function E(e){var n=this;return new Promise((function(r,i){var o,s,a,c,l,p;if(o=e.name,s=e.input,a=e.placeHolder,c=e.resultsList,l=e.data,p={role:"combobox","aria-owns":c.id,"aria-haspopup":!0,"aria-expanded":!1},u(s,t(t({"aria-controls":c.id,"aria-autocomplete":"both"},a&&{placeholder:a}),!e.wrapper&&t({},p))),e.wrapper&&(e.wrapper=u("div",t({around:s,class:o+"_wrapper"},p))),c&&(e.list=u(c.tag,t({dest:["string"==typeof c.destination?document.querySelector(c.destination):c.destination(),c.position],id:c.id,role:"listbox",hidden:"hidden"},c.class&&{class:c.class}))),l.cache)return d(e).then((function(e){try{return h.call(n)}catch(e){return i(e)}}),i);function h(){return T(e),f("init",e),r()}return h.call(n)}))}function x(e){var t=e.prototype;t.init=function(){E(this)},t.start=function(e){j(this,e)},t.unInit=function(){if(this.wrapper){var e=this.wrapper.parentNode;e.insertBefore(this.input,this.wrapper),e.removeChild(this.wrapper)}var t;L((t=this).events,(function(e,n){t[e].removeEventListener(n,t.events[e][n])}))},t.open=function(){g(this)},t.close=function(){w(this)},t.goTo=function(e){O(e,this)},t.next=function(){A(this)},t.previous=function(){S(this)},t.select=function(e){k(this,null,e)},e.search=t.search=function(e,t,n){p(e,t,n)}}return function e(t){this.options=t,this.id=e.instances=(e.instances||0)+1,this.name="autoComplete",this.wrapper=1,this.threshold=1,this.debounce=0,this.resultsList={position:"afterend",tag:"ul",maxResults:5},this.resultItem={tag:"li"},function(e){var t=e.id,r=e.name,i=e.options,o=e.resultsList,s=e.resultItem;for(var a in i)if("object"===n(i[a]))for(var u in e[a]||(e[a]={}),i[a])e[a][u]=i[a][u];else e[a]=i[a];e.selector=e.selector||"#"+r,o.destination=o.destination||e.selector,o.id=o.id||r+"_list_"+t,s.id=s.id||r+"_result",e.input="string"==typeof e.selector?document.querySelector(e.selector):e.selector()}(this),x.call(this,e),E(this)}},"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).autoComplete=t(); diff --git a/docs/demo/js/autoComplete.min.js.gz b/docs/demo/js/autoComplete.min.js.gz index 278cd34..0d94ffc 100644 Binary files a/docs/demo/js/autoComplete.min.js.gz and b/docs/demo/js/autoComplete.min.js.gz differ diff --git a/docs/demo/js/index.js b/docs/demo/js/index.js index 2cb03ac..efbd7d3 100644 --- a/docs/demo/js/index.js +++ b/docs/demo/js/index.js @@ -60,10 +60,10 @@ const autoCompleteJS = new autoComplete({ }, events: { input: { - focus: () => { + focus() { if (autoCompleteJS.input.value.length) autoCompleteJS.start(); }, - selection: (event) => { + selection(event) { const feedback = event.detail; autoCompleteJS.input.blur(); // Prepare User's Selected Value diff --git a/docs/how-to-guides.md b/docs/how-to-guides.md index ba99e8a..daa4b32 100644 --- a/docs/how-to-guides.md +++ b/docs/how-to-guides.md @@ -14,7 +14,7 @@ Guided examples on how-to use autoComplete.js in different use-cases // autoComplete.js Config Options events: { input: { - focus: () => { + focus() { const inputValue = document.querySelector("#autoComplete").value; if (inputValue.length) autoCompleteJS.start(); @@ -86,7 +86,7 @@ query: (query) => { }, events: { input: { - selection: (event) => { + selection(event) { const feedback = event.detail; const input = document.querySelector("#autoComplete"); // Trim selected Value @@ -154,7 +154,7 @@ resultsList: { }, events: { input: { - selection: (event) => { + selection(event) { const feedback = event.detail; const input = document.querySelector("#autoComplete"); // Get selected Value @@ -212,7 +212,7 @@ filter: (list) => { }; const placeHolder = "Pizza, Burger, Sushi"; const resultsList= { - element: (list, data) => { + element(list, data) { if (!data.results.length) { // Create "No Results" message list element const message = document.createElement("div"); @@ -237,10 +237,10 @@ filter: (list) => { resultItem, events: { input: { - focus: () => { + focus() { if (autoCompleteJS_01.input.value.length) autoCompleteJS_01.start(); }, - selection: (event) => { + selection(event) { const selection = event.detail.selection.value; autoCompleteJS_01.input.value = selection; } @@ -256,7 +256,7 @@ filter: (list) => { resultItem, events: { input: { - selection: (event) => { + selection(event) { const selection = event.detail.selection.value; autoCompleteJS_02.input.value = selection; } @@ -268,7 +268,7 @@ filter: (list) => { selector: "#autoComplete_03", placeHolder, data, - query: (query) => { + query(query) { // Split query into array const querySplit = query.split(","); // Get last query value index @@ -282,7 +282,7 @@ filter: (list) => { resultItem, events: { input: { - selection: (event) => { + selection(event) { const feedback = event.detail; const input = autoCompleteJS_03.input; // Trim selected Value @@ -307,7 +307,7 @@ filter: (list) => { placeHolder, data, resultsList: { - element: (list) => { + element(list) { const recentSearch = history.reverse(); const historyLength = recentSearch.length; @@ -334,7 +334,7 @@ filter: (list) => { resultItem, events: { input: { - selection: (event) => { + selection(event) { const feedback = event.detail; const input = autoCompleteJS_04.input; // Get selected Value @@ -353,7 +353,7 @@ filter: (list) => { placeHolder, data: { ...data, - filter: (list) => { + filter(list) { const results = list.filter((item) => { const inputValue = document.querySelector("#autoComplete_05").value.toLowerCase(); const itemValue = item.value.toLowerCase(); @@ -370,7 +370,7 @@ filter: (list) => { resultItem, events: { input: { - selection: (event) => { + selection(event) { const selection = event.detail.selection.value; autoCompleteJS_05.input.value = selection; } diff --git a/docs/index.html b/docs/index.html index 416839d..56df251 100644 --- a/docs/index.html +++ b/docs/index.html @@ -4,10 +4,10 @@ autoComplete.js - Vanilla Javascript library - + + content="@tarekraafat/autocomplete.js, auto complete js, autocomplete, autocomplete address library, autocomplete ajax library, autocomplete api, autocomplete api javascript, autocomplete autosuggest js, autocomplete custom, autocomplete dropdown, autocomplete dropdown in html, autocomplete dropdown javascript, autocomplete full text search, autocomplete html, autocomplete in javascript, autocomplete in js, autocomplete input javascript, autocomplete input js, autocomplete javascript, autocomplete javascript code, autocomplete javascript example, autocomplete javascript github, autocomplete javascript input, autocomplete javascript library, autocomplete javascript plugin, autocomplete js ajax, autocomplete js cdn, autocomplete js codepen, autocomplete js example, autocomplete js github, autocomplete js library, autocomplete js npm, autocomplete library, autocomplete light, autocomplete light js, autocomplete node js, autocomplete open, autocomplete open event, autocomplete open source, autocomplete openonfocus, autocomplete pure javascript, autocomplete search javascript, autocomplete search js, autocomplete select library, autocomplete text library, autocomplete textbox library, autocomplete tutorial, autocomplete tutorial ajax, autocomplete using pure javascript, autocomplete vanilla js, autocomplete with javascript, autocomplete-js, autocomplete.js, autocomplete.js examples, autocompletejs, autofill js, autosuggest dropdown, autosuggest highlight, autosuggest javascript, autosuggest javascript example, autosuggest js, autosuggest js github, autosuggest js npm, autosuggest npm, autosuggest search, autosuggestion example, autosuggestion javascript, best autocomplete javascript library, blazing fast, customizable, developer friendly, drop down list, drop down list html, drop down list javascript, dropdown javascript, dropdown js library, easy autocomplete, easy autocomplete ajax, easy autocomplete cdn, easy autocomplete multiple values, easy to use autocomplete javascript library, fast, fast autocomplete, fast autocomplete javascript, fastest autocomplete javascript library, form auto suggest javascript, github, hackable, high integration, input autocomplete js, javascript, javascript auto suggest box, javascript autocomplete, javascript autocomplete dropdown, javascript autocomplete framework, javascript autocomplete input, javascript autocomplete library tutorial, javascript autocomplete list, javascript autocomplete search, javascript autocomplete textarea, javascript autocomplete textbox, javascript autocomplete textbox from json, javascript autocomplete tutorial, javascript autosuggest, javascript autosuggest library, javascript autosuggest search, javascript dropdown menu, javascript dropdown select, javascript input autocomplete, javascript library, javascript search autocomplete, js, js autocomplete, js autocomplete ajax, js autocomplete input, js autocomplete library, js autocomplete search, js autocomplete select, js autocomplete tutorial, js autosuggest, js dropdown, js dropdown select, js input autocomplete, js search autocomplete, lightning fast, lightweight, lightweight autocomplete javascript, open source, pure javascript autocomplete, pure vanilla javascript, scalability, scalable, search, search autocomplete javascript, search javascript, search library, search library javascript, select, simple autocomplete, simple autocomplete javascript, speed, string search library javascript, suggestions, tarekraafat/autocomplete.js, text search library javascript, vanilla autocomplete, vanilla javascript autocomplete, vanilla javascript library, vanilla js, vanilla js autocomplete, vanilla js autocomplete npm, vanilla js autocomplete plugin, versatile, zero dependencies"> @@ -158,7 +158,7 @@ src="//platform-api.sharethis.com/js/sharethis.js#property=5c213660c276020011d38212&product=inline-share-buttons" async="async"> + href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.0/dist/css/autoComplete.min.css"> @@ -208,8 +208,8 @@ }, mustache: { data: ["../package.json", { - minVersion: "10.1", - version: "10.1.5" + minVersion: "10.2", + version: "10.2.0" }] } } @@ -224,7 +224,7 @@ - + \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 57b87b6..cfaaeda 100644 --- a/docs/index.md +++ b/docs/index.md @@ -39,6 +39,10 @@ autoComplete.js is a simple, pure vanilla Javascript library progressively desig - Pure Vanilla Javascript - Zero Dependencies - Simple & Lightweight +- Powerful Search Engine with two different modes +- Diacritics Support +- Debounce Support +- Life Cycle Events - Useful plugin API - WAI-ARIA Compliant - Highly Customizable diff --git a/docs/release-notes.md b/docs/release-notes.md index 59cb328..1e84342 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -35,7 +35,14 @@ For more information on semantic versioning, please visit . *** -### v10.1.5 ✨ +### v10.2.0 ✨ +- ➕ Added: `submit` API property controls `Enter` button default behavior (Thanks 👍 @CodeWithOz) #249 #224 #189 +- ➕ Added: `query` parameter to the `start("query")` API method for programmatic operations +- 🔧 Fixed: Generated errors when `resultsList` is disabled due to the attachment of the `keydown` event +- 🎛️ Updated: Library code with minor optimizations +- 🧹 Removed: Engines field in package.json + +### v10.1.5 - 🧹 Removed: `preInit` stage (Thanks 👍 @folknor) #229 - 🔧 Fixed: `unInit` to remove the `wrapper` element (Thanks 👍 @deniseismo) #245 diff --git a/package-lock.json b/package-lock.json index 2254edd..8dfdccb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@tarekraafat/autocomplete.js", - "version": "10.1.5", + "version": "10.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@tarekraafat/autocomplete.js", - "version": "10.1.5", + "version": "10.2.0", "funding": [ { "type": "opencollective", @@ -36,10 +36,6 @@ "rollup-plugin-serve": "^1.1.0", "rollup-plugin-sizes": "^1.0.4", "rollup-plugin-terser": "^7.0.2" - }, - "engines": { - "node": ">=12", - "npm": ">=6" } }, "node_modules/@babel/code-frame": { @@ -1612,9 +1608,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "15.12.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.4.tgz", - "integrity": "sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.0.0.tgz", + "integrity": "sha512-TmCW5HoZ2o2/z2EYi109jLqIaPIi9y/lc2LmDCWzuCi35bcaQ+OtUh6nwBiFK7SOu25FAU5+YKdqFZUwtqGSdg==", "dev": true }, "node_modules/acorn": { @@ -1793,9 +1789,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001239", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001239.tgz", - "integrity": "sha512-cyBkXJDMeI4wthy8xJ2FvDU6+0dtcZSJW3voUF8+e9f1bBeuvyZfc3PNbkOETyhbR+dGCPzn9E7MA3iwzusOhQ==", + "version": "1.0.30001241", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001241.tgz", + "integrity": "sha512-1uoSZ1Pq1VpH0WerIMqwptXHNNGfdl7d1cJUFs80CwQ/lVzdhTvsFZCeNFslze7AjsQnb4C85tzclPa1VShbeQ==", "dev": true, "funding": { "type": "opencollective", @@ -1874,9 +1870,9 @@ } }, "node_modules/core-js-compat": { - "version": "3.15.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.15.1.tgz", - "integrity": "sha512-xGhzYMX6y7oEGQGAJmP2TmtBLvR4nZmRGEcFa3ubHOq5YEp51gGN9AovVa0AoujGZIq+Wm6dISiYyGNfdflYww==", + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.15.2.tgz", + "integrity": "sha512-Wp+BJVvwopjI+A1EFqm2dwUmWYXrvucmtIB2LgXn/Rb+gWPKYxtmb4GKHGKG/KGF1eK9jfjzT38DITbTOCX/SQ==", "dev": true, "dependencies": { "browserslist": "^4.16.6", @@ -1926,9 +1922,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.3.758", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.758.tgz", - "integrity": "sha512-StYtiDbgZdjcck3OLwsVVVif7QDuD5m5v2gF+XpETp5lHa7X0y3129YBlYaHRPyj1fep1oAaC6i//gAdp+rhbw==", + "version": "1.3.766", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.766.tgz", + "integrity": "sha512-u2quJ862q9reRKh/je3GXis3w38+RoXH1J9N3XjtsS6NzmUAosNsyZgUVFZPN/ZlJ3v6T0rTyZR3q/J5c6Sy5w==", "dev": true }, "node_modules/escalade": { @@ -1965,9 +1961,9 @@ } }, "node_modules/filesize": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.3.0.tgz", - "integrity": "sha512-ytx0ruGpDHKWVoiui6+BY/QMNngtDQ/pJaFwfBpQif0J63+E8DLdFyqS3NkKQn7vIruUEpoGD9JUJSg7Kp+I0g==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.4.0.tgz", + "integrity": "sha512-mjFIpOHC4jbfcTfoh4rkWpI31mF7viw9ikj/JyLoKzqlwG/YsefKfvYlYhdYdg/9mtK2z1AzgN/0LvVQ3zdlSQ==", "dev": true, "engines": { "node": ">= 0.4.0" @@ -2520,9 +2516,9 @@ } }, "node_modules/rollup": { - "version": "2.52.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.3.tgz", - "integrity": "sha512-QF3Sju8Kl2z0osI4unyOLyUudyhOMK6G0AeqJWgfiyigqLAlnNrfBcDWDx+f1cqn+JU2iIYVkDrgQ6/KtwEfrg==", + "version": "2.52.7", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.7.tgz", + "integrity": "sha512-55cSH4CCU6MaPr9TAOyrIC+7qFCHscL7tkNsm1MBfIJRRqRbCEY0mmeFn4Wg8FKsHtEH8r389Fz38r/o+kgXLg==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -2569,9 +2565,9 @@ } }, "node_modules/rollup-plugin-livereload": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-livereload/-/rollup-plugin-livereload-2.0.0.tgz", - "integrity": "sha512-oC/8NqumGYuphkqrfszOHUUIwzKsaHBICw6QRwT5uD07gvePTS+HW+GFwu6f9K8W02CUuTvtIM9AWJrbj4wE1A==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/rollup-plugin-livereload/-/rollup-plugin-livereload-2.0.5.tgz", + "integrity": "sha512-vqQZ/UQowTW7VoiKEM5ouNW90wE5/GZLfdWuR0ELxyKOJUIaj+uismPZZaICU4DnWPVjnpCDDxEqwU7pcKY/PA==", "dev": true, "dependencies": { "livereload": "^0.9.1" @@ -2727,9 +2723,9 @@ } }, "node_modules/terser": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.0.tgz", - "integrity": "sha512-HP5/9hp2UaZt5fYkuhNBR8YyRcT8juw8+uFbAme53iN9hblvKnLUTKkmwJG6ocWpIKf8UK4DoeWG4ty0J6S6/g==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.1.tgz", + "integrity": "sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg==", "dev": true, "dependencies": { "commander": "^2.20.0", @@ -2814,9 +2810,9 @@ } }, "node_modules/ws": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.0.tgz", - "integrity": "sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.1.tgz", + "integrity": "sha512-2c6faOUH/nhoQN6abwMloF7Iyl0ZS2E9HGtsiLrWn0zOOMWlhtDmdf/uihDt6jnuCxgtwGBNy6Onsoy2s2O2Ow==", "dev": true, "engines": { "node": ">=8.3.0" @@ -3921,9 +3917,9 @@ "dev": true }, "@types/node": { - "version": "15.12.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.4.tgz", - "integrity": "sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.0.0.tgz", + "integrity": "sha512-TmCW5HoZ2o2/z2EYi109jLqIaPIi9y/lc2LmDCWzuCi35bcaQ+OtUh6nwBiFK7SOu25FAU5+YKdqFZUwtqGSdg==", "dev": true }, "acorn": { @@ -4058,9 +4054,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001239", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001239.tgz", - "integrity": "sha512-cyBkXJDMeI4wthy8xJ2FvDU6+0dtcZSJW3voUF8+e9f1bBeuvyZfc3PNbkOETyhbR+dGCPzn9E7MA3iwzusOhQ==", + "version": "1.0.30001241", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001241.tgz", + "integrity": "sha512-1uoSZ1Pq1VpH0WerIMqwptXHNNGfdl7d1cJUFs80CwQ/lVzdhTvsFZCeNFslze7AjsQnb4C85tzclPa1VShbeQ==", "dev": true }, "chalk": { @@ -4127,9 +4123,9 @@ } }, "core-js-compat": { - "version": "3.15.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.15.1.tgz", - "integrity": "sha512-xGhzYMX6y7oEGQGAJmP2TmtBLvR4nZmRGEcFa3ubHOq5YEp51gGN9AovVa0AoujGZIq+Wm6dISiYyGNfdflYww==", + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.15.2.tgz", + "integrity": "sha512-Wp+BJVvwopjI+A1EFqm2dwUmWYXrvucmtIB2LgXn/Rb+gWPKYxtmb4GKHGKG/KGF1eK9jfjzT38DITbTOCX/SQ==", "dev": true, "requires": { "browserslist": "^4.16.6", @@ -4163,9 +4159,9 @@ } }, "electron-to-chromium": { - "version": "1.3.758", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.758.tgz", - "integrity": "sha512-StYtiDbgZdjcck3OLwsVVVif7QDuD5m5v2gF+XpETp5lHa7X0y3129YBlYaHRPyj1fep1oAaC6i//gAdp+rhbw==", + "version": "1.3.766", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.766.tgz", + "integrity": "sha512-u2quJ862q9reRKh/je3GXis3w38+RoXH1J9N3XjtsS6NzmUAosNsyZgUVFZPN/ZlJ3v6T0rTyZR3q/J5c6Sy5w==", "dev": true }, "escalade": { @@ -4193,9 +4189,9 @@ "dev": true }, "filesize": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.3.0.tgz", - "integrity": "sha512-ytx0ruGpDHKWVoiui6+BY/QMNngtDQ/pJaFwfBpQif0J63+E8DLdFyqS3NkKQn7vIruUEpoGD9JUJSg7Kp+I0g==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.4.0.tgz", + "integrity": "sha512-mjFIpOHC4jbfcTfoh4rkWpI31mF7viw9ikj/JyLoKzqlwG/YsefKfvYlYhdYdg/9mtK2z1AzgN/0LvVQ3zdlSQ==", "dev": true }, "fill-range": { @@ -4617,9 +4613,9 @@ } }, "rollup": { - "version": "2.52.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.3.tgz", - "integrity": "sha512-QF3Sju8Kl2z0osI4unyOLyUudyhOMK6G0AeqJWgfiyigqLAlnNrfBcDWDx+f1cqn+JU2iIYVkDrgQ6/KtwEfrg==", + "version": "2.52.7", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.7.tgz", + "integrity": "sha512-55cSH4CCU6MaPr9TAOyrIC+7qFCHscL7tkNsm1MBfIJRRqRbCEY0mmeFn4Wg8FKsHtEH8r389Fz38r/o+kgXLg==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -4649,9 +4645,9 @@ "requires": {} }, "rollup-plugin-livereload": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-livereload/-/rollup-plugin-livereload-2.0.0.tgz", - "integrity": "sha512-oC/8NqumGYuphkqrfszOHUUIwzKsaHBICw6QRwT5uD07gvePTS+HW+GFwu6f9K8W02CUuTvtIM9AWJrbj4wE1A==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/rollup-plugin-livereload/-/rollup-plugin-livereload-2.0.5.tgz", + "integrity": "sha512-vqQZ/UQowTW7VoiKEM5ouNW90wE5/GZLfdWuR0ELxyKOJUIaj+uismPZZaICU4DnWPVjnpCDDxEqwU7pcKY/PA==", "dev": true, "requires": { "livereload": "^0.9.1" @@ -4784,9 +4780,9 @@ } }, "terser": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.0.tgz", - "integrity": "sha512-HP5/9hp2UaZt5fYkuhNBR8YyRcT8juw8+uFbAme53iN9hblvKnLUTKkmwJG6ocWpIKf8UK4DoeWG4ty0J6S6/g==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.1.tgz", + "integrity": "sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg==", "dev": true, "requires": { "commander": "^2.20.0", @@ -4846,9 +4842,9 @@ "dev": true }, "ws": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.0.tgz", - "integrity": "sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.1.tgz", + "integrity": "sha512-2c6faOUH/nhoQN6abwMloF7Iyl0ZS2E9HGtsiLrWn0zOOMWlhtDmdf/uihDt6jnuCxgtwGBNy6Onsoy2s2O2Ow==", "dev": true, "requires": {} } diff --git a/package.json b/package.json index c075039..72359aa 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "10.1.5", + "version": "10.2.0", "name": "@tarekraafat/autocomplete.js", "description": "Simple autocomplete pure vanilla Javascript library.", "keywords": [ @@ -75,15 +75,11 @@ "rollup-plugin-terser": "^7.0.2" }, "scripts": { - "dev": "rollup -c -w", - "build": "rollup -c", + "dev": "NODE_ENV=development rollup -c -w", + "build": "NODE_ENV=production rollup -c", "docs": "jsdoc -c jsdocs.json" }, "license": "Apache-2.0", - "engines": { - "node": ">=12", - "npm": ">=6" - }, "contributors": [ { "name": "Tarek Raafat", @@ -234,6 +230,10 @@ { "name": "Waranyoo Butsingkorn", "url": "https://github.com/50kudos" + }, + { + "name": "sunshineplan", + "url": "https://github.com/sunshineplan" } ], "maintainers": [ diff --git a/rollup.config.js b/rollup.config.js index fbc0173..bc7755a 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -7,10 +7,12 @@ import gzipPlugin from "rollup-plugin-gzip"; import analyze from "rollup-plugin-analyzer"; import sizes from "rollup-plugin-sizes"; import serve from "rollup-plugin-serve"; -// import livereload from "rollup-plugin-livereload"; +import livereload from "rollup-plugin-livereload"; // Library Name const libName = "autoComplete"; +// Build Environment +const isProduction = process.env.NODE_ENV === "production"; // Library Max. Size Allowed const limitBytes = 3 * 1024 * 1024; // Library Size Analyzer @@ -34,13 +36,14 @@ export default [ file: "./docs/demo/js/autoComplete.min.js", name: libName, format: "umd", + // sourcemap: isProduction ? false : "inline", }, ], plugins: [ nodent({ es7: true, promises: true, - sourcemap: true, + // sourcemap: isProduction ? false : true, noRuntime: true, es6target: true, }), @@ -71,13 +74,14 @@ export default [ file: "./docs/demo/js/autoComplete.js", name: libName, format: "umd", + // sourcemap: isProduction ? false : "inline", }, ], plugins: [ nodent({ es7: true, promises: true, - sourcemap: true, + // sourcemap: isProduction ? false : true, noRuntime: true, es6target: true, }), @@ -96,16 +100,17 @@ export default [ }), sizes(), // Server - serve({ - open: true, - openPage: "./docs/demo/index.html", - host: "localhost", - port: 8000, - verbose: true, - contentBase: "./docs/demo", - }), - // // Live Reload - // livereload({ watch: "./docs/demo/" }), + !isProduction && + serve({ + open: true, + openPage: "./docs/demo/index.html", + host: "localhost", + port: 8000, + verbose: true, + contentBase: "./docs/demo", + }), + // Live Reload + !isProduction && livereload({ watch: ["./docs/demo/", "./src/"] }), ], }, ]; diff --git a/src/autoComplete.js b/src/autoComplete.js index 656209d..b604f79 100644 --- a/src/autoComplete.js +++ b/src/autoComplete.js @@ -42,6 +42,7 @@ import init from "./services/init"; * @param {Function} [config.resultItem.element] - Invoked before showing the results list. Allows manipulation of the DOM before it is added to the document. Signature: (item: HTMLElement, data: { match, value, [key] }). * @param {(Boolean|String)} [config.resultItem.highlight=false] - Enable to highlight matching characters using HTMLMarkElement, or a string of CSS classes to add to any generated mark elements. * @param {String} [config.resultItem.selected] - CSS classes to add and remove from result items the user navigates to using the keyboard. + * @param {Boolean} [config.submit] - If enabled pressing enter will not prevent default behavior. * @param {Object} [config.events] - Allows adding custom or overriding internal event handling. * @param {Object} [config.events.input] - Maps event names to event handlers for the input element. Each key must be a valid event name, see {@link https://developer.mozilla.org/en-US/docs/Web/Events}, and each value must be an event handler function. Default handlers are keydown and blur. * @param {Object} [config.events.list] - Same as config.events.input, but for the result list container element. Default handlers are mousedown and click. diff --git a/src/controllers/eventController.js b/src/controllers/eventController.js index 8f819b5..0c3c550 100644 --- a/src/controllers/eventController.js +++ b/src/controllers/eventController.js @@ -11,7 +11,7 @@ import { click, navigate, close } from "./listController"; const eventsManager = (events, callback) => { for (const element in events) { for (const event in events[element]) { - callback(event, element); + callback(element, event); } } }; @@ -37,37 +37,38 @@ const addEvents = (ctx) => { // Private events listeners list const privateEvents = { input: { - input: () => { + input() { run(); }, - keydown: (event) => { + keydown(event) { navigate(event, ctx); }, - blur: () => { + blur() { close(ctx); }, }, list: { - mousedown: (event) => { + mousedown(event) { event.preventDefault(); }, - click: (event) => { + click(event) { click(event, ctx); }, }, }; // Populate all private events into public events list - eventsManager(privateEvents, (event, element) => { - // do NOT populate list events If "resultsList" disabled - if (!resultsList && element === "list") return; - // do NOT overwrite public events + eventsManager(privateEvents, (element, event) => { + // Do NOT populate any events except "input" If "resultsList" disabled + if (!resultsList && event !== "input") return; + // Do NOT overwrite public events if (publicEvents[element][event]) return; + // Populate public events publicEvents[element][event] = privateEvents[element][event]; }); // Attach all public events - eventsManager(publicEvents, (event, element) => { + eventsManager(publicEvents, (element, event) => { ctx[element].addEventListener(event, publicEvents[element][event]); }); }; @@ -78,7 +79,7 @@ const addEvents = (ctx) => { * @param {Object} ctx - autoComplete.js context */ const removeEvents = (ctx) => { - eventsManager(ctx.events, (event, element) => { + eventsManager(ctx.events, (element, event) => { ctx[element].removeEventListener(event, ctx.events[element][event]); }); }; diff --git a/src/controllers/listController.js b/src/controllers/listController.js index 26dab9c..e0da3e5 100644 --- a/src/controllers/listController.js +++ b/src/controllers/listController.js @@ -1,8 +1,6 @@ import { create } from "../helpers/io"; import eventEmitter from "../helpers/eventEmitter"; -let classes; - // String holders const Expand = "aria-expanded"; const Active = "aria-activedescendant"; @@ -116,7 +114,12 @@ const close = (ctx) => { * @param {Object} ctx - autoComplete.js context */ const goTo = (index, ctx) => { - const results = ctx.list.getElementsByTagName(ctx.resultItem.tag); + const { list, resultItem } = ctx; + + // List of result items + const results = list.getElementsByTagName(resultItem.tag); + // Selected result item Classes + const cls = resultItem.selected ? resultItem.selected.split(" ") : false; if (ctx.isOpen && results.length) { // Previous cursor state @@ -134,19 +137,19 @@ const goTo = (index, ctx) => { // Remove "aria-selected" attribute from the item results[state].removeAttribute(Selected); // Remove "selected" class from the item - if (classes) results[state].classList.remove(...classes); + if (cls) results[state].classList.remove(...cls); } // Set "aria-selected" value to true results[index].setAttribute(Selected, true); // Add "selected" class to the selected item - if (classes) results[index].classList.add(...classes); + if (cls) results[index].classList.add(...cls); // Set "aria-activedescendant" value to the selected item ctx.input.setAttribute(Active, results[ctx.cursor].id); // Scroll to selection - ctx.list.scrollTop = results[index].offsetTop - ctx.list.clientHeight + results[index].clientHeight + 5; + list.scrollTop = results[index].offsetTop - list.clientHeight + results[index].clientHeight + 5; // Prepare Selection data feedback object ctx.feedback.cursor = ctx.cursor; @@ -218,8 +221,6 @@ const click = (event, ctx) => { // Check if clicked item is a "result" item if (item && item.nodeName === itemTag) { - event.preventDefault(); - const index = items.indexOf(item); select(ctx, event, index); @@ -233,30 +234,23 @@ const click = (event, ctx) => { * @param {Object} ctx - autoComplete.js context */ const navigate = function (event, ctx) { - const key = event.keyCode; - const selected = ctx.resultItem.selected; - - if (selected) classes = selected.split(" "); - // Check pressed key - switch (key) { + switch (event.keyCode) { // Down/Up arrow case 40: case 38: event.preventDefault(); // Move cursor based on pressed key - key === 40 ? next(ctx) : previous(ctx); + event.keyCode === 40 ? next(ctx) : previous(ctx); break; // Enter case 13: - event.preventDefault(); + if (!ctx.submit) event.preventDefault(); // If cursor moved - if (ctx.cursor >= 0) { - select(ctx, event); - } + if (ctx.cursor >= 0) select(ctx, event); break; // Tab @@ -265,14 +259,11 @@ const navigate = function (event, ctx) { event.preventDefault(); select(ctx, event); - } else { - close(ctx); } + break; // Esc case 27: - event.preventDefault(); - // Clear "input" value ctx.input.value = ""; diff --git a/src/helpers/io.js b/src/helpers/io.js index 5408481..d0be3cb 100644 --- a/src/helpers/io.js +++ b/src/helpers/io.js @@ -105,14 +105,14 @@ const checkTrigger = (query, condition, threshold) => (condition ? condition(que * Highlight matching characters * * @param {String} value - user's raw search query value - * @param {String} klass - of highlighted character + * @param {String} cls - of highlighted character * * @returns {HTMLElement} - newly create html element */ -const mark = (value, klass) => +const mark = (value, cls) => create("mark", { innerHTML: value, - ...(typeof klass === "string" && { class: klass }), + ...(typeof cls === "string" && { class: cls }), }).outerHTML; export { select, create, getQuery, format, debounce, checkTrigger, mark }; diff --git a/src/services/extend.js b/src/services/extend.js index 5028733..e489e2f 100644 --- a/src/services/extend.js +++ b/src/services/extend.js @@ -17,9 +17,14 @@ export default function (autoComplete) { init(this); }; - // Start autoComplete.js engine - prototype.start = function () { - start(this); + /** + * Start autoComplete.js engine + * + * @param {String} query - Search query value + * + */ + prototype.start = function (query) { + start(this, query); }; // Un-Initialize autoComplete.js engine @@ -49,7 +54,6 @@ export default function (autoComplete) { * * @param {Number} index - of the selected result item * - * @returns {void} */ prototype.goTo = function (index) { goTo(index, this); @@ -70,7 +74,6 @@ export default function (autoComplete) { * * @param {Number} index - of the selected result item * - * @returns {void} */ prototype.select = function (index) { select(this, null, index); diff --git a/src/services/start.js b/src/services/start.js index 8a47653..d0688e6 100644 --- a/src/services/start.js +++ b/src/services/start.js @@ -6,12 +6,13 @@ import { render, close } from "../controllers/listController"; * Start stage * * @param {Object} ctx - autoComplete.js context + * @param {String} q - API search query value */ -export default async function (ctx) { +export default async function (ctx, q) { const { input, query, trigger, threshold, resultsList } = ctx; // Get "input" query value - let queryVal = getQuery(input); + let queryVal = q || getQuery(input); queryVal = query ? query(queryVal) : queryVal; // Get trigger decision const condition = checkTrigger(queryVal, trigger, threshold);