diff --git a/demo/css/kompletr.demo.min.css b/demo/css/kompletr.demo.min.css index 328e1b7..b244015 100644 --- a/demo/css/kompletr.demo.min.css +++ b/demo/css/kompletr.demo.min.css @@ -1 +1 @@ -@import url("https://fonts.googleapis.com/css?family=Open+Sans");@import url("https://fonts.googleapis.com/css?family=Thasadith");html{font-size:14px}.github-corner{position:fixed;right:0;top:0;border-bottom:0;text-decoration:none;z-index:1;transition:all 0.2s ease-in-out}.github-corner svg{height:80px;width:80px;color:#333;fill:#2cc1fb;fill:var(--theme-color, #2cc1fb)}.github-corner:hover{transform:scale(1.1);cursor:pointer}body{display:flex;flex-direction:column;margin:0;min-height:100vh;font-family:"Open Sans",sans-serif;background:#333}hgroup{margin:0 auto 80px;text-align:center}hgroup img{margin:90px auto 0;display:block;max-width:500px}hgroup cite{color:#fff;z-index:999999999;position:relative}a{color:#2cc1fb}footer{margin-top:auto;color:#fff;text-align:center;font-size:0.9em}.form--search{width:400px;max-width:400px;position:relative;margin:0 auto;-webkit-box-shadow:0px 0px 105px 45px rgba(44,193,251,0.9);-moz-box-shadow:0px 0px 105px 45px rgba(44,193,251,0.9);box-shadow:0px 0px 105px 45px rgba(44,193,251,0.9)}@media (max-width: 480px){.form--search{width:90%}}.input--search,.item--result{font-family:"Open Sans",sans-serif;font-size:100%}.input--search{display:block;box-sizing:border-box;margin:0 auto;padding:15px 10px;width:100%;min-width:240px;max-width:600px;height:auto;font-size:1.2rem;line-height:1.5;border-radius:8px}.input--search:focus{outline:none}.input--search{color:#fff;background:#333}::placeholder{color:silver} +@import url("https://fonts.googleapis.com/css?family=Open+Sans");@import url("https://fonts.googleapis.com/css?family=Thasadith");html{font-size:14px}.github-corner{position:fixed;right:0;top:0;border-bottom:0;text-decoration:none;z-index:1;transition:all 0.2s ease-in-out}.github-corner svg{height:80px;width:80px;color:#333;fill:#2cc1fb;fill:var(--theme-color, #2cc1fb)}.github-corner:hover{transform:scale(1.1);cursor:pointer}body{display:flex;flex-direction:column;margin:0;min-height:100vh;font-family:"Open Sans",sans-serif;background:#333}hgroup{margin:0 auto 80px;text-align:center}hgroup img{margin:90px auto 0;display:block;max-width:500px}hgroup cite{color:#fff;z-index:999999999;position:relative}a{color:#2cc1fb}footer{margin-top:auto;color:#fff;text-align:center;font-size:0.9em}.form--search{width:400px;max-width:400px;position:relative;margin:0 auto;-webkit-box-shadow:0px 0px 105px 45px rgba(44,193,251,0.9);-moz-box-shadow:0px 0px 105px 45px rgba(44,193,251,0.9);box-shadow:0px 0px 105px 45px rgba(44,193,251,0.9)}@media (max-width: 480px){.form--search{width:90%}}.input--search{font-family:"Open Sans",sans-serif;font-size:100%}.input--search{display:block;box-sizing:border-box;margin:0 auto;padding:15px 10px;width:100%;min-width:240px;max-width:600px;height:auto;font-size:1.2rem;line-height:1.5;border-radius:0;color:#fff;background:#333;border:none;outline:none}.input--search:focus{outline:none}::placeholder{color:silver} diff --git a/demo/js/kompletr.min.js b/demo/js/kompletr.min.js index 63dac9b..b556f64 100644 --- a/demo/js/kompletr.min.js +++ b/demo/js/kompletr.min.js @@ -1 +1 @@ -var t={d:function(e,s){for(var r in s)t.o(s,r)&&!t.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:s[r]})},o:function(t,e){return Object.prototype.hasOwnProperty.call(t,e)}},e={};t.d(e,{L:function(){return p}});class s{constructor(){}static fadeIn(t){t.style.opacity=0,t.style.display="block",function e(){let s=parseFloat(t.style.opacity);(s+=.1)>1||(t.style.opacity=s,requestAnimationFrame(e))}()}static fadeOut(t){t.style.opacity=1,function e(){(t.style.opacity-=.1)<0?t.style.display="none":requestAnimationFrame(e)}()}}const r=Object.freeze({error:"kompletr.error",domDone:"kompletr.dom.done",dataDone:"kompletr.data.done",selectDone:"kompletr.select.done"}),i=Object.freeze({cache:"cache",callback:"callback",local:"local"}),a=Object.freeze({prefix:"prefix",expression:"expression"}),o=Object.freeze({light:"light",dark:"dark"});class n{broadcaster=null;cache=null;callbacks={};configuration=null;dom=null;props=null;constructor({configuration:t,properties:e,dom:i,cache:a,broadcaster:o,onKeyup:n,onSelect:l,onError:c}){try{this.configuration=t,this.broadcaster=o,this.props=e,this.dom=i,this.cache=a,this.broadcaster.subscribe(r.error,this.error),this.broadcaster.subscribe(r.dataDone,this.showResults),this.broadcaster.subscribe(r.domDone,s.fadeIn.bind(null,this.dom.result)),this.broadcaster.subscribe(r.domDone,this.bindResults),this.broadcaster.subscribe(r.selectDone,this.closeTheShop),this.broadcaster.listen(this.dom.input,"keyup",this.suggest),this.broadcaster.listen(this.dom.body,"click",this.closeTheShop),(n||l||c)&&(this.callbacks=Object.assign(this.callbacks,{onKeyup:n,onSelect:l,onError:c}))}catch(t){o?o.trigger(r.error,t):console.error(`[kompletr] An error has occured -> ${t.stack}`)}}closeTheShop=t=>{if(t.srcElement===this.dom.input)return!0;s.fadeOut(this.dom.result),this.resetPointer()};resetPointer=()=>{this.props.pointer=-1};error=t=>{console.error(`[kompletr] An error has occured -> ${t.stack}`),s.fadeOut(this.dom.result),this.callbacks.onError&&this.callbacks.onError(t)};filter=(t,e)=>t.filter((t=>{const s="string"==typeof t.data?t.data:t.data[this.configuration.propToMapAsValue];return this.configuration.filterOn===a.prefix?0===s.toLowerCase().lastIndexOf(e.toLowerCase(),0):-1!==s.toLowerCase().lastIndexOf(e.toLowerCase())}));showResults=async({from:t,data:e})=>{this.props.data=e,e=this.props.data.map(((t,e)=>({idx:e,data:t}))),this.callbacks.onKeyup||(e=this.filter(e,this.dom.input.value)),this.configuration.cache&&t!==i.cache&&this.cache.set({string:this.dom.input.value,data:e}),this.dom.buildResults(e.slice(0,this.configuration.maxResults),this.configuration.fieldsToDisplay)};bindResults=()=>{if(this.dom.result?.children?.length)for(let t=0;t{this.broadcaster.listen(this.dom.result.children[t],"click",(()=>{this.dom.focused=this.dom.result.children[t],this.select(this.dom.focused.id)}))})(t)};suggest=t=>{if(this.dom.input.value.length{try{this.configuration.cache&&await this.cache.isValid(t)?this.cache.get(t,(t=>{this.broadcaster.trigger(r.dataDone,{from:i.cache,data:t})})):this.callbacks.onKeyup?this.callbacks.onKeyup(t,(t=>{this.broadcaster.trigger(r.dataDone,{from:i.callback,data:t})})):this.broadcaster.trigger(r.dataDone,{from:i.local,data:this.props.data})}catch(t){this.broadcaster.trigger(r.error,t)}};navigate=t=>{try{if(38!=t&&40!=t)return!1;if(this.props.pointer<-1||this.props.pointer>this.dom.result.children.length-1)return!1;if(38===t&&0===this.props.pointer||40===t&&this.props.pointer===this.dom.result.children.length-1)return!1;38===t&&this.props.pointer>=-1?this.props.pointer--:40===t&&this.props.pointer{try{this.dom.input.value="object"==typeof this.props.data[t]?this.props.data[t][this.configuration.propToMapAsValue]:this.props.data[t],this.callbacks.onSelect&&this.callbacks.onSelect(this.props.data[t]),this.broadcaster.trigger(r.selectDone)}catch(t){this.broadcaster.trigger(r.error,t)}}}class l{_multiple=!1;_theme=o.light;_fieldsToDisplay=[];_maxResults=10;_startQueryingFromChar=2;_propToMapAsValue="";_filterOn=a.prefix;_cache=0;get multiple(){return this._multiple}set multiple(t){this._multiple=t}get theme(){return this._theme}set theme(t){const e=Object.keys(o);if(!e.includes(t))throw new Error(`theme should be one of ${e.toString()}, ${t} given`);this._theme=t}get fieldsToDisplay(){return this._fieldsToDisplay}set fieldsToDisplay(t){this._fieldsToDisplay=t}get maxResults(){return this._maxResults}set maxResults(t){this._maxResults=t}get startQueryingFromChar(){return this._startQueryingFromChar}set startQueryingFromChar(t){this._startQueryingFromChar=t}get propToMapAsValue(){return this._propToMapAsValue}set propToMapAsValue(t){this._propToMapAsValue=t}get filterOn(){return this._filterOn}set filterOn(t){const e=Object.keys(a);if(!e.includes(t))throw new Error(`filterOn should be one of ${e.toString()}, ${t} given`);this._filterOn=t}get cache(){return this._cache}set cache(t){if(isNaN(parseInt(t,10)))throw new Error("cache should be an integer");this._cache=t}constructor(t){if(void 0!==t){if("object"!=typeof t)throw new Error("options should be an object");this.theme=t?.theme||this._theme,this.multiple=t?.multiple||this._multiple,this.fieldsToDisplay=t?.fieldsToDisplay||this._fieldsToDisplay,this.maxResults=t?.maxResults||this._maxResults,this.startQueryingFromChar=t?.startQueryingFromChar||this._startQueryingFromChar,this.propToMapAsValue=t?.propToMapAsValue||this._propToMapAsValue,this.filterOn=t?.filterOn||this._filterOn,this.cache=t?.cache||this._cache}}}class c{_name=null;_duration=null;_braodcaster=null;constructor(t,e=0,s="kompletr.cache"){if(!window.caches)return!1;this._broadcaster=t,this._name=s,this._duration=e}get(t,e){window.caches.open(this._name).then((s=>{s.match(t).then((async t=>{e(await t.json())}))})).catch((t=>{this._broadcaster.trigger(r.error,t)}))}set({string:t,data:e}){window.caches.open(this._name).then((s=>{const r=new Headers;r.set("Content-Type","application/json"),r.set("Cache-Control",`max-age=${this._duration}`),s.put(`/${t}`,new Response(JSON.stringify(e),{headers:r}))})).catch((t=>{this._broadcaster.trigger(r.error,t)}))}async isValid(t){try{const e=await window.caches.open(this._name);return!!await e.match(`/${t}`)}catch(t){this._broadcaster.trigger(r.error,t)}}}class h{subscribers=[];constructor(){}subscribe(t,e){if(!Object.values(r).includes(t))throw new Error(`Event should be one of ${Object.keys(r)}: ${t} given.`);this.subscribers.push({type:t,handler:e})}listen(t,e,s){t.addEventListener(e,s)}trigger(t,e={}){if(!Object.values(r).includes(t))throw new Error(`Event should be one of ${Object.keys(r)}: ${t} given.`);this.subscribers.filter((e=>e.type===t)).forEach((t=>t.handler(e)))}}class u{_data=null;get data(){return this._data}set data(t){if(!Array.isArray(t))throw new Error(`data must be an array (${t.toString()} given)`);this._data=t}_pointer=null;get pointer(){return this._pointer}set pointer(t){if(isNaN(parseInt(t,10)))throw new Error(`pointer must be an integer (${t.toString()} given)`);this._pointer=t}_previousValue=null;get previousValue(){return this._previousValue}set previousValue(t){this._previousValue=t}constructor(t=[]){this._data=t}}class d{_identifiers={results:"kpl-result"};_classes={main:"kompletr",input:"input--search",results:"form--search__result",result:"item--result",data:"item--data",focus:"focus"};_body=null;get body(){return this._body}set body(t){this._body=t}_input=null;get input(){return this._input}set input(t){if(t instanceof HTMLInputElement==0)throw new Error(`input should be an HTMLInputElement instance: ${t} given.`);this._input=t}_focused=null;get focused(){return this._focused}set focused(t){this._focused=t}_result=null;get result(){return this._result}set result(t){this._result=t}_broadcaster=null;constructor(t,e,s={theme:"light"}){this._broadcaster=e,this.body=document.getElementsByTagName("body")[0],this.input=t instanceof HTMLInputElement?t:document.getElementById(t),this.input.setAttribute("class",`${this._input.getAttribute("class")} ${this._classes.input}`),this.result=this.build("div",[{id:this._identifiers.results},{class:this._classes.results}]),this.input.parentElement.setAttribute("class",`${this._input.parentElement.getAttribute("class")} ${this._classes.main} ${s.theme}`),this.input.parentElement.appendChild(this._result)}build(t,e=[]){const s=document.createElement(t);return e.forEach((t=>{s.setAttribute(Object.keys(t)[0],Object.values(t)[0])})),s}focus(t){if(isNaN(parseInt(t,10))||t<0||t>this.result.children.length-1)throw new Error("pointer should be a valid integer in the result lenght range: "+t+" given.");this.focused=null,Array.from(this.result.children).forEach((t=>{(t=>{t.className=this._classes.result})(t)})),this.focused=this.result.children[t],this.result.children[t].className+=` ${this._classes.focus}`}buildResults(t,e){let s="";s=t&&t.length?t.reduce(((t,s)=>{switch(t+=`
`,typeof s.data){case"string":t+=`${s.data}`;break;case"object":{let r=Array.isArray(e)&&e.length?e:Object.keys(s.data);for(let e=0;e${s.data[r[e]]}`;break}}return t+"
"}),""):`
Not found
`,this.result.innerHTML=s,this._broadcaster.trigger(r.domDone,this.result)}}const p=function({input:t,data:e,options:s,onKeyup:r,onSelect:i,onError:a}){try{const[o,p,m]=[new h,new l(s),new u(e)],[f,b]=[new d(t,o,p),p.cache?new c(o,p.cache):null];new n({configuration:p,properties:m,dom:f,cache:b,broadcaster:o,onKeyup:r,onSelect:i,onError:a})}catch(t){console.error(`[kompletr] An error has occured -> ${t.stack}`)}};window.kompletr=p;var m=e.L;export{m as kompletr}; \ No newline at end of file +var t={d:function(e,s){for(var r in s)t.o(s,r)&&!t.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:s[r]})},o:function(t,e){return Object.prototype.hasOwnProperty.call(t,e)}},e={};t.d(e,{L:function(){return d}});class s{constructor(){}static fadeIn(t){t.style.opacity=0,t.style.display="block",function e(){let s=parseFloat(t.style.opacity);(s+=.1)>1||(t.style.opacity=s,requestAnimationFrame(e))}()}static fadeOut(t){t.style.opacity=1,function e(){(t.style.opacity-=.1)<0?t.style.display="none":requestAnimationFrame(e)}()}}const r=Object.freeze({error:"kompletr.error",domDone:"kompletr.dom.done",dataDone:"kompletr.data.done",selectDone:"kompletr.select.done"}),i=Object.freeze({cache:"cache",callback:"callback",local:"local"}),a=Object.freeze({prefix:"prefix",expression:"expression"}),o=Object.freeze({light:"light",dark:"dark"});class n{broadcaster=null;cache=null;callbacks={};configuration=null;dom=null;props=null;constructor({configuration:t,properties:e,dom:i,cache:a,broadcaster:o,onKeyup:n,onSelect:l,onError:c}){try{this.configuration=t,this.broadcaster=o,this.props=e,this.dom=i,this.cache=a,this.broadcaster.subscribe(r.error,this.error),this.broadcaster.subscribe(r.dataDone,this.showResults),this.broadcaster.subscribe(r.domDone,s.fadeIn.bind(null,this.dom.result)),this.broadcaster.subscribe(r.domDone,this.bindResults),this.broadcaster.subscribe(r.selectDone,this.closeTheShop),this.broadcaster.listen(this.dom.input,"keyup",this.suggest),this.broadcaster.listen(this.dom.body,"click",this.closeTheShop),(n||l||c)&&(this.callbacks=Object.assign(this.callbacks,{onKeyup:n,onSelect:l,onError:c}))}catch(t){o?o.trigger(r.error,t):console.error(`[kompletr] An error has occured -> ${t.stack}`)}}closeTheShop=t=>{if(t.srcElement===this.dom.input)return!0;s.fadeOut(this.dom.result),this.resetPointer()};resetPointer=()=>{this.props.pointer=-1};error=t=>{console.error(`[kompletr] An error has occured -> ${t.stack}`),s.fadeOut(this.dom.result),this.callbacks.onError&&this.callbacks.onError(t)};filter=(t,e)=>t.filter((t=>{const s="string"==typeof t.data?t.data:t.data[this.configuration.propToMapAsValue];return this.configuration.filterOn===a.prefix?0===s.toLowerCase().lastIndexOf(e.toLowerCase(),0):-1!==s.toLowerCase().lastIndexOf(e.toLowerCase())}));showResults=async({from:t,data:e})=>{this.props.data=e,e=this.props.data.map(((t,e)=>({idx:e,data:t}))),this.callbacks.onKeyup||(e=this.filter(e,this.dom.input.value)),this.configuration.cache&&t!==i.cache&&this.cache.set({string:this.dom.input.value,data:e}),this.dom.buildResults(e.slice(0,this.configuration.maxResults),this.configuration.fieldsToDisplay)};bindResults=()=>{if(this.dom.result?.children?.length)for(let t=0;t{this.broadcaster.listen(this.dom.result.children[t],"click",(()=>{this.dom.focused=this.dom.result.children[t],this.select(this.dom.focused.id)}))})(t)};suggest=t=>{if(this.dom.input.value.length{try{this.configuration.cache&&await this.cache.isValid(t)?this.cache.get(t,(t=>{this.broadcaster.trigger(r.dataDone,{from:i.cache,data:t})})):this.callbacks.onKeyup?this.callbacks.onKeyup(t,(t=>{this.broadcaster.trigger(r.dataDone,{from:i.callback,data:t})})):this.broadcaster.trigger(r.dataDone,{from:i.local,data:this.props.data})}catch(t){this.broadcaster.trigger(r.error,t)}};navigate=t=>{try{if(38!=t&&40!=t)return!1;if(this.props.pointer<-1||this.props.pointer>this.dom.result.children.length-1)return!1;if(38===t&&0===this.props.pointer||40===t&&this.props.pointer===this.dom.result.children.length-1)return!1;38===t&&this.props.pointer>=-1?this.props.pointer--:40===t&&this.props.pointer{try{this.dom.input.value="object"==typeof this.props.data[t]?this.props.data[t][this.configuration.propToMapAsValue]:this.props.data[t],this.callbacks.onSelect&&this.callbacks.onSelect(this.props.data[t]),this.broadcaster.trigger(r.selectDone)}catch(t){this.broadcaster.trigger(r.error,t)}}}class l{_multiple=!1;_theme=o.light;_fieldsToDisplay=[];_maxResults=10;_startQueryingFromChar=2;_propToMapAsValue="";_filterOn=a.prefix;_cache=0;get multiple(){return this._multiple}set multiple(t){this._multiple=t}get theme(){return this._theme}set theme(t){const e=Object.keys(o);if(!e.includes(t))throw new Error(`theme should be one of ${e.toString()}, ${t} given`);this._theme=t}get fieldsToDisplay(){return this._fieldsToDisplay}set fieldsToDisplay(t){this._fieldsToDisplay=t}get maxResults(){return this._maxResults}set maxResults(t){this._maxResults=t}get startQueryingFromChar(){return this._startQueryingFromChar}set startQueryingFromChar(t){this._startQueryingFromChar=t}get propToMapAsValue(){return this._propToMapAsValue}set propToMapAsValue(t){this._propToMapAsValue=t}get filterOn(){return this._filterOn}set filterOn(t){const e=Object.keys(a);if(!e.includes(t))throw new Error(`filterOn should be one of ${e.toString()}, ${t} given`);this._filterOn=t}get cache(){return this._cache}set cache(t){if(isNaN(parseInt(t,10)))throw new Error("cache should be an integer");this._cache=t}constructor(t){if(void 0!==t){if("object"!=typeof t)throw new Error("options should be an object");this.theme=t?.theme||this._theme,this.multiple=t?.multiple||this._multiple,this.fieldsToDisplay=t?.fieldsToDisplay||this._fieldsToDisplay,this.maxResults=t?.maxResults||this._maxResults,this.startQueryingFromChar=t?.startQueryingFromChar||this._startQueryingFromChar,this.propToMapAsValue=t?.propToMapAsValue||this._propToMapAsValue,this.filterOn=t?.filterOn||this._filterOn,this.cache=t?.cache||this._cache}}}class c{_name=null;_duration=null;_braodcaster=null;constructor(t,e=0,s="kompletr.cache"){if(!window.caches)return!1;this._broadcaster=t,this._name=s,this._duration=e}get(t,e){window.caches.open(this._name).then((s=>{s.match(t).then((async t=>{e(await t.json())}))})).catch((t=>{this._broadcaster.trigger(r.error,t)}))}set({string:t,data:e}){window.caches.open(this._name).then((s=>{const r=new Headers;r.set("Content-Type","application/json"),r.set("Cache-Control",`max-age=${this._duration}`),s.put(`/${t}`,new Response(JSON.stringify(e),{headers:r}))})).catch((t=>{this._broadcaster.trigger(r.error,t)}))}async isValid(t){try{const e=await window.caches.open(this._name);return!!await e.match(`/${t}`)}catch(t){this._broadcaster.trigger(r.error,t)}}}class h{subscribers=[];constructor(){}subscribe(t,e){if(!Object.values(r).includes(t))throw new Error(`Event should be one of ${Object.keys(r)}: ${t} given.`);this.subscribers.push({type:t,handler:e})}listen(t,e,s){t.addEventListener(e,s)}trigger(t,e={}){if(!Object.values(r).includes(t))throw new Error(`Event should be one of ${Object.keys(r)}: ${t} given.`);this.subscribers.filter((e=>e.type===t)).forEach((t=>t.handler(e)))}}class u{_data=null;get data(){return this._data}set data(t){if(!Array.isArray(t))throw new Error(`data must be an array (${t.toString()} given)`);this._data=t}_pointer=null;get pointer(){return this._pointer}set pointer(t){if(isNaN(parseInt(t,10)))throw new Error(`pointer must be an integer (${t.toString()} given)`);this._pointer=t}_previousValue=null;get previousValue(){return this._previousValue}set previousValue(t){this._previousValue=t}constructor(t=[]){this._data=t}}class p{_identifiers={wrapper:"kompletr-wrapper",results:"kompletr-results"};_classes={main:"kompletr",results:"container--search-results",result:"item--row",data:"item--property",focus:"focus"};_body=null;get body(){return this._body}set body(t){this._body=t}_input=null;get input(){return this._input}set input(t){if(t instanceof HTMLInputElement==0)throw new Error(`input should be an HTMLInputElement instance: ${t} given.`);this._input=t}_focused=null;get focused(){return this._focused}set focused(t){this._focused=t}_result=null;get result(){return this._result}set result(t){this._result=t}_broadcaster=null;constructor(t,e,s={theme:"light"}){this._broadcaster=e,this.body=document.getElementsByTagName("body")[0],this.input=t instanceof HTMLInputElement?t:document.getElementById(t),this.result=this.build("div",[{id:this._identifiers.results},{class:this._classes.results}]),this.wrapper=this.build("div",[{id:this._identifiers.wrapper},{class:this._classes.results}]),this.wrapper.setAttribute("class",`${this._input.parentElement.getAttribute("class")} ${this._classes.main} ${s.theme}`),this.input.parentNode.insertBefore(this.wrapper,this.input),this.wrapper.appendChild(this.input),this.wrapper.appendChild(this.result)}build(t,e=[]){const s=document.createElement(t);return e.forEach((t=>{s.setAttribute(Object.keys(t)[0],Object.values(t)[0])})),s}focus(t){if(isNaN(parseInt(t,10))||t<0||t>this.result.children.length-1)throw new Error("pointer should be a valid integer in the result lenght range: "+t+" given.");this.focused=null,Array.from(this.result.children).forEach((t=>{(t=>{t.className=this._classes.result})(t)})),this.focused=this.result.children[t],this.result.children[t].className+=` ${this._classes.focus}`}buildResults(t,e){let s="";s=t&&t.length?t.reduce(((t,s)=>{switch(t+=`
`,typeof s.data){case"string":t+=`${s.data}`;break;case"object":{let r=Array.isArray(e)&&e.length?e:Object.keys(s.data);for(let e=0;e${s.data[r[e]]}`;break}}return t+"
"}),""):`
Not found
`,this.result.innerHTML=s,this._broadcaster.trigger(r.domDone,this.result)}}const d=function({input:t,data:e,options:s,onKeyup:r,onSelect:i,onError:a}){try{const[o,d,m]=[new h,new l(s),new u(e)],[f,b]=[new p(t,o,d),d.cache?new c(o,d.cache):null];new n({configuration:d,properties:m,dom:f,cache:b,broadcaster:o,onKeyup:r,onSelect:i,onError:a})}catch(t){console.error(`[kompletr] An error has occured -> ${t.stack}`)}};window.kompletr=d;var m=e.L;export{m as kompletr}; \ No newline at end of file diff --git a/dist/js/kompletr.min.js b/dist/js/kompletr.min.js index 63dac9b..b556f64 100644 --- a/dist/js/kompletr.min.js +++ b/dist/js/kompletr.min.js @@ -1 +1 @@ -var t={d:function(e,s){for(var r in s)t.o(s,r)&&!t.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:s[r]})},o:function(t,e){return Object.prototype.hasOwnProperty.call(t,e)}},e={};t.d(e,{L:function(){return p}});class s{constructor(){}static fadeIn(t){t.style.opacity=0,t.style.display="block",function e(){let s=parseFloat(t.style.opacity);(s+=.1)>1||(t.style.opacity=s,requestAnimationFrame(e))}()}static fadeOut(t){t.style.opacity=1,function e(){(t.style.opacity-=.1)<0?t.style.display="none":requestAnimationFrame(e)}()}}const r=Object.freeze({error:"kompletr.error",domDone:"kompletr.dom.done",dataDone:"kompletr.data.done",selectDone:"kompletr.select.done"}),i=Object.freeze({cache:"cache",callback:"callback",local:"local"}),a=Object.freeze({prefix:"prefix",expression:"expression"}),o=Object.freeze({light:"light",dark:"dark"});class n{broadcaster=null;cache=null;callbacks={};configuration=null;dom=null;props=null;constructor({configuration:t,properties:e,dom:i,cache:a,broadcaster:o,onKeyup:n,onSelect:l,onError:c}){try{this.configuration=t,this.broadcaster=o,this.props=e,this.dom=i,this.cache=a,this.broadcaster.subscribe(r.error,this.error),this.broadcaster.subscribe(r.dataDone,this.showResults),this.broadcaster.subscribe(r.domDone,s.fadeIn.bind(null,this.dom.result)),this.broadcaster.subscribe(r.domDone,this.bindResults),this.broadcaster.subscribe(r.selectDone,this.closeTheShop),this.broadcaster.listen(this.dom.input,"keyup",this.suggest),this.broadcaster.listen(this.dom.body,"click",this.closeTheShop),(n||l||c)&&(this.callbacks=Object.assign(this.callbacks,{onKeyup:n,onSelect:l,onError:c}))}catch(t){o?o.trigger(r.error,t):console.error(`[kompletr] An error has occured -> ${t.stack}`)}}closeTheShop=t=>{if(t.srcElement===this.dom.input)return!0;s.fadeOut(this.dom.result),this.resetPointer()};resetPointer=()=>{this.props.pointer=-1};error=t=>{console.error(`[kompletr] An error has occured -> ${t.stack}`),s.fadeOut(this.dom.result),this.callbacks.onError&&this.callbacks.onError(t)};filter=(t,e)=>t.filter((t=>{const s="string"==typeof t.data?t.data:t.data[this.configuration.propToMapAsValue];return this.configuration.filterOn===a.prefix?0===s.toLowerCase().lastIndexOf(e.toLowerCase(),0):-1!==s.toLowerCase().lastIndexOf(e.toLowerCase())}));showResults=async({from:t,data:e})=>{this.props.data=e,e=this.props.data.map(((t,e)=>({idx:e,data:t}))),this.callbacks.onKeyup||(e=this.filter(e,this.dom.input.value)),this.configuration.cache&&t!==i.cache&&this.cache.set({string:this.dom.input.value,data:e}),this.dom.buildResults(e.slice(0,this.configuration.maxResults),this.configuration.fieldsToDisplay)};bindResults=()=>{if(this.dom.result?.children?.length)for(let t=0;t{this.broadcaster.listen(this.dom.result.children[t],"click",(()=>{this.dom.focused=this.dom.result.children[t],this.select(this.dom.focused.id)}))})(t)};suggest=t=>{if(this.dom.input.value.length{try{this.configuration.cache&&await this.cache.isValid(t)?this.cache.get(t,(t=>{this.broadcaster.trigger(r.dataDone,{from:i.cache,data:t})})):this.callbacks.onKeyup?this.callbacks.onKeyup(t,(t=>{this.broadcaster.trigger(r.dataDone,{from:i.callback,data:t})})):this.broadcaster.trigger(r.dataDone,{from:i.local,data:this.props.data})}catch(t){this.broadcaster.trigger(r.error,t)}};navigate=t=>{try{if(38!=t&&40!=t)return!1;if(this.props.pointer<-1||this.props.pointer>this.dom.result.children.length-1)return!1;if(38===t&&0===this.props.pointer||40===t&&this.props.pointer===this.dom.result.children.length-1)return!1;38===t&&this.props.pointer>=-1?this.props.pointer--:40===t&&this.props.pointer{try{this.dom.input.value="object"==typeof this.props.data[t]?this.props.data[t][this.configuration.propToMapAsValue]:this.props.data[t],this.callbacks.onSelect&&this.callbacks.onSelect(this.props.data[t]),this.broadcaster.trigger(r.selectDone)}catch(t){this.broadcaster.trigger(r.error,t)}}}class l{_multiple=!1;_theme=o.light;_fieldsToDisplay=[];_maxResults=10;_startQueryingFromChar=2;_propToMapAsValue="";_filterOn=a.prefix;_cache=0;get multiple(){return this._multiple}set multiple(t){this._multiple=t}get theme(){return this._theme}set theme(t){const e=Object.keys(o);if(!e.includes(t))throw new Error(`theme should be one of ${e.toString()}, ${t} given`);this._theme=t}get fieldsToDisplay(){return this._fieldsToDisplay}set fieldsToDisplay(t){this._fieldsToDisplay=t}get maxResults(){return this._maxResults}set maxResults(t){this._maxResults=t}get startQueryingFromChar(){return this._startQueryingFromChar}set startQueryingFromChar(t){this._startQueryingFromChar=t}get propToMapAsValue(){return this._propToMapAsValue}set propToMapAsValue(t){this._propToMapAsValue=t}get filterOn(){return this._filterOn}set filterOn(t){const e=Object.keys(a);if(!e.includes(t))throw new Error(`filterOn should be one of ${e.toString()}, ${t} given`);this._filterOn=t}get cache(){return this._cache}set cache(t){if(isNaN(parseInt(t,10)))throw new Error("cache should be an integer");this._cache=t}constructor(t){if(void 0!==t){if("object"!=typeof t)throw new Error("options should be an object");this.theme=t?.theme||this._theme,this.multiple=t?.multiple||this._multiple,this.fieldsToDisplay=t?.fieldsToDisplay||this._fieldsToDisplay,this.maxResults=t?.maxResults||this._maxResults,this.startQueryingFromChar=t?.startQueryingFromChar||this._startQueryingFromChar,this.propToMapAsValue=t?.propToMapAsValue||this._propToMapAsValue,this.filterOn=t?.filterOn||this._filterOn,this.cache=t?.cache||this._cache}}}class c{_name=null;_duration=null;_braodcaster=null;constructor(t,e=0,s="kompletr.cache"){if(!window.caches)return!1;this._broadcaster=t,this._name=s,this._duration=e}get(t,e){window.caches.open(this._name).then((s=>{s.match(t).then((async t=>{e(await t.json())}))})).catch((t=>{this._broadcaster.trigger(r.error,t)}))}set({string:t,data:e}){window.caches.open(this._name).then((s=>{const r=new Headers;r.set("Content-Type","application/json"),r.set("Cache-Control",`max-age=${this._duration}`),s.put(`/${t}`,new Response(JSON.stringify(e),{headers:r}))})).catch((t=>{this._broadcaster.trigger(r.error,t)}))}async isValid(t){try{const e=await window.caches.open(this._name);return!!await e.match(`/${t}`)}catch(t){this._broadcaster.trigger(r.error,t)}}}class h{subscribers=[];constructor(){}subscribe(t,e){if(!Object.values(r).includes(t))throw new Error(`Event should be one of ${Object.keys(r)}: ${t} given.`);this.subscribers.push({type:t,handler:e})}listen(t,e,s){t.addEventListener(e,s)}trigger(t,e={}){if(!Object.values(r).includes(t))throw new Error(`Event should be one of ${Object.keys(r)}: ${t} given.`);this.subscribers.filter((e=>e.type===t)).forEach((t=>t.handler(e)))}}class u{_data=null;get data(){return this._data}set data(t){if(!Array.isArray(t))throw new Error(`data must be an array (${t.toString()} given)`);this._data=t}_pointer=null;get pointer(){return this._pointer}set pointer(t){if(isNaN(parseInt(t,10)))throw new Error(`pointer must be an integer (${t.toString()} given)`);this._pointer=t}_previousValue=null;get previousValue(){return this._previousValue}set previousValue(t){this._previousValue=t}constructor(t=[]){this._data=t}}class d{_identifiers={results:"kpl-result"};_classes={main:"kompletr",input:"input--search",results:"form--search__result",result:"item--result",data:"item--data",focus:"focus"};_body=null;get body(){return this._body}set body(t){this._body=t}_input=null;get input(){return this._input}set input(t){if(t instanceof HTMLInputElement==0)throw new Error(`input should be an HTMLInputElement instance: ${t} given.`);this._input=t}_focused=null;get focused(){return this._focused}set focused(t){this._focused=t}_result=null;get result(){return this._result}set result(t){this._result=t}_broadcaster=null;constructor(t,e,s={theme:"light"}){this._broadcaster=e,this.body=document.getElementsByTagName("body")[0],this.input=t instanceof HTMLInputElement?t:document.getElementById(t),this.input.setAttribute("class",`${this._input.getAttribute("class")} ${this._classes.input}`),this.result=this.build("div",[{id:this._identifiers.results},{class:this._classes.results}]),this.input.parentElement.setAttribute("class",`${this._input.parentElement.getAttribute("class")} ${this._classes.main} ${s.theme}`),this.input.parentElement.appendChild(this._result)}build(t,e=[]){const s=document.createElement(t);return e.forEach((t=>{s.setAttribute(Object.keys(t)[0],Object.values(t)[0])})),s}focus(t){if(isNaN(parseInt(t,10))||t<0||t>this.result.children.length-1)throw new Error("pointer should be a valid integer in the result lenght range: "+t+" given.");this.focused=null,Array.from(this.result.children).forEach((t=>{(t=>{t.className=this._classes.result})(t)})),this.focused=this.result.children[t],this.result.children[t].className+=` ${this._classes.focus}`}buildResults(t,e){let s="";s=t&&t.length?t.reduce(((t,s)=>{switch(t+=`
`,typeof s.data){case"string":t+=`${s.data}`;break;case"object":{let r=Array.isArray(e)&&e.length?e:Object.keys(s.data);for(let e=0;e${s.data[r[e]]}`;break}}return t+"
"}),""):`
Not found
`,this.result.innerHTML=s,this._broadcaster.trigger(r.domDone,this.result)}}const p=function({input:t,data:e,options:s,onKeyup:r,onSelect:i,onError:a}){try{const[o,p,m]=[new h,new l(s),new u(e)],[f,b]=[new d(t,o,p),p.cache?new c(o,p.cache):null];new n({configuration:p,properties:m,dom:f,cache:b,broadcaster:o,onKeyup:r,onSelect:i,onError:a})}catch(t){console.error(`[kompletr] An error has occured -> ${t.stack}`)}};window.kompletr=p;var m=e.L;export{m as kompletr}; \ No newline at end of file +var t={d:function(e,s){for(var r in s)t.o(s,r)&&!t.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:s[r]})},o:function(t,e){return Object.prototype.hasOwnProperty.call(t,e)}},e={};t.d(e,{L:function(){return d}});class s{constructor(){}static fadeIn(t){t.style.opacity=0,t.style.display="block",function e(){let s=parseFloat(t.style.opacity);(s+=.1)>1||(t.style.opacity=s,requestAnimationFrame(e))}()}static fadeOut(t){t.style.opacity=1,function e(){(t.style.opacity-=.1)<0?t.style.display="none":requestAnimationFrame(e)}()}}const r=Object.freeze({error:"kompletr.error",domDone:"kompletr.dom.done",dataDone:"kompletr.data.done",selectDone:"kompletr.select.done"}),i=Object.freeze({cache:"cache",callback:"callback",local:"local"}),a=Object.freeze({prefix:"prefix",expression:"expression"}),o=Object.freeze({light:"light",dark:"dark"});class n{broadcaster=null;cache=null;callbacks={};configuration=null;dom=null;props=null;constructor({configuration:t,properties:e,dom:i,cache:a,broadcaster:o,onKeyup:n,onSelect:l,onError:c}){try{this.configuration=t,this.broadcaster=o,this.props=e,this.dom=i,this.cache=a,this.broadcaster.subscribe(r.error,this.error),this.broadcaster.subscribe(r.dataDone,this.showResults),this.broadcaster.subscribe(r.domDone,s.fadeIn.bind(null,this.dom.result)),this.broadcaster.subscribe(r.domDone,this.bindResults),this.broadcaster.subscribe(r.selectDone,this.closeTheShop),this.broadcaster.listen(this.dom.input,"keyup",this.suggest),this.broadcaster.listen(this.dom.body,"click",this.closeTheShop),(n||l||c)&&(this.callbacks=Object.assign(this.callbacks,{onKeyup:n,onSelect:l,onError:c}))}catch(t){o?o.trigger(r.error,t):console.error(`[kompletr] An error has occured -> ${t.stack}`)}}closeTheShop=t=>{if(t.srcElement===this.dom.input)return!0;s.fadeOut(this.dom.result),this.resetPointer()};resetPointer=()=>{this.props.pointer=-1};error=t=>{console.error(`[kompletr] An error has occured -> ${t.stack}`),s.fadeOut(this.dom.result),this.callbacks.onError&&this.callbacks.onError(t)};filter=(t,e)=>t.filter((t=>{const s="string"==typeof t.data?t.data:t.data[this.configuration.propToMapAsValue];return this.configuration.filterOn===a.prefix?0===s.toLowerCase().lastIndexOf(e.toLowerCase(),0):-1!==s.toLowerCase().lastIndexOf(e.toLowerCase())}));showResults=async({from:t,data:e})=>{this.props.data=e,e=this.props.data.map(((t,e)=>({idx:e,data:t}))),this.callbacks.onKeyup||(e=this.filter(e,this.dom.input.value)),this.configuration.cache&&t!==i.cache&&this.cache.set({string:this.dom.input.value,data:e}),this.dom.buildResults(e.slice(0,this.configuration.maxResults),this.configuration.fieldsToDisplay)};bindResults=()=>{if(this.dom.result?.children?.length)for(let t=0;t{this.broadcaster.listen(this.dom.result.children[t],"click",(()=>{this.dom.focused=this.dom.result.children[t],this.select(this.dom.focused.id)}))})(t)};suggest=t=>{if(this.dom.input.value.length{try{this.configuration.cache&&await this.cache.isValid(t)?this.cache.get(t,(t=>{this.broadcaster.trigger(r.dataDone,{from:i.cache,data:t})})):this.callbacks.onKeyup?this.callbacks.onKeyup(t,(t=>{this.broadcaster.trigger(r.dataDone,{from:i.callback,data:t})})):this.broadcaster.trigger(r.dataDone,{from:i.local,data:this.props.data})}catch(t){this.broadcaster.trigger(r.error,t)}};navigate=t=>{try{if(38!=t&&40!=t)return!1;if(this.props.pointer<-1||this.props.pointer>this.dom.result.children.length-1)return!1;if(38===t&&0===this.props.pointer||40===t&&this.props.pointer===this.dom.result.children.length-1)return!1;38===t&&this.props.pointer>=-1?this.props.pointer--:40===t&&this.props.pointer{try{this.dom.input.value="object"==typeof this.props.data[t]?this.props.data[t][this.configuration.propToMapAsValue]:this.props.data[t],this.callbacks.onSelect&&this.callbacks.onSelect(this.props.data[t]),this.broadcaster.trigger(r.selectDone)}catch(t){this.broadcaster.trigger(r.error,t)}}}class l{_multiple=!1;_theme=o.light;_fieldsToDisplay=[];_maxResults=10;_startQueryingFromChar=2;_propToMapAsValue="";_filterOn=a.prefix;_cache=0;get multiple(){return this._multiple}set multiple(t){this._multiple=t}get theme(){return this._theme}set theme(t){const e=Object.keys(o);if(!e.includes(t))throw new Error(`theme should be one of ${e.toString()}, ${t} given`);this._theme=t}get fieldsToDisplay(){return this._fieldsToDisplay}set fieldsToDisplay(t){this._fieldsToDisplay=t}get maxResults(){return this._maxResults}set maxResults(t){this._maxResults=t}get startQueryingFromChar(){return this._startQueryingFromChar}set startQueryingFromChar(t){this._startQueryingFromChar=t}get propToMapAsValue(){return this._propToMapAsValue}set propToMapAsValue(t){this._propToMapAsValue=t}get filterOn(){return this._filterOn}set filterOn(t){const e=Object.keys(a);if(!e.includes(t))throw new Error(`filterOn should be one of ${e.toString()}, ${t} given`);this._filterOn=t}get cache(){return this._cache}set cache(t){if(isNaN(parseInt(t,10)))throw new Error("cache should be an integer");this._cache=t}constructor(t){if(void 0!==t){if("object"!=typeof t)throw new Error("options should be an object");this.theme=t?.theme||this._theme,this.multiple=t?.multiple||this._multiple,this.fieldsToDisplay=t?.fieldsToDisplay||this._fieldsToDisplay,this.maxResults=t?.maxResults||this._maxResults,this.startQueryingFromChar=t?.startQueryingFromChar||this._startQueryingFromChar,this.propToMapAsValue=t?.propToMapAsValue||this._propToMapAsValue,this.filterOn=t?.filterOn||this._filterOn,this.cache=t?.cache||this._cache}}}class c{_name=null;_duration=null;_braodcaster=null;constructor(t,e=0,s="kompletr.cache"){if(!window.caches)return!1;this._broadcaster=t,this._name=s,this._duration=e}get(t,e){window.caches.open(this._name).then((s=>{s.match(t).then((async t=>{e(await t.json())}))})).catch((t=>{this._broadcaster.trigger(r.error,t)}))}set({string:t,data:e}){window.caches.open(this._name).then((s=>{const r=new Headers;r.set("Content-Type","application/json"),r.set("Cache-Control",`max-age=${this._duration}`),s.put(`/${t}`,new Response(JSON.stringify(e),{headers:r}))})).catch((t=>{this._broadcaster.trigger(r.error,t)}))}async isValid(t){try{const e=await window.caches.open(this._name);return!!await e.match(`/${t}`)}catch(t){this._broadcaster.trigger(r.error,t)}}}class h{subscribers=[];constructor(){}subscribe(t,e){if(!Object.values(r).includes(t))throw new Error(`Event should be one of ${Object.keys(r)}: ${t} given.`);this.subscribers.push({type:t,handler:e})}listen(t,e,s){t.addEventListener(e,s)}trigger(t,e={}){if(!Object.values(r).includes(t))throw new Error(`Event should be one of ${Object.keys(r)}: ${t} given.`);this.subscribers.filter((e=>e.type===t)).forEach((t=>t.handler(e)))}}class u{_data=null;get data(){return this._data}set data(t){if(!Array.isArray(t))throw new Error(`data must be an array (${t.toString()} given)`);this._data=t}_pointer=null;get pointer(){return this._pointer}set pointer(t){if(isNaN(parseInt(t,10)))throw new Error(`pointer must be an integer (${t.toString()} given)`);this._pointer=t}_previousValue=null;get previousValue(){return this._previousValue}set previousValue(t){this._previousValue=t}constructor(t=[]){this._data=t}}class p{_identifiers={wrapper:"kompletr-wrapper",results:"kompletr-results"};_classes={main:"kompletr",results:"container--search-results",result:"item--row",data:"item--property",focus:"focus"};_body=null;get body(){return this._body}set body(t){this._body=t}_input=null;get input(){return this._input}set input(t){if(t instanceof HTMLInputElement==0)throw new Error(`input should be an HTMLInputElement instance: ${t} given.`);this._input=t}_focused=null;get focused(){return this._focused}set focused(t){this._focused=t}_result=null;get result(){return this._result}set result(t){this._result=t}_broadcaster=null;constructor(t,e,s={theme:"light"}){this._broadcaster=e,this.body=document.getElementsByTagName("body")[0],this.input=t instanceof HTMLInputElement?t:document.getElementById(t),this.result=this.build("div",[{id:this._identifiers.results},{class:this._classes.results}]),this.wrapper=this.build("div",[{id:this._identifiers.wrapper},{class:this._classes.results}]),this.wrapper.setAttribute("class",`${this._input.parentElement.getAttribute("class")} ${this._classes.main} ${s.theme}`),this.input.parentNode.insertBefore(this.wrapper,this.input),this.wrapper.appendChild(this.input),this.wrapper.appendChild(this.result)}build(t,e=[]){const s=document.createElement(t);return e.forEach((t=>{s.setAttribute(Object.keys(t)[0],Object.values(t)[0])})),s}focus(t){if(isNaN(parseInt(t,10))||t<0||t>this.result.children.length-1)throw new Error("pointer should be a valid integer in the result lenght range: "+t+" given.");this.focused=null,Array.from(this.result.children).forEach((t=>{(t=>{t.className=this._classes.result})(t)})),this.focused=this.result.children[t],this.result.children[t].className+=` ${this._classes.focus}`}buildResults(t,e){let s="";s=t&&t.length?t.reduce(((t,s)=>{switch(t+=`
`,typeof s.data){case"string":t+=`${s.data}`;break;case"object":{let r=Array.isArray(e)&&e.length?e:Object.keys(s.data);for(let e=0;e${s.data[r[e]]}`;break}}return t+"
"}),""):`
Not found
`,this.result.innerHTML=s,this._broadcaster.trigger(r.domDone,this.result)}}const d=function({input:t,data:e,options:s,onKeyup:r,onSelect:i,onError:a}){try{const[o,d,m]=[new h,new l(s),new u(e)],[f,b]=[new p(t,o,d),d.cache?new c(o,d.cache):null];new n({configuration:d,properties:m,dom:f,cache:b,broadcaster:o,onKeyup:r,onSelect:i,onError:a})}catch(t){console.error(`[kompletr] An error has occured -> ${t.stack}`)}};window.kompletr=d;var m=e.L;export{m as kompletr}; \ No newline at end of file diff --git a/package.json b/package.json index 6321967..15fb065 100755 --- a/package.json +++ b/package.json @@ -55,9 +55,9 @@ "ci:e2e": "cypress run --browser chrome ./cypress", "ci:cy": "cypress open", "ci:test": "node --experimental-vm-modules node_modules/jest/bin/jest.js ./test/**.spec.js -- --coverage", - "demo": "npm run demo:css && npm run demo:js", - "demo:css": "node-sass ./src/sass/kompletr.demo.scss ./demo/css/kompletr.demo.min.css --output-style compressed", - "demo:css-demo": "node-sass ./src/sass/kompletr.scss ./demo/css/kompletr.min.css --output-style compressed", + "demo": "npm run demo:css && npm run demo:css-demo && npm run demo:js", + "demo:css": "node-sass ./src/sass/kompletr.scss ./demo/css/kompletr.min.css --output-style compressed", + "demo:css-demo": "node-sass ./src/sass/kompletr.demo.scss ./demo/css/kompletr.demo.min.css --output-style compressed", "demo:js": "webpack --mode production --config ./webpack.config.prod.js", "dev": "webpack-dashboard -- webpack serve --hot --mode development --config ./webpack.config.js", "dist": "mkdir -p dist & npm run dist:css && npm run dist:js",