From 46edb214a3bc277e6920edb640e564b1cfa32610 Mon Sep 17 00:00:00 2001 From: charrafimed Date: Wed, 7 Jan 2026 02:13:37 +0100 Subject: [PATCH 1/5] wip --- dist/cdn.js | 14 ++--- dist/cdn.min.js | 2 +- dist/module.cjs.js | 14 ++--- dist/module.esm.js | 14 ++--- index.html | 24 ++++---- scripts/build.ts | 86 ----------------------------- src/core/ComboboxCollection.ts | 54 +++++++----------- src/factories/CreateComboboxRoot.ts | 1 + 8 files changed, 53 insertions(+), 156 deletions(-) delete mode 100644 scripts/build.ts diff --git a/dist/cdn.js b/dist/cdn.js index 1840682..9bc06e8 100644 --- a/dist/cdn.js +++ b/dist/cdn.js @@ -229,19 +229,16 @@ if (!this.needsReindex) return; this.navIndex = []; + console.log("called"); for (let i = 0; i < this.items.length; i++) { if (!this.items[i]?.disabled) { this.navIndex.push(i); } } - if (this.items.length >= this.searchThreshold) { - this.searchIndex = this.items.map((item) => ({ - key: item.key, - value: String(item.value).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "") - })); - } else { - this.searchIndex = []; - } + this.searchIndex = this.items.map((item) => ({ + key: item.key, + value: String(item.value).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "") + })); this.needsReindex = false; } search(query) { @@ -261,6 +258,7 @@ if (this.searchIndex) { const normalized = q.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); results = []; + console.log("inside search index", this.searchIndex); for (const {key, value} of this.searchIndex) { if (value.includes(normalized)) { const item = this.itemsMap.get(key); diff --git a/dist/cdn.min.js b/dist/cdn.min.js index 38a7076..a4b8002 100644 --- a/dist/cdn.min.js +++ b/dist/cdn.min.js @@ -1 +1 @@ -(()=>{function v(h){return{init(){let t=h.extractProp(this.$el,"display-value","");t&&(this.__displayValue=t),this.__handleEvents()},__handleEvents(){this.$el.addEventListener("focus",()=>{this.__startTyping()}),this.$el.addEventListener("input",t=>{t.stopPropagation(),this.__isTyping&&this.__open()}),this.$el.addEventListener("blur",()=>{this.__stopTyping()}),this.$el.addEventListener("keydown",t=>{switch(t.key){case"ArrowDown":if(t.preventDefault(),t.stopPropagation(),!this.__isOpen){this.__open();break}this.__activateNext();break;case"ArrowUp":if(t.preventDefault(),t.stopPropagation(),!this.__isOpen){this.__open();break}this.__activatePrev();break;case"Enter":t.preventDefault(),t.stopPropagation(),this.__selectActive(),this.__stopTyping(),this.__isMultiple||(this.__close(),this.__resetInput());break}})}}}function f(h,t){let i="option";return{__uniqueKey:"option-"+t,init(){this.$el.dataset.slot=i;let e=h.extractProp(this.$el,"value","");this.$el.dataset.key=this.__uniqueKey,this.$el.dataset.value=e;let n=h.extractProp(this.$el,"disabled",!1,!1);this.__add(this.__uniqueKey,e,n),this.$watch("__activedKey",s=>{s===this.__uniqueKey?this.$el.setAttribute("data-active","true"):this.$el.removeAttribute("data-active")}),this.$watch("__selectedKeys",s=>{let r=!1;this.__isMultiple?r=Array.isArray(s)&&s.includes(this.__uniqueKey):r=s===this.__uniqueKey,r?(this.$el.setAttribute("aria-selected","true"),this.$el.setAttribute("data-selected","true")):(this.$el.setAttribute("aria-selected","false"),this.$el.removeAttribute("data-selected"))}),this.$nextTick(()=>{n&&this.$el.setAttribute("tabindex","-1")})},destroy(){this.__forget(this.__uniqueKey)}}}var b=class{constructor(t={}){this.items=[];this.itemsMap=new Map;this.activeNavPos=-1;this.needsReindex=!1;this.navIndex=[];this.lastQuery="";this.isProcessing=!1;this.pending=Alpine.reactive({state:!1}),this.activeIndex=Alpine.reactive({value:void 0}),this.searchThreshold=t.searchThreshold??500}add(t,i,e=!1){if(this.itemsMap.has(t))return;let n={key:t,value:i,disabled:e};this.items.push(n),this.itemsMap.set(t,n),this.invalidate()}forget(t){let i=this.itemsMap.get(t);if(!i)return;let e=this.items.indexOf(i);this.itemsMap.delete(t),this.items.splice(e,1),this.activeIndex.value===e?(this.activeIndex.value=void 0,this.activeNavPos=-1):this.activeIndex.value&&this.activeIndex.value>e&&this.activeIndex.value--,this.invalidate()}activate(t){let i=this.get(t);if(!i||i.disabled)return;this.rebuildIndexes();let e=this.items.indexOf(i);this.activeIndex.value!==e&&(this.activeIndex.value=e,this.activeNavPos=this.navIndex.indexOf(e))}deactivate(){this.activeIndex.value=void 0,this.activeNavPos=-1}isActivated(t){let i=this.get(t);return i?this.items.indexOf(i)===this.activeIndex.value:!1}getActiveItem(){return this.activeIndex.value===void 0?null:this.items[this.activeIndex.value]}activateFirst(){this.rebuildIndexes(),!!this.navIndex.length&&(this.navIndex[0]&&(this.activeIndex.value=this.navIndex[0]),this.activeNavPos=0)}activateLast(){if(this.rebuildIndexes(),!this.navIndex.length)return;this.activeNavPos=this.navIndex.length-1;let t=this.navIndex[this.activeNavPos];typeof t=="number"&&(this.activeIndex.value=t)}activateNext(){if(this.rebuildIndexes(),!!this.navIndex.length){if(this.activeNavPos===-1){this.activateFirst();return}this.activeNavPos=(this.activeNavPos+1)%this.navIndex.length,this.activeIndex.value=this.navIndex[this.activeNavPos]}}activatePrev(){if(this.rebuildIndexes(),!!this.navIndex.length){if(this.activeNavPos===-1){this.activateLast();return}this.activeNavPos=this.activeNavPos===0?this.navIndex.length-1:this.activeNavPos-1,this.activeIndex.value=this.navIndex[this.activeNavPos]}}invalidate(){this.needsReindex=!0,this.lastQuery="",this.lastResults=[],this.scheduleBatch()}scheduleBatch(){this.isProcessing||(this.isProcessing=!0,this.pending.state=!0,queueMicrotask(()=>{this.rebuildIndexes(),this.isProcessing=!1,this.pending.state=!1}))}toggleIsPending(){this.pending.state=!this.pending.state}rebuildIndexes(){if(!!this.needsReindex){this.navIndex=[];for(let t=0;t=this.searchThreshold?this.searchIndex=this.items.map(t=>({key:t.key,value:String(t.value).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"")})):this.searchIndex=[],this.needsReindex=!1}}search(t){if(!t)return this.lastQuery="",this.lastResults=[],this.items;let i=t.toLowerCase();if(this.lastQuery&&i.startsWith(this.lastQuery)&&this.lastResults){let n=this.lastResults.filter(s=>String(s.value).toLowerCase().includes(i));return this.lastQuery=i,this.lastResults=n,n}let e;if(this.searchIndex){let n=i.normalize("NFD").replace(/[\u0300-\u036f]/g,"");e=[];for(let{key:s,value:r}of this.searchIndex)if(r.includes(n)){let l=this.itemsMap.get(s);l&&e.push(l)}}else e=this.items.filter(n=>String(n.value).toLowerCase().includes(i));return this.lastQuery=i,this.lastResults=e,e}get(t){return this.itemsMap.get(t)}getValueByKey(t){return this.get(t)?.value}getKeyByIndex(t){return t==null?null:this.items[t]?.key??null}all(){return this.items}get size(){return this.items.length}},y=b;function m({el:h,effect:t}){let i=new y;return{__state:null,__isOpen:!1,__isMultiple:!1,__isTyping:!1,__isLoading:!1,__uid:0,__static:!1,__keepActivated:!0,__optionsEl:void 0,__compareBy:void 0,__activedKey:void 0,__selectedKeys:void 0,__filteredKeys:null,__isDisabled:!1,__searchQuery:"",__add:(e,n,s)=>i.add(e,n,s),__forget:e=>i.forget(e),__activate:e=>i.activate(e),__isActive:e=>i.isActivated(e),__deactivate:()=>i.deactivate(),__getValueByKey:e=>i.getValueByKey(e),__activateNext:()=>i.activateNext(),__activatePrev:()=>i.activatePrev(),__activateFirst:()=>i.activateFirst(),__activateLast:()=>i.activateLast(),__isVisible(e){return this.__searchQuery===""||!this.__filteredKeys?!0:this.__filteredKeys.includes(e)},init(){t(()=>{this.__isLoading=i.pending.state}),t(()=>{this.__activedKey=i.getKeyByIndex(i.activeIndex.value)}),t(()=>{let s=i.search(this.__searchQuery).map(r=>r.key);s.length>=0?this.__filteredKeys=s:this.__filteredKeys=null,this.__activedKey&&this.__filteredKeys&&!this.__filteredKeys.includes(this.__activedKey)&&i.deactivate(),this.__isOpen&&!i.getActiveItem()&&this.__filteredKeys&&this.__filteredKeys.length&&i.activate(this.__filteredKeys[0])}),this.__isMultiple?this.__selectedKeys=[]:this.__selectedKeys=null,this.__isMultiple=Alpine.extractProp(h,"multiple",!1),this.__isDisabled=Alpine.extractProp(h,"disabled",!1),this.__compareBy=Alpine.extractProp(h,"by","");let e=this.__isMultiple?[]:"",n=Alpine.extractProp(h,"initial-value",e);this.__state=n,this.__registerEventsDelector()},__open(){if(this.__isOpen)return;this.__isOpen=!0;let e=this.$refs.__input;requestAnimationFrame(()=>{e.focus({preventScroll:!0}),this.__activateSelectedOrFirst()})},__activateSelectedOrFirst(e=!0){if(!(!this.__isOpen||i.getActiveItem())){if(e&&this.__selectedKeys){let s=this.__isMultiple?this.__selectedKeys[0]:this.__selectedKeys;if(s){this.__activate(s);return}}i.activateFirst()}},__close(){this.__isOpen=!1,this.__deactivate()},__handleSelection(e){let n=this.__getValueByKey(e);if(!this.__isMultiple){this.__selectedKeys=e,this.__state===n?(this.__state=null,this.__selectedKeys=null):this.__state=n,this.__static||this.__close();return}Array.isArray(this.__selectedKeys)||(this.__selectedKeys=[]),Array.isArray(this.__state)||(this.__state=[]);let s=this.__state.findIndex(l=>this.__compare(l,n)),r=this.__selectedKeys.indexOf(e);s===-1?(this.__state.push(n),this.__selectedKeys.push(e)):(this.__state.splice(s,1),this.__selectedKeys.splice(r,1))},__selectActive(){!this.__activedKey||this.__handleSelection(this.__activedKey)},__startTyping(){this.__isTyping=!0},__stopTyping(){this.__isTyping=!1},__resetInput(){let e=this.$refs.__input;if(!e)return;let n=this.__getCurrentValue();e.value=n},__getCurrentValue(){return!this.$refs.__input||!this.__state?"":typeof this.__state=="string"?this.__state:""},__compare(e,n){let s=this.__compareBy;if(!this.__compareBy)s=(r,l)=>Alpine.raw(r)===Alpine.raw(l);else if(typeof this.__compareBy=="string"){let r=this.__compareBy;s=(l,d)=>{if(!l||typeof l!="object"||!d||typeof d!="object")return Alpine.raw(l)===Alpine.raw(d);let p=l,a=d;return p[r]===a[r]}}return s(e,n)},__nextId(){return++this.__uid},__registerEventsDelector(){let e=s=>Alpine.findClosest(s,r=>r.dataset.slot==="option"),n=s=>function(r){if(r.stopPropagation(),!(r.target instanceof Element))return;let l=e(r.target);!l||s(l)};this.$nextTick(()=>{this.__optionsEl=this.$refs.__options,!!this.__optionsEl&&(this.__optionsEl.addEventListener("click",n(s=>{!s.dataset.key||(this.__handleSelection(s.dataset.key),!this.__isMultiple&&!this.__static&&(this.__close(),this.__resetInput()),this.$nextTick(()=>this.$refs.__input.focus({preventScroll:!0})))})),this.__optionsEl.addEventListener("mouseover",n(s=>{!s.dataset.key||this.__activate(s.dataset.key)})),this.__optionsEl.addEventListener("mousemove",n(s=>{this.__isActive(s.dataset.key||"")||!s.dataset.key||this.__activate(s.dataset.key)})),this.__optionsEl.addEventListener("mouseout",n(()=>{this.__keepActivated||this.__deactivate()})))})}}}function x(h){h.directive("combobox",(a,{value:o,modifiers:u},{Alpine:_,effect:c})=>{switch(o){case null:t(_,a,c);break;case"input":i(_,a);break;case"button":r(_,a);break;case"options":e(a);break;case"option":n(_,a);break;case"options-group":s(_,a);break;case"loading":d(_,a,u);break;case"separator":p(_,a);break;case"empty":l(_,a);break;default:console.error("invalid x-combobox value",o,"use input, button, option, options or leave mepty for root level instead");break}}).before("bind");function t(a,o,u){a.bind(o,{"x-data"(){return m({el:o,effect:u})}})}function i(a,o){a.bind(o,{"x-ref":"__input","x-model":"__searchQuery","x-bind:id"(){return this.$id("combobox-input")},role:"combobox",tabindex:"0","aria-autocomplete":"list","x-data"(){return v(a)}})}function e(a){h.bind(a,{"x-ref":"__options","x-bind:id"(){return this.$id("combobox-options")},role:"listbox","x-init"(){return this.$data.__static=h.extractProp(this.$el,"static",!1),h.bound(this.$el,"keepActivated")&&(this.__keepActivated=!0),this.$el.dataset.slot="options"},"x-show"(){return this.$data.__static?!0:this.$data.__isOpen}})}function n(a,o){a.bind(o,{"x-id"(){return["combobox-option"]},"x-bind:id"(){return this.$id("combobox-option")},role:"option","x-show"(){return this.$data.__isVisible(this.$el.dataset.key)},"x-data"(){return f(a,this.__nextId())}})}function s(a,o){a.bind(o,{"x-id"(){return["combobox-options-group"]},"x-bind:id"(){return this.$id("combobox-options-group")},role:"option","x-show"(){return!0}})}function r(a,o){a.bind(o,{"x-ref":"__button","x-bind:id"(){return this.$id("combobox-button")},tabindex:"-1","aria-haspopup":"true","x-on:click"(u){this.__isDisabled||(this.__isOpen?(this.__close(),this.__resetInput()):(u.preventDefault(),this.__open()),requestAnimationFrame(()=>this.$refs.__input.focus({preventScroll:!0})))}})}function l(a,o){a.bind(o,{"x-bind:id"(){return this.$id("combobox-button")},tabindex:"-1","aria-haspopup":"true","x-show"(){return Array.isArray(this.__filteredKeys)&&this.__filteredKeys.length===0}})}function d(a,o,u){let _=a.$data(o);u.filter(c=>c==="hide")}function p(a,o){a.bind(o,{})}}document.addEventListener("alpine:init",()=>{window.Alpine.plugin(x)});})(); +(()=>{function v(h){return{init(){let e=h.extractProp(this.$el,"display-value","");e&&(this.__displayValue=e),this.__handleEvents()},__handleEvents(){this.$el.addEventListener("focus",()=>{this.__startTyping()}),this.$el.addEventListener("input",e=>{e.stopPropagation(),this.__isTyping&&this.__open()}),this.$el.addEventListener("blur",()=>{this.__stopTyping()}),this.$el.addEventListener("keydown",e=>{switch(e.key){case"ArrowDown":if(e.preventDefault(),e.stopPropagation(),!this.__isOpen){this.__open();break}this.__activateNext();break;case"ArrowUp":if(e.preventDefault(),e.stopPropagation(),!this.__isOpen){this.__open();break}this.__activatePrev();break;case"Enter":e.preventDefault(),e.stopPropagation(),this.__selectActive(),this.__stopTyping(),this.__isMultiple||(this.__close(),this.__resetInput());break}})}}}function f(h,e){let i="option";return{__uniqueKey:"option-"+e,init(){this.$el.dataset.slot=i;let t=h.extractProp(this.$el,"value","");this.$el.dataset.key=this.__uniqueKey,this.$el.dataset.value=t;let n=h.extractProp(this.$el,"disabled",!1,!1);this.__add(this.__uniqueKey,t,n),this.$watch("__activedKey",s=>{s===this.__uniqueKey?this.$el.setAttribute("data-active","true"):this.$el.removeAttribute("data-active")}),this.$watch("__selectedKeys",s=>{let r=!1;this.__isMultiple?r=Array.isArray(s)&&s.includes(this.__uniqueKey):r=s===this.__uniqueKey,r?(this.$el.setAttribute("aria-selected","true"),this.$el.setAttribute("data-selected","true")):(this.$el.setAttribute("aria-selected","false"),this.$el.removeAttribute("data-selected"))}),this.$nextTick(()=>{n&&this.$el.setAttribute("tabindex","-1")})},destroy(){this.__forget(this.__uniqueKey)}}}var b=class{constructor(e={}){this.items=[];this.itemsMap=new Map;this.activeNavPos=-1;this.needsReindex=!1;this.navIndex=[];this.lastQuery="";this.isProcessing=!1;this.pending=Alpine.reactive({state:!1}),this.activeIndex=Alpine.reactive({value:void 0}),this.searchThreshold=e.searchThreshold??500}add(e,i,t=!1){if(this.itemsMap.has(e))return;let n={key:e,value:i,disabled:t};this.items.push(n),this.itemsMap.set(e,n),this.invalidate()}forget(e){let i=this.itemsMap.get(e);if(!i)return;let t=this.items.indexOf(i);this.itemsMap.delete(e),this.items.splice(t,1),this.activeIndex.value===t?(this.activeIndex.value=void 0,this.activeNavPos=-1):this.activeIndex.value&&this.activeIndex.value>t&&this.activeIndex.value--,this.invalidate()}activate(e){let i=this.get(e);if(!i||i.disabled)return;this.rebuildIndexes();let t=this.items.indexOf(i);this.activeIndex.value!==t&&(this.activeIndex.value=t,this.activeNavPos=this.navIndex.indexOf(t))}deactivate(){this.activeIndex.value=void 0,this.activeNavPos=-1}isActivated(e){let i=this.get(e);return i?this.items.indexOf(i)===this.activeIndex.value:!1}getActiveItem(){return this.activeIndex.value===void 0?null:this.items[this.activeIndex.value]}activateFirst(){this.rebuildIndexes(),!!this.navIndex.length&&(this.navIndex[0]&&(this.activeIndex.value=this.navIndex[0]),this.activeNavPos=0)}activateLast(){if(this.rebuildIndexes(),!this.navIndex.length)return;this.activeNavPos=this.navIndex.length-1;let e=this.navIndex[this.activeNavPos];typeof e=="number"&&(this.activeIndex.value=e)}activateNext(){if(this.rebuildIndexes(),!!this.navIndex.length){if(this.activeNavPos===-1){this.activateFirst();return}this.activeNavPos=(this.activeNavPos+1)%this.navIndex.length,this.activeIndex.value=this.navIndex[this.activeNavPos]}}activatePrev(){if(this.rebuildIndexes(),!!this.navIndex.length){if(this.activeNavPos===-1){this.activateLast();return}this.activeNavPos=this.activeNavPos===0?this.navIndex.length-1:this.activeNavPos-1,this.activeIndex.value=this.navIndex[this.activeNavPos]}}invalidate(){this.needsReindex=!0,this.lastQuery="",this.lastResults=[],this.scheduleBatch()}scheduleBatch(){this.isProcessing||(this.isProcessing=!0,this.pending.state=!0,queueMicrotask(()=>{this.rebuildIndexes(),this.isProcessing=!1,this.pending.state=!1}))}toggleIsPending(){this.pending.state=!this.pending.state}rebuildIndexes(){if(!!this.needsReindex){this.navIndex=[],console.log("called");for(let e=0;e({key:e.key,value:String(e.value).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"")})),this.needsReindex=!1}}search(e){if(!e)return this.lastQuery="",this.lastResults=[],this.items;let i=e.toLowerCase();if(this.lastQuery&&i.startsWith(this.lastQuery)&&this.lastResults){let n=this.lastResults.filter(s=>String(s.value).toLowerCase().includes(i));return this.lastQuery=i,this.lastResults=n,n}let t;if(this.searchIndex){let n=i.normalize("NFD").replace(/[\u0300-\u036f]/g,"");t=[],console.log("inside search index",this.searchIndex);for(let{key:s,value:r}of this.searchIndex)if(r.includes(n)){let l=this.itemsMap.get(s);l&&t.push(l)}}else t=this.items.filter(n=>String(n.value).toLowerCase().includes(i));return this.lastQuery=i,this.lastResults=t,t}get(e){return this.itemsMap.get(e)}getValueByKey(e){return this.get(e)?.value}getKeyByIndex(e){return e==null?null:this.items[e]?.key??null}all(){return this.items}get size(){return this.items.length}},y=b;function m({el:h,effect:e}){let i=new y;return{__state:null,__isOpen:!1,__isMultiple:!1,__isTyping:!1,__isLoading:!1,__uid:0,__static:!1,__keepActivated:!0,__optionsEl:void 0,__compareBy:void 0,__activedKey:void 0,__selectedKeys:void 0,__filteredKeys:null,__isDisabled:!1,__searchQuery:"",__add:(t,n,s)=>i.add(t,n,s),__forget:t=>i.forget(t),__activate:t=>i.activate(t),__isActive:t=>i.isActivated(t),__deactivate:()=>i.deactivate(),__getValueByKey:t=>i.getValueByKey(t),__activateNext:()=>i.activateNext(),__activatePrev:()=>i.activatePrev(),__activateFirst:()=>i.activateFirst(),__activateLast:()=>i.activateLast(),__isVisible(t){return this.__searchQuery===""||!this.__filteredKeys?!0:this.__filteredKeys.includes(t)},init(){e(()=>{this.__isLoading=i.pending.state}),e(()=>{this.__activedKey=i.getKeyByIndex(i.activeIndex.value)}),e(()=>{let s=i.search(this.__searchQuery).map(r=>r.key);s.length>=0?this.__filteredKeys=s:this.__filteredKeys=null,this.__activedKey&&this.__filteredKeys&&!this.__filteredKeys.includes(this.__activedKey)&&i.deactivate(),this.__isOpen&&!i.getActiveItem()&&this.__filteredKeys&&this.__filteredKeys.length&&i.activate(this.__filteredKeys[0])}),this.__isMultiple?this.__selectedKeys=[]:this.__selectedKeys=null,this.__isMultiple=Alpine.extractProp(h,"multiple",!1),this.__isDisabled=Alpine.extractProp(h,"disabled",!1),this.__compareBy=Alpine.extractProp(h,"by","");let t=this.__isMultiple?[]:"",n=Alpine.extractProp(h,"initial-value",t);this.__state=n,this.__registerEventsDelector()},__open(){if(this.__isOpen)return;this.__isOpen=!0;let t=this.$refs.__input;requestAnimationFrame(()=>{t.focus({preventScroll:!0}),this.__activateSelectedOrFirst()})},__activateSelectedOrFirst(t=!0){if(!(!this.__isOpen||i.getActiveItem())){if(t&&this.__selectedKeys){let s=this.__isMultiple?this.__selectedKeys[0]:this.__selectedKeys;if(s){this.__activate(s);return}}i.activateFirst()}},__close(){this.__isOpen=!1,this.__deactivate()},__handleSelection(t){let n=this.__getValueByKey(t);if(!this.__isMultiple){this.__selectedKeys=t,this.__state===n?(this.__state=null,this.__selectedKeys=null):this.__state=n,this.__static||this.__close();return}Array.isArray(this.__selectedKeys)||(this.__selectedKeys=[]),Array.isArray(this.__state)||(this.__state=[]);let s=this.__state.findIndex(l=>this.__compare(l,n)),r=this.__selectedKeys.indexOf(t);s===-1?(this.__state.push(n),this.__selectedKeys.push(t)):(this.__state.splice(s,1),this.__selectedKeys.splice(r,1))},__selectActive(){!this.__activedKey||this.__handleSelection(this.__activedKey)},__startTyping(){this.__isTyping=!0},__stopTyping(){this.__isTyping=!1},__resetInput(){let t=this.$refs.__input;if(!t)return;let n=this.__getCurrentValue();t.value=n},__getCurrentValue(){return!this.$refs.__input||!this.__state?"":typeof this.__state=="string"?this.__state:""},__compare(t,n){let s=this.__compareBy;if(!this.__compareBy)s=(r,l)=>Alpine.raw(r)===Alpine.raw(l);else if(typeof this.__compareBy=="string"){let r=this.__compareBy;s=(l,d)=>{if(!l||typeof l!="object"||!d||typeof d!="object")return Alpine.raw(l)===Alpine.raw(d);let p=l,a=d;return p[r]===a[r]}}return s(t,n)},__nextId(){return++this.__uid},__registerEventsDelector(){let t=s=>Alpine.findClosest(s,r=>r.dataset.slot==="option"),n=s=>function(r){if(r.stopPropagation(),!(r.target instanceof Element))return;let l=t(r.target);!l||s(l)};this.$nextTick(()=>{this.__optionsEl=this.$refs.__options,!!this.__optionsEl&&(this.__optionsEl.addEventListener("click",n(s=>{!s.dataset.key||(this.__handleSelection(s.dataset.key),!this.__isMultiple&&!this.__static&&(this.__close(),this.__resetInput()),this.$nextTick(()=>this.$refs.__input.focus({preventScroll:!0})))})),this.__optionsEl.addEventListener("mouseover",n(s=>{!s.dataset.key||this.__activate(s.dataset.key)})),this.__optionsEl.addEventListener("mousemove",n(s=>{this.__isActive(s.dataset.key||"")||!s.dataset.key||this.__activate(s.dataset.key)})),this.__optionsEl.addEventListener("mouseout",n(()=>{this.__keepActivated||this.__deactivate()})))})}}}function x(h){h.directive("combobox",(a,{value:o,modifiers:u},{Alpine:_,effect:c})=>{switch(o){case null:e(_,a,c);break;case"input":i(_,a);break;case"button":r(_,a);break;case"options":t(a);break;case"option":n(_,a);break;case"options-group":s(_,a);break;case"loading":d(_,a,u);break;case"separator":p(_,a);break;case"empty":l(_,a);break;default:console.error("invalid x-combobox value",o,"use input, button, option, options or leave mepty for root level instead");break}}).before("bind");function e(a,o,u){a.bind(o,{"x-data"(){return m({el:o,effect:u})}})}function i(a,o){a.bind(o,{"x-ref":"__input","x-model":"__searchQuery","x-bind:id"(){return this.$id("combobox-input")},role:"combobox",tabindex:"0","aria-autocomplete":"list","x-data"(){return v(a)}})}function t(a){h.bind(a,{"x-ref":"__options","x-bind:id"(){return this.$id("combobox-options")},role:"listbox","x-init"(){return this.$data.__static=h.extractProp(this.$el,"static",!1),h.bound(this.$el,"keepActivated")&&(this.__keepActivated=!0),this.$el.dataset.slot="options"},"x-show"(){return this.$data.__static?!0:this.$data.__isOpen}})}function n(a,o){a.bind(o,{"x-id"(){return["combobox-option"]},"x-bind:id"(){return this.$id("combobox-option")},role:"option","x-show"(){return this.$data.__isVisible(this.$el.dataset.key)},"x-data"(){return f(a,this.__nextId())}})}function s(a,o){a.bind(o,{"x-id"(){return["combobox-options-group"]},"x-bind:id"(){return this.$id("combobox-options-group")},role:"option","x-show"(){return!0}})}function r(a,o){a.bind(o,{"x-ref":"__button","x-bind:id"(){return this.$id("combobox-button")},tabindex:"-1","aria-haspopup":"true","x-on:click"(u){this.__isDisabled||(this.__isOpen?(this.__close(),this.__resetInput()):(u.preventDefault(),this.__open()),requestAnimationFrame(()=>this.$refs.__input.focus({preventScroll:!0})))}})}function l(a,o){a.bind(o,{"x-bind:id"(){return this.$id("combobox-button")},tabindex:"-1","aria-haspopup":"true","x-show"(){return Array.isArray(this.__filteredKeys)&&this.__filteredKeys.length===0}})}function d(a,o,u){let _=a.$data(o);u.filter(c=>c==="hide")}function p(a,o){a.bind(o,{})}}document.addEventListener("alpine:init",()=>{window.Alpine.plugin(x)});})(); diff --git a/dist/module.cjs.js b/dist/module.cjs.js index 1c7f549..382c85a 100644 --- a/dist/module.cjs.js +++ b/dist/module.cjs.js @@ -243,19 +243,16 @@ var ComboboxCollection = class { if (!this.needsReindex) return; this.navIndex = []; + console.log("called"); for (let i = 0; i < this.items.length; i++) { if (!((_a = this.items[i]) == null ? void 0 : _a.disabled)) { this.navIndex.push(i); } } - if (this.items.length >= this.searchThreshold) { - this.searchIndex = this.items.map((item) => ({ - key: item.key, - value: String(item.value).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "") - })); - } else { - this.searchIndex = []; - } + this.searchIndex = this.items.map((item) => ({ + key: item.key, + value: String(item.value).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "") + })); this.needsReindex = false; } search(query) { @@ -275,6 +272,7 @@ var ComboboxCollection = class { if (this.searchIndex) { const normalized = q.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); results = []; + console.log("inside search index", this.searchIndex); for (const {key, value} of this.searchIndex) { if (value.includes(normalized)) { const item = this.itemsMap.get(key); diff --git a/dist/module.esm.js b/dist/module.esm.js index fd47acc..a9c54b7 100644 --- a/dist/module.esm.js +++ b/dist/module.esm.js @@ -228,19 +228,16 @@ var ComboboxCollection = class { if (!this.needsReindex) return; this.navIndex = []; + console.log("called"); for (let i = 0; i < this.items.length; i++) { if (!this.items[i]?.disabled) { this.navIndex.push(i); } } - if (this.items.length >= this.searchThreshold) { - this.searchIndex = this.items.map((item) => ({ - key: item.key, - value: String(item.value).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "") - })); - } else { - this.searchIndex = []; - } + this.searchIndex = this.items.map((item) => ({ + key: item.key, + value: String(item.value).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "") + })); this.needsReindex = false; } search(query) { @@ -260,6 +257,7 @@ var ComboboxCollection = class { if (this.searchIndex) { const normalized = q.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); results = []; + console.log("inside search index", this.searchIndex); for (const {key, value} of this.searchIndex) { if (value.includes(normalized)) { const item = this.itemsMap.get(key); diff --git a/index.html b/index.html index 650ec68..a1b14e1 100644 --- a/index.html +++ b/index.html @@ -18,25 +18,27 @@ -
+
- +
- - - - -
    + + + +
    • tenjarine
    • diff --git a/scripts/build.ts b/scripts/build.ts deleted file mode 100644 index b9d47df..0000000 --- a/scripts/build.ts +++ /dev/null @@ -1,86 +0,0 @@ -import fs from 'fs' -import brotliSize from 'brotli-size' -import esbuild, { BuildOptions, BuildResult } from 'esbuild' - -(() => { - if (!fs.existsSync(`./dist`)) { - fs.mkdirSync(`./dist`, 0o744) - } - - const files: string[] = fs.readdirSync(`./builds`) - console.log(files) - - files.forEach(file => { - bundleFile(file) - }) -})() - -function bundleFile(file: string): void { - - const map: Record void> = { - 'cdn.js': () => { - build({ - entryPoints: [`builds/${file}`], - outfile: `dist/${file}`, - bundle: true, - platform: 'browser', - define: { CDN: 'true' }, - }) - build({ - entryPoints: [`builds/${file}`], - outfile: `dist/${file.replace('.js', '.min.js')}`, - bundle: true, - minify: true, - platform: 'browser', - define: { CDN: 'true' }, - }).then(() => { - outputSize('alpine-animation', `dist/${file.replace('.js', '.min.js')}`) - }) - }, - 'module.js': () => { - build({ - entryPoints: [`builds/${file}`], - outfile: `dist/${file.replace('.js', '.esm.js')}`, - bundle: true, - platform: 'neutral', - mainFields: ['main', 'module'], - }) - build({ - entryPoints: [`builds/${file}`], - outfile: `dist/${file.replace('.js', '.cjs.js')}`, - bundle: true, - target: ['node10.4'], - platform: 'node', - }) - } - } - - const fn = map[file] - if (fn) fn() - else console.error(`No build config for file: ${file}`) -} - -function build(options: BuildOptions): Promise { - options.define ||= {} - options.define['process.env.NODE_ENV'] = process.argv.includes('--production') ? `'production'` : `'development'` - - return esbuild.build({ - watch: process.argv.includes('--watch'), - ...options, - }).catch(() => process.exit(1)) -} - -async function outputSize(pkg: string, file: string): Promise { - const data = fs.readFileSync(file) - const size = await brotliSize(data) - console.log('\x1b[32m', `${pkg}: ${bytesToSize(size)}`) -} - - -function bytesToSize(bytes: number): string { - const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'] - if (bytes === 0) return 'n/a' - const i = Math.floor(Math.log(bytes) / Math.log(1024)) - if (i === 0) return `${bytes} ${sizes[i]}` - return `${(bytes / 1024 ** i).toFixed(1)} ${sizes[i]}` -} diff --git a/src/core/ComboboxCollection.ts b/src/core/ComboboxCollection.ts index 1aa1d5d..7abed65 100644 --- a/src/core/ComboboxCollection.ts +++ b/src/core/ComboboxCollection.ts @@ -20,7 +20,6 @@ export default class ComboboxCollection { public pending: Pending; - // used to track the reactivity system on the component proxy public activeIndex: ActiveIndex; public searchThreshold: number; @@ -90,23 +89,21 @@ export default class ComboboxCollection { } deactivate() { - this.activeIndex.value = undefined - this.activeNavPos = -1 + this.activeIndex.value = undefined; + this.activeNavPos = -1; } isActivated(key: string) { - const item = this.get(key) + const item = this.get(key); - if (!item) return false + if (!item) return false; - return this.items.indexOf(item) === this.activeIndex.value + return this.items.indexOf(item) === this.activeIndex.value; } getActiveItem() { - return this.activeIndex.value === undefined - ? null - : this.items[this.activeIndex.value] + return this.activeIndex.value === undefined ? null : this.items[this.activeIndex.value] } /* ---------------------------------------- @@ -177,10 +174,10 @@ export default class ComboboxCollection { * ------------------------------------- */ private invalidate() { - this.needsReindex = true - this.lastQuery = '' - this.lastResults = [] - this.scheduleBatch() + this.needsReindex = true; + this.lastQuery = ''; + this.lastResults = []; + this.scheduleBatch(); } private scheduleBatch() { @@ -205,26 +202,18 @@ export default class ComboboxCollection { this.navIndex = []; + console.log('called'); + for (let i = 0; i < this.items.length; i++) { if (!this.items[i]?.disabled) { this.navIndex.push(i) } } - if (this.items.length >= this.searchThreshold) { - - this.searchIndex = this.items.map(item => ({ - key: item.key, - value: String(item.value) - .toLowerCase() - .normalize('NFD') - .replace(/[\u0300-\u036f]/g, '') - })) - } else { - - this.searchIndex = [] - - } + this.searchIndex = this.items.map(item => ({ + key: item.key, + value: String(item.value).toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '') + })) this.needsReindex = false } @@ -234,6 +223,7 @@ export default class ComboboxCollection { * ------------------------------------- */ search(query: string): Item[] { + if (!query) { this.lastQuery = '' this.lastResults = [] @@ -242,9 +232,7 @@ export default class ComboboxCollection { const q = query.toLowerCase() - if (this.lastQuery && - q.startsWith(this.lastQuery) && - this.lastResults) { + if (this.lastQuery && q.startsWith(this.lastQuery) && this.lastResults) { const filtered = this.lastResults.filter(item => String(item.value).toLowerCase().includes(q) @@ -258,12 +246,10 @@ export default class ComboboxCollection { let results: Item[]; if (this.searchIndex) { - const normalized = q - .normalize('NFD') - .replace(/[\u0300-\u036f]/g, '') + const normalized = q.normalize('NFD').replace(/[\u0300-\u036f]/g, ''); results = []; - + console.log('inside search index', this.searchIndex); for (const { key, value } of this.searchIndex) { if (value.includes(normalized)) { diff --git a/src/factories/CreateComboboxRoot.ts b/src/factories/CreateComboboxRoot.ts index 480a552..05f94d4 100644 --- a/src/factories/CreateComboboxRoot.ts +++ b/src/factories/CreateComboboxRoot.ts @@ -76,6 +76,7 @@ export default function CreateComboboxRoot( let results = collection.search(this.__searchQuery).map((result) => result.key); + if (results.length >= 0) { this.__filteredKeys = results } else { From 0fb60510e116c26128ddcff2dcafb8958afe7f97 Mon Sep 17 00:00:00 2001 From: charrafimed Date: Wed, 7 Jan 2026 15:35:51 +0100 Subject: [PATCH 2/5] wip --- dist/cdn.js | 1 - dist/cdn.min.js | 2 +- dist/module.cjs.js | 1 - dist/module.esm.js | 1 - src/core/ComboboxCollection.ts | 186 +++++++++++++-------------- src/factories/CreateComboboxInput.ts | 3 +- src/index.ts | 2 +- 7 files changed, 95 insertions(+), 101 deletions(-) diff --git a/dist/cdn.js b/dist/cdn.js index 9bc06e8..bd19463 100644 --- a/dist/cdn.js +++ b/dist/cdn.js @@ -229,7 +229,6 @@ if (!this.needsReindex) return; this.navIndex = []; - console.log("called"); for (let i = 0; i < this.items.length; i++) { if (!this.items[i]?.disabled) { this.navIndex.push(i); diff --git a/dist/cdn.min.js b/dist/cdn.min.js index a4b8002..bd067e2 100644 --- a/dist/cdn.min.js +++ b/dist/cdn.min.js @@ -1 +1 @@ -(()=>{function v(h){return{init(){let e=h.extractProp(this.$el,"display-value","");e&&(this.__displayValue=e),this.__handleEvents()},__handleEvents(){this.$el.addEventListener("focus",()=>{this.__startTyping()}),this.$el.addEventListener("input",e=>{e.stopPropagation(),this.__isTyping&&this.__open()}),this.$el.addEventListener("blur",()=>{this.__stopTyping()}),this.$el.addEventListener("keydown",e=>{switch(e.key){case"ArrowDown":if(e.preventDefault(),e.stopPropagation(),!this.__isOpen){this.__open();break}this.__activateNext();break;case"ArrowUp":if(e.preventDefault(),e.stopPropagation(),!this.__isOpen){this.__open();break}this.__activatePrev();break;case"Enter":e.preventDefault(),e.stopPropagation(),this.__selectActive(),this.__stopTyping(),this.__isMultiple||(this.__close(),this.__resetInput());break}})}}}function f(h,e){let i="option";return{__uniqueKey:"option-"+e,init(){this.$el.dataset.slot=i;let t=h.extractProp(this.$el,"value","");this.$el.dataset.key=this.__uniqueKey,this.$el.dataset.value=t;let n=h.extractProp(this.$el,"disabled",!1,!1);this.__add(this.__uniqueKey,t,n),this.$watch("__activedKey",s=>{s===this.__uniqueKey?this.$el.setAttribute("data-active","true"):this.$el.removeAttribute("data-active")}),this.$watch("__selectedKeys",s=>{let r=!1;this.__isMultiple?r=Array.isArray(s)&&s.includes(this.__uniqueKey):r=s===this.__uniqueKey,r?(this.$el.setAttribute("aria-selected","true"),this.$el.setAttribute("data-selected","true")):(this.$el.setAttribute("aria-selected","false"),this.$el.removeAttribute("data-selected"))}),this.$nextTick(()=>{n&&this.$el.setAttribute("tabindex","-1")})},destroy(){this.__forget(this.__uniqueKey)}}}var b=class{constructor(e={}){this.items=[];this.itemsMap=new Map;this.activeNavPos=-1;this.needsReindex=!1;this.navIndex=[];this.lastQuery="";this.isProcessing=!1;this.pending=Alpine.reactive({state:!1}),this.activeIndex=Alpine.reactive({value:void 0}),this.searchThreshold=e.searchThreshold??500}add(e,i,t=!1){if(this.itemsMap.has(e))return;let n={key:e,value:i,disabled:t};this.items.push(n),this.itemsMap.set(e,n),this.invalidate()}forget(e){let i=this.itemsMap.get(e);if(!i)return;let t=this.items.indexOf(i);this.itemsMap.delete(e),this.items.splice(t,1),this.activeIndex.value===t?(this.activeIndex.value=void 0,this.activeNavPos=-1):this.activeIndex.value&&this.activeIndex.value>t&&this.activeIndex.value--,this.invalidate()}activate(e){let i=this.get(e);if(!i||i.disabled)return;this.rebuildIndexes();let t=this.items.indexOf(i);this.activeIndex.value!==t&&(this.activeIndex.value=t,this.activeNavPos=this.navIndex.indexOf(t))}deactivate(){this.activeIndex.value=void 0,this.activeNavPos=-1}isActivated(e){let i=this.get(e);return i?this.items.indexOf(i)===this.activeIndex.value:!1}getActiveItem(){return this.activeIndex.value===void 0?null:this.items[this.activeIndex.value]}activateFirst(){this.rebuildIndexes(),!!this.navIndex.length&&(this.navIndex[0]&&(this.activeIndex.value=this.navIndex[0]),this.activeNavPos=0)}activateLast(){if(this.rebuildIndexes(),!this.navIndex.length)return;this.activeNavPos=this.navIndex.length-1;let e=this.navIndex[this.activeNavPos];typeof e=="number"&&(this.activeIndex.value=e)}activateNext(){if(this.rebuildIndexes(),!!this.navIndex.length){if(this.activeNavPos===-1){this.activateFirst();return}this.activeNavPos=(this.activeNavPos+1)%this.navIndex.length,this.activeIndex.value=this.navIndex[this.activeNavPos]}}activatePrev(){if(this.rebuildIndexes(),!!this.navIndex.length){if(this.activeNavPos===-1){this.activateLast();return}this.activeNavPos=this.activeNavPos===0?this.navIndex.length-1:this.activeNavPos-1,this.activeIndex.value=this.navIndex[this.activeNavPos]}}invalidate(){this.needsReindex=!0,this.lastQuery="",this.lastResults=[],this.scheduleBatch()}scheduleBatch(){this.isProcessing||(this.isProcessing=!0,this.pending.state=!0,queueMicrotask(()=>{this.rebuildIndexes(),this.isProcessing=!1,this.pending.state=!1}))}toggleIsPending(){this.pending.state=!this.pending.state}rebuildIndexes(){if(!!this.needsReindex){this.navIndex=[],console.log("called");for(let e=0;e({key:e.key,value:String(e.value).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"")})),this.needsReindex=!1}}search(e){if(!e)return this.lastQuery="",this.lastResults=[],this.items;let i=e.toLowerCase();if(this.lastQuery&&i.startsWith(this.lastQuery)&&this.lastResults){let n=this.lastResults.filter(s=>String(s.value).toLowerCase().includes(i));return this.lastQuery=i,this.lastResults=n,n}let t;if(this.searchIndex){let n=i.normalize("NFD").replace(/[\u0300-\u036f]/g,"");t=[],console.log("inside search index",this.searchIndex);for(let{key:s,value:r}of this.searchIndex)if(r.includes(n)){let l=this.itemsMap.get(s);l&&t.push(l)}}else t=this.items.filter(n=>String(n.value).toLowerCase().includes(i));return this.lastQuery=i,this.lastResults=t,t}get(e){return this.itemsMap.get(e)}getValueByKey(e){return this.get(e)?.value}getKeyByIndex(e){return e==null?null:this.items[e]?.key??null}all(){return this.items}get size(){return this.items.length}},y=b;function m({el:h,effect:e}){let i=new y;return{__state:null,__isOpen:!1,__isMultiple:!1,__isTyping:!1,__isLoading:!1,__uid:0,__static:!1,__keepActivated:!0,__optionsEl:void 0,__compareBy:void 0,__activedKey:void 0,__selectedKeys:void 0,__filteredKeys:null,__isDisabled:!1,__searchQuery:"",__add:(t,n,s)=>i.add(t,n,s),__forget:t=>i.forget(t),__activate:t=>i.activate(t),__isActive:t=>i.isActivated(t),__deactivate:()=>i.deactivate(),__getValueByKey:t=>i.getValueByKey(t),__activateNext:()=>i.activateNext(),__activatePrev:()=>i.activatePrev(),__activateFirst:()=>i.activateFirst(),__activateLast:()=>i.activateLast(),__isVisible(t){return this.__searchQuery===""||!this.__filteredKeys?!0:this.__filteredKeys.includes(t)},init(){e(()=>{this.__isLoading=i.pending.state}),e(()=>{this.__activedKey=i.getKeyByIndex(i.activeIndex.value)}),e(()=>{let s=i.search(this.__searchQuery).map(r=>r.key);s.length>=0?this.__filteredKeys=s:this.__filteredKeys=null,this.__activedKey&&this.__filteredKeys&&!this.__filteredKeys.includes(this.__activedKey)&&i.deactivate(),this.__isOpen&&!i.getActiveItem()&&this.__filteredKeys&&this.__filteredKeys.length&&i.activate(this.__filteredKeys[0])}),this.__isMultiple?this.__selectedKeys=[]:this.__selectedKeys=null,this.__isMultiple=Alpine.extractProp(h,"multiple",!1),this.__isDisabled=Alpine.extractProp(h,"disabled",!1),this.__compareBy=Alpine.extractProp(h,"by","");let t=this.__isMultiple?[]:"",n=Alpine.extractProp(h,"initial-value",t);this.__state=n,this.__registerEventsDelector()},__open(){if(this.__isOpen)return;this.__isOpen=!0;let t=this.$refs.__input;requestAnimationFrame(()=>{t.focus({preventScroll:!0}),this.__activateSelectedOrFirst()})},__activateSelectedOrFirst(t=!0){if(!(!this.__isOpen||i.getActiveItem())){if(t&&this.__selectedKeys){let s=this.__isMultiple?this.__selectedKeys[0]:this.__selectedKeys;if(s){this.__activate(s);return}}i.activateFirst()}},__close(){this.__isOpen=!1,this.__deactivate()},__handleSelection(t){let n=this.__getValueByKey(t);if(!this.__isMultiple){this.__selectedKeys=t,this.__state===n?(this.__state=null,this.__selectedKeys=null):this.__state=n,this.__static||this.__close();return}Array.isArray(this.__selectedKeys)||(this.__selectedKeys=[]),Array.isArray(this.__state)||(this.__state=[]);let s=this.__state.findIndex(l=>this.__compare(l,n)),r=this.__selectedKeys.indexOf(t);s===-1?(this.__state.push(n),this.__selectedKeys.push(t)):(this.__state.splice(s,1),this.__selectedKeys.splice(r,1))},__selectActive(){!this.__activedKey||this.__handleSelection(this.__activedKey)},__startTyping(){this.__isTyping=!0},__stopTyping(){this.__isTyping=!1},__resetInput(){let t=this.$refs.__input;if(!t)return;let n=this.__getCurrentValue();t.value=n},__getCurrentValue(){return!this.$refs.__input||!this.__state?"":typeof this.__state=="string"?this.__state:""},__compare(t,n){let s=this.__compareBy;if(!this.__compareBy)s=(r,l)=>Alpine.raw(r)===Alpine.raw(l);else if(typeof this.__compareBy=="string"){let r=this.__compareBy;s=(l,d)=>{if(!l||typeof l!="object"||!d||typeof d!="object")return Alpine.raw(l)===Alpine.raw(d);let p=l,a=d;return p[r]===a[r]}}return s(t,n)},__nextId(){return++this.__uid},__registerEventsDelector(){let t=s=>Alpine.findClosest(s,r=>r.dataset.slot==="option"),n=s=>function(r){if(r.stopPropagation(),!(r.target instanceof Element))return;let l=t(r.target);!l||s(l)};this.$nextTick(()=>{this.__optionsEl=this.$refs.__options,!!this.__optionsEl&&(this.__optionsEl.addEventListener("click",n(s=>{!s.dataset.key||(this.__handleSelection(s.dataset.key),!this.__isMultiple&&!this.__static&&(this.__close(),this.__resetInput()),this.$nextTick(()=>this.$refs.__input.focus({preventScroll:!0})))})),this.__optionsEl.addEventListener("mouseover",n(s=>{!s.dataset.key||this.__activate(s.dataset.key)})),this.__optionsEl.addEventListener("mousemove",n(s=>{this.__isActive(s.dataset.key||"")||!s.dataset.key||this.__activate(s.dataset.key)})),this.__optionsEl.addEventListener("mouseout",n(()=>{this.__keepActivated||this.__deactivate()})))})}}}function x(h){h.directive("combobox",(a,{value:o,modifiers:u},{Alpine:_,effect:c})=>{switch(o){case null:e(_,a,c);break;case"input":i(_,a);break;case"button":r(_,a);break;case"options":t(a);break;case"option":n(_,a);break;case"options-group":s(_,a);break;case"loading":d(_,a,u);break;case"separator":p(_,a);break;case"empty":l(_,a);break;default:console.error("invalid x-combobox value",o,"use input, button, option, options or leave mepty for root level instead");break}}).before("bind");function e(a,o,u){a.bind(o,{"x-data"(){return m({el:o,effect:u})}})}function i(a,o){a.bind(o,{"x-ref":"__input","x-model":"__searchQuery","x-bind:id"(){return this.$id("combobox-input")},role:"combobox",tabindex:"0","aria-autocomplete":"list","x-data"(){return v(a)}})}function t(a){h.bind(a,{"x-ref":"__options","x-bind:id"(){return this.$id("combobox-options")},role:"listbox","x-init"(){return this.$data.__static=h.extractProp(this.$el,"static",!1),h.bound(this.$el,"keepActivated")&&(this.__keepActivated=!0),this.$el.dataset.slot="options"},"x-show"(){return this.$data.__static?!0:this.$data.__isOpen}})}function n(a,o){a.bind(o,{"x-id"(){return["combobox-option"]},"x-bind:id"(){return this.$id("combobox-option")},role:"option","x-show"(){return this.$data.__isVisible(this.$el.dataset.key)},"x-data"(){return f(a,this.__nextId())}})}function s(a,o){a.bind(o,{"x-id"(){return["combobox-options-group"]},"x-bind:id"(){return this.$id("combobox-options-group")},role:"option","x-show"(){return!0}})}function r(a,o){a.bind(o,{"x-ref":"__button","x-bind:id"(){return this.$id("combobox-button")},tabindex:"-1","aria-haspopup":"true","x-on:click"(u){this.__isDisabled||(this.__isOpen?(this.__close(),this.__resetInput()):(u.preventDefault(),this.__open()),requestAnimationFrame(()=>this.$refs.__input.focus({preventScroll:!0})))}})}function l(a,o){a.bind(o,{"x-bind:id"(){return this.$id("combobox-button")},tabindex:"-1","aria-haspopup":"true","x-show"(){return Array.isArray(this.__filteredKeys)&&this.__filteredKeys.length===0}})}function d(a,o,u){let _=a.$data(o);u.filter(c=>c==="hide")}function p(a,o){a.bind(o,{})}}document.addEventListener("alpine:init",()=>{window.Alpine.plugin(x)});})(); +(()=>{function v(h){return{init(){let t=h.extractProp(this.$el,"display-value","");t&&(this.__displayValue=t),this.__handleEvents()},__handleEvents(){this.$el.addEventListener("focus",()=>{this.__startTyping()}),this.$el.addEventListener("input",t=>{t.stopPropagation(),this.__isTyping&&this.__open()}),this.$el.addEventListener("blur",()=>{this.__stopTyping()}),this.$el.addEventListener("keydown",t=>{switch(t.key){case"ArrowDown":if(t.preventDefault(),t.stopPropagation(),!this.__isOpen){this.__open();break}this.__activateNext();break;case"ArrowUp":if(t.preventDefault(),t.stopPropagation(),!this.__isOpen){this.__open();break}this.__activatePrev();break;case"Enter":t.preventDefault(),t.stopPropagation(),this.__selectActive(),this.__stopTyping(),this.__isMultiple||(this.__close(),this.__resetInput());break}})}}}function f(h,t){let i="option";return{__uniqueKey:"option-"+t,init(){this.$el.dataset.slot=i;let e=h.extractProp(this.$el,"value","");this.$el.dataset.key=this.__uniqueKey,this.$el.dataset.value=e;let n=h.extractProp(this.$el,"disabled",!1,!1);this.__add(this.__uniqueKey,e,n),this.$watch("__activedKey",s=>{s===this.__uniqueKey?this.$el.setAttribute("data-active","true"):this.$el.removeAttribute("data-active")}),this.$watch("__selectedKeys",s=>{let r=!1;this.__isMultiple?r=Array.isArray(s)&&s.includes(this.__uniqueKey):r=s===this.__uniqueKey,r?(this.$el.setAttribute("aria-selected","true"),this.$el.setAttribute("data-selected","true")):(this.$el.setAttribute("aria-selected","false"),this.$el.removeAttribute("data-selected"))}),this.$nextTick(()=>{n&&this.$el.setAttribute("tabindex","-1")})},destroy(){this.__forget(this.__uniqueKey)}}}var b=class{constructor(t={}){this.items=[];this.itemsMap=new Map;this.activeNavPos=-1;this.needsReindex=!1;this.navIndex=[];this.lastQuery="";this.isProcessing=!1;this.pending=Alpine.reactive({state:!1}),this.activeIndex=Alpine.reactive({value:void 0}),this.searchThreshold=t.searchThreshold??500}add(t,i,e=!1){if(this.itemsMap.has(t))return;let n={key:t,value:i,disabled:e};this.items.push(n),this.itemsMap.set(t,n),this.invalidate()}forget(t){let i=this.itemsMap.get(t);if(!i)return;let e=this.items.indexOf(i);this.itemsMap.delete(t),this.items.splice(e,1),this.activeIndex.value===e?(this.activeIndex.value=void 0,this.activeNavPos=-1):this.activeIndex.value&&this.activeIndex.value>e&&this.activeIndex.value--,this.invalidate()}activate(t){let i=this.get(t);if(!i||i.disabled)return;this.rebuildIndexes();let e=this.items.indexOf(i);this.activeIndex.value!==e&&(this.activeIndex.value=e,this.activeNavPos=this.navIndex.indexOf(e))}deactivate(){this.activeIndex.value=void 0,this.activeNavPos=-1}isActivated(t){let i=this.get(t);return i?this.items.indexOf(i)===this.activeIndex.value:!1}getActiveItem(){return this.activeIndex.value===void 0?null:this.items[this.activeIndex.value]}activateFirst(){this.rebuildIndexes(),!!this.navIndex.length&&(this.navIndex[0]&&(this.activeIndex.value=this.navIndex[0]),this.activeNavPos=0)}activateLast(){if(this.rebuildIndexes(),!this.navIndex.length)return;this.activeNavPos=this.navIndex.length-1;let t=this.navIndex[this.activeNavPos];typeof t=="number"&&(this.activeIndex.value=t)}activateNext(){if(this.rebuildIndexes(),!!this.navIndex.length){if(this.activeNavPos===-1){this.activateFirst();return}this.activeNavPos=(this.activeNavPos+1)%this.navIndex.length,this.activeIndex.value=this.navIndex[this.activeNavPos]}}activatePrev(){if(this.rebuildIndexes(),!!this.navIndex.length){if(this.activeNavPos===-1){this.activateLast();return}this.activeNavPos=this.activeNavPos===0?this.navIndex.length-1:this.activeNavPos-1,this.activeIndex.value=this.navIndex[this.activeNavPos]}}invalidate(){this.needsReindex=!0,this.lastQuery="",this.lastResults=[],this.scheduleBatch()}scheduleBatch(){this.isProcessing||(this.isProcessing=!0,this.pending.state=!0,queueMicrotask(()=>{this.rebuildIndexes(),this.isProcessing=!1,this.pending.state=!1}))}toggleIsPending(){this.pending.state=!this.pending.state}rebuildIndexes(){if(!!this.needsReindex){this.navIndex=[];for(let t=0;t({key:t.key,value:String(t.value).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"")})),this.needsReindex=!1}}search(t){if(!t)return this.lastQuery="",this.lastResults=[],this.items;let i=t.toLowerCase();if(this.lastQuery&&i.startsWith(this.lastQuery)&&this.lastResults){let n=this.lastResults.filter(s=>String(s.value).toLowerCase().includes(i));return this.lastQuery=i,this.lastResults=n,n}let e;if(this.searchIndex){let n=i.normalize("NFD").replace(/[\u0300-\u036f]/g,"");e=[],console.log("inside search index",this.searchIndex);for(let{key:s,value:r}of this.searchIndex)if(r.includes(n)){let l=this.itemsMap.get(s);l&&e.push(l)}}else e=this.items.filter(n=>String(n.value).toLowerCase().includes(i));return this.lastQuery=i,this.lastResults=e,e}get(t){return this.itemsMap.get(t)}getValueByKey(t){return this.get(t)?.value}getKeyByIndex(t){return t==null?null:this.items[t]?.key??null}all(){return this.items}get size(){return this.items.length}},y=b;function m({el:h,effect:t}){let i=new y;return{__state:null,__isOpen:!1,__isMultiple:!1,__isTyping:!1,__isLoading:!1,__uid:0,__static:!1,__keepActivated:!0,__optionsEl:void 0,__compareBy:void 0,__activedKey:void 0,__selectedKeys:void 0,__filteredKeys:null,__isDisabled:!1,__searchQuery:"",__add:(e,n,s)=>i.add(e,n,s),__forget:e=>i.forget(e),__activate:e=>i.activate(e),__isActive:e=>i.isActivated(e),__deactivate:()=>i.deactivate(),__getValueByKey:e=>i.getValueByKey(e),__activateNext:()=>i.activateNext(),__activatePrev:()=>i.activatePrev(),__activateFirst:()=>i.activateFirst(),__activateLast:()=>i.activateLast(),__isVisible(e){return this.__searchQuery===""||!this.__filteredKeys?!0:this.__filteredKeys.includes(e)},init(){t(()=>{this.__isLoading=i.pending.state}),t(()=>{this.__activedKey=i.getKeyByIndex(i.activeIndex.value)}),t(()=>{let s=i.search(this.__searchQuery).map(r=>r.key);s.length>=0?this.__filteredKeys=s:this.__filteredKeys=null,this.__activedKey&&this.__filteredKeys&&!this.__filteredKeys.includes(this.__activedKey)&&i.deactivate(),this.__isOpen&&!i.getActiveItem()&&this.__filteredKeys&&this.__filteredKeys.length&&i.activate(this.__filteredKeys[0])}),this.__isMultiple?this.__selectedKeys=[]:this.__selectedKeys=null,this.__isMultiple=Alpine.extractProp(h,"multiple",!1),this.__isDisabled=Alpine.extractProp(h,"disabled",!1),this.__compareBy=Alpine.extractProp(h,"by","");let e=this.__isMultiple?[]:"",n=Alpine.extractProp(h,"initial-value",e);this.__state=n,this.__registerEventsDelector()},__open(){if(this.__isOpen)return;this.__isOpen=!0;let e=this.$refs.__input;requestAnimationFrame(()=>{e.focus({preventScroll:!0}),this.__activateSelectedOrFirst()})},__activateSelectedOrFirst(e=!0){if(!(!this.__isOpen||i.getActiveItem())){if(e&&this.__selectedKeys){let s=this.__isMultiple?this.__selectedKeys[0]:this.__selectedKeys;if(s){this.__activate(s);return}}i.activateFirst()}},__close(){this.__isOpen=!1,this.__deactivate()},__handleSelection(e){let n=this.__getValueByKey(e);if(!this.__isMultiple){this.__selectedKeys=e,this.__state===n?(this.__state=null,this.__selectedKeys=null):this.__state=n,this.__static||this.__close();return}Array.isArray(this.__selectedKeys)||(this.__selectedKeys=[]),Array.isArray(this.__state)||(this.__state=[]);let s=this.__state.findIndex(l=>this.__compare(l,n)),r=this.__selectedKeys.indexOf(e);s===-1?(this.__state.push(n),this.__selectedKeys.push(e)):(this.__state.splice(s,1),this.__selectedKeys.splice(r,1))},__selectActive(){!this.__activedKey||this.__handleSelection(this.__activedKey)},__startTyping(){this.__isTyping=!0},__stopTyping(){this.__isTyping=!1},__resetInput(){let e=this.$refs.__input;if(!e)return;let n=this.__getCurrentValue();e.value=n},__getCurrentValue(){return!this.$refs.__input||!this.__state?"":typeof this.__state=="string"?this.__state:""},__compare(e,n){let s=this.__compareBy;if(!this.__compareBy)s=(r,l)=>Alpine.raw(r)===Alpine.raw(l);else if(typeof this.__compareBy=="string"){let r=this.__compareBy;s=(l,d)=>{if(!l||typeof l!="object"||!d||typeof d!="object")return Alpine.raw(l)===Alpine.raw(d);let p=l,a=d;return p[r]===a[r]}}return s(e,n)},__nextId(){return++this.__uid},__registerEventsDelector(){let e=s=>Alpine.findClosest(s,r=>r.dataset.slot==="option"),n=s=>function(r){if(r.stopPropagation(),!(r.target instanceof Element))return;let l=e(r.target);!l||s(l)};this.$nextTick(()=>{this.__optionsEl=this.$refs.__options,!!this.__optionsEl&&(this.__optionsEl.addEventListener("click",n(s=>{!s.dataset.key||(this.__handleSelection(s.dataset.key),!this.__isMultiple&&!this.__static&&(this.__close(),this.__resetInput()),this.$nextTick(()=>this.$refs.__input.focus({preventScroll:!0})))})),this.__optionsEl.addEventListener("mouseover",n(s=>{!s.dataset.key||this.__activate(s.dataset.key)})),this.__optionsEl.addEventListener("mousemove",n(s=>{this.__isActive(s.dataset.key||"")||!s.dataset.key||this.__activate(s.dataset.key)})),this.__optionsEl.addEventListener("mouseout",n(()=>{this.__keepActivated||this.__deactivate()})))})}}}function x(h){h.directive("combobox",(a,{value:o,modifiers:u},{Alpine:_,effect:c})=>{switch(o){case null:t(_,a,c);break;case"input":i(_,a);break;case"button":r(_,a);break;case"options":e(a);break;case"option":n(_,a);break;case"options-group":s(_,a);break;case"loading":d(_,a,u);break;case"separator":p(_,a);break;case"empty":l(_,a);break;default:console.error("invalid x-combobox value",o,"use input, button, option, options or leave mepty for root level instead");break}}).before("bind");function t(a,o,u){a.bind(o,{"x-data"(){return m({el:o,effect:u})}})}function i(a,o){a.bind(o,{"x-ref":"__input","x-model":"__searchQuery","x-bind:id"(){return this.$id("combobox-input")},role:"combobox",tabindex:"0","aria-autocomplete":"list","x-data"(){return v(a)}})}function e(a){h.bind(a,{"x-ref":"__options","x-bind:id"(){return this.$id("combobox-options")},role:"listbox","x-init"(){return this.$data.__static=h.extractProp(this.$el,"static",!1),h.bound(this.$el,"keepActivated")&&(this.__keepActivated=!0),this.$el.dataset.slot="options"},"x-show"(){return this.$data.__static?!0:this.$data.__isOpen}})}function n(a,o){a.bind(o,{"x-id"(){return["combobox-option"]},"x-bind:id"(){return this.$id("combobox-option")},role:"option","x-show"(){return this.$data.__isVisible(this.$el.dataset.key)},"x-data"(){return f(a,this.__nextId())}})}function s(a,o){a.bind(o,{"x-id"(){return["combobox-options-group"]},"x-bind:id"(){return this.$id("combobox-options-group")},role:"option","x-show"(){return!0}})}function r(a,o){a.bind(o,{"x-ref":"__button","x-bind:id"(){return this.$id("combobox-button")},tabindex:"-1","aria-haspopup":"true","x-on:click"(u){this.__isDisabled||(this.__isOpen?(this.__close(),this.__resetInput()):(u.preventDefault(),this.__open()),requestAnimationFrame(()=>this.$refs.__input.focus({preventScroll:!0})))}})}function l(a,o){a.bind(o,{"x-bind:id"(){return this.$id("combobox-button")},tabindex:"-1","aria-haspopup":"true","x-show"(){return Array.isArray(this.__filteredKeys)&&this.__filteredKeys.length===0}})}function d(a,o,u){let _=a.$data(o);u.filter(c=>c==="hide")}function p(a,o){a.bind(o,{})}}document.addEventListener("alpine:init",()=>{window.Alpine.plugin(x)});})(); diff --git a/dist/module.cjs.js b/dist/module.cjs.js index 382c85a..c8489bc 100644 --- a/dist/module.cjs.js +++ b/dist/module.cjs.js @@ -243,7 +243,6 @@ var ComboboxCollection = class { if (!this.needsReindex) return; this.navIndex = []; - console.log("called"); for (let i = 0; i < this.items.length; i++) { if (!((_a = this.items[i]) == null ? void 0 : _a.disabled)) { this.navIndex.push(i); diff --git a/dist/module.esm.js b/dist/module.esm.js index a9c54b7..7da312f 100644 --- a/dist/module.esm.js +++ b/dist/module.esm.js @@ -228,7 +228,6 @@ var ComboboxCollection = class { if (!this.needsReindex) return; this.navIndex = []; - console.log("called"); for (let i = 0; i < this.items.length; i++) { if (!this.items[i]?.disabled) { this.navIndex.push(i); diff --git a/src/core/ComboboxCollection.ts b/src/core/ComboboxCollection.ts index 7abed65..fb562a1 100644 --- a/src/core/ComboboxCollection.ts +++ b/src/core/ComboboxCollection.ts @@ -58,8 +58,10 @@ export default class ComboboxCollection { this.items.splice(index, 1); if (this.activeIndex.value === index) { + this.activeIndex.value = undefined; this.activeNavPos = -1; + } else if (this.activeIndex.value && this.activeIndex.value > index) { this.activeIndex.value--; } @@ -106,68 +108,7 @@ export default class ComboboxCollection { return this.activeIndex.value === undefined ? null : this.items[this.activeIndex.value] } - /* ---------------------------------------- - * Keyboard navigation - * ------------------------------------- */ - - activateFirst() { - this.rebuildIndexes() - - if (!this.navIndex.length) return; - - if (this.navIndex[0]) { - this.activeIndex.value = this.navIndex[0]; - } - - this.activeNavPos = 0; - } - - activateLast() { - this.rebuildIndexes() - - if (!this.navIndex.length) return - - this.activeNavPos = this.navIndex.length - 1 - - const activeIndex = this.navIndex[this.activeNavPos]; - - if (typeof activeIndex === 'number') { - this.activeIndex.value = activeIndex; - } - } - - activateNext() { - - this.rebuildIndexes(); - - if (!this.navIndex.length) return; - - if (this.activeNavPos === -1) { - this.activateFirst(); - return; - } - - this.activeNavPos = (this.activeNavPos + 1) % this.navIndex.length - - this.activeIndex.value = this.navIndex[this.activeNavPos] - } - - activatePrev() { - this.rebuildIndexes() - if (!this.navIndex.length) return - - if (this.activeNavPos === -1) { - this.activateLast() - return - } - - this.activeNavPos = - this.activeNavPos === 0 - ? this.navIndex.length - 1 - : this.activeNavPos - 1 - this.activeIndex.value = this.navIndex[this.activeNavPos] - } /* ---------------------------------------- * Indexing @@ -181,41 +122,39 @@ export default class ComboboxCollection { } private scheduleBatch() { - if (this.isProcessing) return + if (this.isProcessing) return; - this.isProcessing = true - this.pending.state = true + this.isProcessing = true; + this.pending.state = true; queueMicrotask(() => { - this.rebuildIndexes() - this.isProcessing = false - this.pending.state = false + this.rebuildIndexes(); + this.isProcessing = false; + this.pending.state = false; }) } - toggleIsPending() { + public toggleIsPending() { this.pending.state = !this.pending.state; } private rebuildIndexes() { - if (!this.needsReindex) return + if (!this.needsReindex) return; this.navIndex = []; - console.log('called'); - for (let i = 0; i < this.items.length; i++) { if (!this.items[i]?.disabled) { - this.navIndex.push(i) + this.navIndex.push(i); } } - this.searchIndex = this.items.map(item => ({ + this.searchIndex = this.items.map((item) => ({ key: item.key, value: String(item.value).toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '') - })) + })); - this.needsReindex = false + this.needsReindex = false; } /* ---------------------------------------- @@ -225,22 +164,20 @@ export default class ComboboxCollection { search(query: string): Item[] { if (!query) { - this.lastQuery = '' - this.lastResults = [] - return this.items + this.lastQuery = ''; + this.lastResults = []; + return this.items; } - const q = query.toLowerCase() + const q = query.toLowerCase(); if (this.lastQuery && q.startsWith(this.lastQuery) && this.lastResults) { - const filtered = this.lastResults.filter(item => - String(item.value).toLowerCase().includes(q) - ) + const filtered = this.lastResults.filter(item => String(item.value).toLowerCase().includes(q)); - this.lastQuery = q - this.lastResults = filtered - return filtered + this.lastQuery = q; + this.lastResults = filtered; + return filtered; } let results: Item[]; @@ -249,20 +186,16 @@ export default class ComboboxCollection { const normalized = q.normalize('NFD').replace(/[\u0300-\u036f]/g, ''); results = []; - console.log('inside search index', this.searchIndex); + for (const { key, value } of this.searchIndex) { if (value.includes(normalized)) { - const item = this.itemsMap.get(key); - if (item) results.push(item); } } } else { - results = this.items.filter(item => - String(item.value).toLowerCase().includes(q) - ); + results = this.items.filter(item => String(item.value).toLowerCase().includes(q)); } this.lastQuery = q; @@ -277,7 +210,7 @@ export default class ComboboxCollection { * ------------------------------------- */ get(key: string): Item | undefined { - return this.itemsMap.get(key) + return this.itemsMap.get(key); } getValueByKey(key: string): Item['value'] | undefined { @@ -286,14 +219,77 @@ export default class ComboboxCollection { } getKeyByIndex(index: number | null | undefined) { - return index == null ? null : this.items[index]?.key ?? null + return index == null ? null : this.items[index]?.key ?? null; } all() { - return this.items + return this.items; } get size() { - return this.items.length + return this.items.length; + } + + /* ---------------------------------------- + * Keyboard navigation + * ------------------------------------- */ + + activateFirst() { + this.rebuildIndexes() + + if (!this.navIndex.length) return; + + if (this.navIndex[0]) { + this.activeIndex.value = this.navIndex[0]; + } + + this.activeNavPos = 0; + } + + activateLast() { + this.rebuildIndexes() + + if (!this.navIndex.length) return + + this.activeNavPos = this.navIndex.length - 1 + + const activeIndex = this.navIndex[this.activeNavPos]; + + if (typeof activeIndex === 'number') { + this.activeIndex.value = activeIndex; + } + } + + activateNext() { + + this.rebuildIndexes(); + + if (!this.navIndex.length) return; + + if (this.activeNavPos === -1) { + this.activateFirst(); + return; + } + + this.activeNavPos = (this.activeNavPos + 1) % this.navIndex.length + + this.activeIndex.value = this.navIndex[this.activeNavPos] + } + + activatePrev() { + this.rebuildIndexes() + if (!this.navIndex.length) return + + if (this.activeNavPos === -1) { + this.activateLast() + return + } + + this.activeNavPos = + this.activeNavPos === 0 + ? this.navIndex.length - 1 + : this.activeNavPos - 1 + + this.activeIndex.value = this.navIndex[this.activeNavPos] } } diff --git a/src/factories/CreateComboboxInput.ts b/src/factories/CreateComboboxInput.ts index 5ff1b40..3add383 100644 --- a/src/factories/CreateComboboxInput.ts +++ b/src/factories/CreateComboboxInput.ts @@ -22,7 +22,7 @@ export default function CreateComboboxInput(Alpine: AlpineType): ComboboxInputDa this.$el.addEventListener('input', (e: InputEvent) => { e.stopPropagation(); - + if (this.__isTyping) { this.__open(); } @@ -70,6 +70,7 @@ export default function CreateComboboxInput(Alpine: AlpineType): ComboboxInputDa this.__close() this.__resetInput() } + break; } }); diff --git a/src/index.ts b/src/index.ts index 214abba..a2e1cdc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -106,7 +106,7 @@ export default function combobox(Alpine: Alpine): void { }, 'x-data'() { // @todo: move to constructor function here for memory gains - return CreateComboboxOption(Alpine, this.__nextId()) + return CreateComboboxOption(Alpine, this.__nextId()); }, }); } From 5328c82749ab98a13b92797a486655a4b21dc4ee Mon Sep 17 00:00:00 2001 From: charrafimed Date: Thu, 8 Jan 2026 10:59:47 +0100 Subject: [PATCH 3/5] wip --- src/core/ComboboxCollection.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/ComboboxCollection.ts b/src/core/ComboboxCollection.ts index fb562a1..99d7d0b 100644 --- a/src/core/ComboboxCollection.ts +++ b/src/core/ComboboxCollection.ts @@ -118,10 +118,12 @@ export default class ComboboxCollection { this.needsReindex = true; this.lastQuery = ''; this.lastResults = []; + this.scheduleBatch(); } private scheduleBatch() { + if (this.isProcessing) return; this.isProcessing = true; @@ -139,6 +141,7 @@ export default class ComboboxCollection { } private rebuildIndexes() { + if (!this.needsReindex) return; this.navIndex = []; From 061e2b4aad6c2b6738d06566b4207c9b6fa98ed4 Mon Sep 17 00:00:00 2001 From: charrafimed Date: Thu, 5 Feb 2026 15:14:05 +0100 Subject: [PATCH 4/5] wip --- src/magics/{combobox.js => combobox.ts} | 0 src/magics/{option.js => option.ts} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/magics/{combobox.js => combobox.ts} (100%) rename src/magics/{option.js => option.ts} (100%) diff --git a/src/magics/combobox.js b/src/magics/combobox.ts similarity index 100% rename from src/magics/combobox.js rename to src/magics/combobox.ts diff --git a/src/magics/option.js b/src/magics/option.ts similarity index 100% rename from src/magics/option.js rename to src/magics/option.ts From f10d796ba7d1ca807cce525bf52747f9ede7e6d0 Mon Sep 17 00:00:00 2001 From: charrafimed Date: Sat, 7 Feb 2026 18:56:12 +0100 Subject: [PATCH 5/5] wip --- dist/cdn.js | 121 +++++++++--------- dist/cdn.min.js | 2 +- dist/module.cjs.js | 121 +++++++++--------- dist/module.esm.js | 121 +++++++++--------- index.html | 18 +-- ...mboboxCollection.ts => RoverCollection.ts} | 2 +- ...teComboboxInput.ts => CreateRoverInput.ts} | 0 ...ComboboxOption.ts => CreateRoverOption.ts} | 0 ...eateComboboxRoot.ts => CreateRoverRoot.ts} | 4 +- src/index.ts | 38 +++--- 10 files changed, 212 insertions(+), 215 deletions(-) rename src/core/{ComboboxCollection.ts => RoverCollection.ts} (99%) rename src/factories/{CreateComboboxInput.ts => CreateRoverInput.ts} (100%) rename src/factories/{CreateComboboxOption.ts => CreateRoverOption.ts} (100%) rename src/factories/{CreateComboboxRoot.ts => CreateRoverRoot.ts} (98%) diff --git a/dist/cdn.js b/dist/cdn.js index bd19463..29a79bb 100644 --- a/dist/cdn.js +++ b/dist/cdn.js @@ -1,5 +1,5 @@ (() => { - // src/factories/CreateComboboxInput.ts + // src/factories/CreateRoverInput.ts function CreateComboboxInput(Alpine2) { return { init() { @@ -57,7 +57,7 @@ }; } - // src/factories/CreateComboboxOption.ts + // src/factories/CreateRoverOption.ts function CreateComboboxOption(Alpine2, nextId) { const SLOT_NAME = "option"; return { @@ -103,8 +103,8 @@ }; } - // src/core/ComboboxCollection.ts - var ComboboxCollection = class { + // src/core/RoverCollection.ts + var RoverCollection = class { constructor(options = {}) { this.items = []; this.itemsMap = new Map(); @@ -164,47 +164,6 @@ getActiveItem() { return this.activeIndex.value === void 0 ? null : this.items[this.activeIndex.value]; } - activateFirst() { - this.rebuildIndexes(); - if (!this.navIndex.length) - return; - if (this.navIndex[0]) { - this.activeIndex.value = this.navIndex[0]; - } - this.activeNavPos = 0; - } - activateLast() { - this.rebuildIndexes(); - if (!this.navIndex.length) - return; - this.activeNavPos = this.navIndex.length - 1; - const activeIndex = this.navIndex[this.activeNavPos]; - if (typeof activeIndex === "number") { - this.activeIndex.value = activeIndex; - } - } - activateNext() { - this.rebuildIndexes(); - if (!this.navIndex.length) - return; - if (this.activeNavPos === -1) { - this.activateFirst(); - return; - } - this.activeNavPos = (this.activeNavPos + 1) % this.navIndex.length; - this.activeIndex.value = this.navIndex[this.activeNavPos]; - } - activatePrev() { - this.rebuildIndexes(); - if (!this.navIndex.length) - return; - if (this.activeNavPos === -1) { - this.activateLast(); - return; - } - this.activeNavPos = this.activeNavPos === 0 ? this.navIndex.length - 1 : this.activeNavPos - 1; - this.activeIndex.value = this.navIndex[this.activeNavPos]; - } invalidate() { this.needsReindex = true; this.lastQuery = ""; @@ -257,7 +216,6 @@ if (this.searchIndex) { const normalized = q.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); results = []; - console.log("inside search index", this.searchIndex); for (const {key, value} of this.searchIndex) { if (value.includes(normalized)) { const item = this.itemsMap.get(key); @@ -287,12 +245,53 @@ get size() { return this.items.length; } + activateFirst() { + this.rebuildIndexes(); + if (!this.navIndex.length) + return; + if (this.navIndex[0]) { + this.activeIndex.value = this.navIndex[0]; + } + this.activeNavPos = 0; + } + activateLast() { + this.rebuildIndexes(); + if (!this.navIndex.length) + return; + this.activeNavPos = this.navIndex.length - 1; + const activeIndex = this.navIndex[this.activeNavPos]; + if (typeof activeIndex === "number") { + this.activeIndex.value = activeIndex; + } + } + activateNext() { + this.rebuildIndexes(); + if (!this.navIndex.length) + return; + if (this.activeNavPos === -1) { + this.activateFirst(); + return; + } + this.activeNavPos = (this.activeNavPos + 1) % this.navIndex.length; + this.activeIndex.value = this.navIndex[this.activeNavPos]; + } + activatePrev() { + this.rebuildIndexes(); + if (!this.navIndex.length) + return; + if (this.activeNavPos === -1) { + this.activateLast(); + return; + } + this.activeNavPos = this.activeNavPos === 0 ? this.navIndex.length - 1 : this.activeNavPos - 1; + this.activeIndex.value = this.navIndex[this.activeNavPos]; + } }; - var ComboboxCollection_default = ComboboxCollection; + var RoverCollection_default = RoverCollection; - // src/factories/CreateComboboxRoot.ts + // src/factories/CreateRoverRoot.ts function CreateComboboxRoot({el, effect}) { - const collection = new ComboboxCollection_default(); + const collection = new RoverCollection_default(); return { __state: null, __isOpen: false, @@ -517,8 +516,8 @@ } // src/index.ts - function combobox(Alpine2) { - Alpine2.directive("combobox", (el, {value, modifiers}, {Alpine: Alpine3, effect}) => { + function rover(Alpine2) { + Alpine2.directive("rover", (el, {value, modifiers}, {Alpine: Alpine3, effect}) => { switch (value) { case null: handleRoot(Alpine3, el, effect); @@ -548,7 +547,7 @@ handleEmptyState(Alpine3, el); break; default: - console.error("invalid x-combobox value", value, "use input, button, option, options or leave mepty for root level instead"); + console.error("invalid x-rover value", value, "use input, button, option, options or leave mepty for root level instead"); break; } }).before("bind"); @@ -564,7 +563,7 @@ "x-ref": "__input", "x-model": "__searchQuery", "x-bind:id"() { - return this.$id("combobox-input"); + return this.$id("rover-input"); }, role: "combobox", tabindex: "0", @@ -578,7 +577,7 @@ Alpine2.bind(el, { "x-ref": "__options", "x-bind:id"() { - return this.$id("combobox-options"); + return this.$id("rover-options"); }, role: "listbox", "x-init"() { @@ -596,10 +595,10 @@ function handleOption(Alpine3, el) { Alpine3.bind(el, { "x-id"() { - return ["combobox-option"]; + return ["rover-option"]; }, "x-bind:id"() { - return this.$id("combobox-option"); + return this.$id("rover-option"); }, role: "option", "x-show"() { @@ -613,10 +612,10 @@ function handleOptionsGroup(Alpine3, el) { Alpine3.bind(el, { "x-id"() { - return ["combobox-options-group"]; + return ["rover-options-group"]; }, "x-bind:id"() { - return this.$id("combobox-options-group"); + return this.$id("rover-options-group"); }, role: "option", "x-show"() { @@ -628,7 +627,7 @@ Alpine3.bind(el, { "x-ref": "__button", "x-bind:id"() { - return this.$id("combobox-button"); + return this.$id("rover-button"); }, tabindex: "-1", "aria-haspopup": "true", @@ -649,7 +648,7 @@ function handleEmptyState(Alpine3, el) { Alpine3.bind(el, { "x-bind:id"() { - return this.$id("combobox-button"); + return this.$id("rover-button"); }, tabindex: "-1", "aria-haspopup": "true", @@ -672,6 +671,6 @@ // builds/cdn.js document.addEventListener("alpine:init", () => { - window.Alpine.plugin(combobox); + window.Alpine.plugin(rover); }); })(); diff --git a/dist/cdn.min.js b/dist/cdn.min.js index bd067e2..2fedc80 100644 --- a/dist/cdn.min.js +++ b/dist/cdn.min.js @@ -1 +1 @@ -(()=>{function v(h){return{init(){let t=h.extractProp(this.$el,"display-value","");t&&(this.__displayValue=t),this.__handleEvents()},__handleEvents(){this.$el.addEventListener("focus",()=>{this.__startTyping()}),this.$el.addEventListener("input",t=>{t.stopPropagation(),this.__isTyping&&this.__open()}),this.$el.addEventListener("blur",()=>{this.__stopTyping()}),this.$el.addEventListener("keydown",t=>{switch(t.key){case"ArrowDown":if(t.preventDefault(),t.stopPropagation(),!this.__isOpen){this.__open();break}this.__activateNext();break;case"ArrowUp":if(t.preventDefault(),t.stopPropagation(),!this.__isOpen){this.__open();break}this.__activatePrev();break;case"Enter":t.preventDefault(),t.stopPropagation(),this.__selectActive(),this.__stopTyping(),this.__isMultiple||(this.__close(),this.__resetInput());break}})}}}function f(h,t){let i="option";return{__uniqueKey:"option-"+t,init(){this.$el.dataset.slot=i;let e=h.extractProp(this.$el,"value","");this.$el.dataset.key=this.__uniqueKey,this.$el.dataset.value=e;let n=h.extractProp(this.$el,"disabled",!1,!1);this.__add(this.__uniqueKey,e,n),this.$watch("__activedKey",s=>{s===this.__uniqueKey?this.$el.setAttribute("data-active","true"):this.$el.removeAttribute("data-active")}),this.$watch("__selectedKeys",s=>{let r=!1;this.__isMultiple?r=Array.isArray(s)&&s.includes(this.__uniqueKey):r=s===this.__uniqueKey,r?(this.$el.setAttribute("aria-selected","true"),this.$el.setAttribute("data-selected","true")):(this.$el.setAttribute("aria-selected","false"),this.$el.removeAttribute("data-selected"))}),this.$nextTick(()=>{n&&this.$el.setAttribute("tabindex","-1")})},destroy(){this.__forget(this.__uniqueKey)}}}var b=class{constructor(t={}){this.items=[];this.itemsMap=new Map;this.activeNavPos=-1;this.needsReindex=!1;this.navIndex=[];this.lastQuery="";this.isProcessing=!1;this.pending=Alpine.reactive({state:!1}),this.activeIndex=Alpine.reactive({value:void 0}),this.searchThreshold=t.searchThreshold??500}add(t,i,e=!1){if(this.itemsMap.has(t))return;let n={key:t,value:i,disabled:e};this.items.push(n),this.itemsMap.set(t,n),this.invalidate()}forget(t){let i=this.itemsMap.get(t);if(!i)return;let e=this.items.indexOf(i);this.itemsMap.delete(t),this.items.splice(e,1),this.activeIndex.value===e?(this.activeIndex.value=void 0,this.activeNavPos=-1):this.activeIndex.value&&this.activeIndex.value>e&&this.activeIndex.value--,this.invalidate()}activate(t){let i=this.get(t);if(!i||i.disabled)return;this.rebuildIndexes();let e=this.items.indexOf(i);this.activeIndex.value!==e&&(this.activeIndex.value=e,this.activeNavPos=this.navIndex.indexOf(e))}deactivate(){this.activeIndex.value=void 0,this.activeNavPos=-1}isActivated(t){let i=this.get(t);return i?this.items.indexOf(i)===this.activeIndex.value:!1}getActiveItem(){return this.activeIndex.value===void 0?null:this.items[this.activeIndex.value]}activateFirst(){this.rebuildIndexes(),!!this.navIndex.length&&(this.navIndex[0]&&(this.activeIndex.value=this.navIndex[0]),this.activeNavPos=0)}activateLast(){if(this.rebuildIndexes(),!this.navIndex.length)return;this.activeNavPos=this.navIndex.length-1;let t=this.navIndex[this.activeNavPos];typeof t=="number"&&(this.activeIndex.value=t)}activateNext(){if(this.rebuildIndexes(),!!this.navIndex.length){if(this.activeNavPos===-1){this.activateFirst();return}this.activeNavPos=(this.activeNavPos+1)%this.navIndex.length,this.activeIndex.value=this.navIndex[this.activeNavPos]}}activatePrev(){if(this.rebuildIndexes(),!!this.navIndex.length){if(this.activeNavPos===-1){this.activateLast();return}this.activeNavPos=this.activeNavPos===0?this.navIndex.length-1:this.activeNavPos-1,this.activeIndex.value=this.navIndex[this.activeNavPos]}}invalidate(){this.needsReindex=!0,this.lastQuery="",this.lastResults=[],this.scheduleBatch()}scheduleBatch(){this.isProcessing||(this.isProcessing=!0,this.pending.state=!0,queueMicrotask(()=>{this.rebuildIndexes(),this.isProcessing=!1,this.pending.state=!1}))}toggleIsPending(){this.pending.state=!this.pending.state}rebuildIndexes(){if(!!this.needsReindex){this.navIndex=[];for(let t=0;t({key:t.key,value:String(t.value).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"")})),this.needsReindex=!1}}search(t){if(!t)return this.lastQuery="",this.lastResults=[],this.items;let i=t.toLowerCase();if(this.lastQuery&&i.startsWith(this.lastQuery)&&this.lastResults){let n=this.lastResults.filter(s=>String(s.value).toLowerCase().includes(i));return this.lastQuery=i,this.lastResults=n,n}let e;if(this.searchIndex){let n=i.normalize("NFD").replace(/[\u0300-\u036f]/g,"");e=[],console.log("inside search index",this.searchIndex);for(let{key:s,value:r}of this.searchIndex)if(r.includes(n)){let l=this.itemsMap.get(s);l&&e.push(l)}}else e=this.items.filter(n=>String(n.value).toLowerCase().includes(i));return this.lastQuery=i,this.lastResults=e,e}get(t){return this.itemsMap.get(t)}getValueByKey(t){return this.get(t)?.value}getKeyByIndex(t){return t==null?null:this.items[t]?.key??null}all(){return this.items}get size(){return this.items.length}},y=b;function m({el:h,effect:t}){let i=new y;return{__state:null,__isOpen:!1,__isMultiple:!1,__isTyping:!1,__isLoading:!1,__uid:0,__static:!1,__keepActivated:!0,__optionsEl:void 0,__compareBy:void 0,__activedKey:void 0,__selectedKeys:void 0,__filteredKeys:null,__isDisabled:!1,__searchQuery:"",__add:(e,n,s)=>i.add(e,n,s),__forget:e=>i.forget(e),__activate:e=>i.activate(e),__isActive:e=>i.isActivated(e),__deactivate:()=>i.deactivate(),__getValueByKey:e=>i.getValueByKey(e),__activateNext:()=>i.activateNext(),__activatePrev:()=>i.activatePrev(),__activateFirst:()=>i.activateFirst(),__activateLast:()=>i.activateLast(),__isVisible(e){return this.__searchQuery===""||!this.__filteredKeys?!0:this.__filteredKeys.includes(e)},init(){t(()=>{this.__isLoading=i.pending.state}),t(()=>{this.__activedKey=i.getKeyByIndex(i.activeIndex.value)}),t(()=>{let s=i.search(this.__searchQuery).map(r=>r.key);s.length>=0?this.__filteredKeys=s:this.__filteredKeys=null,this.__activedKey&&this.__filteredKeys&&!this.__filteredKeys.includes(this.__activedKey)&&i.deactivate(),this.__isOpen&&!i.getActiveItem()&&this.__filteredKeys&&this.__filteredKeys.length&&i.activate(this.__filteredKeys[0])}),this.__isMultiple?this.__selectedKeys=[]:this.__selectedKeys=null,this.__isMultiple=Alpine.extractProp(h,"multiple",!1),this.__isDisabled=Alpine.extractProp(h,"disabled",!1),this.__compareBy=Alpine.extractProp(h,"by","");let e=this.__isMultiple?[]:"",n=Alpine.extractProp(h,"initial-value",e);this.__state=n,this.__registerEventsDelector()},__open(){if(this.__isOpen)return;this.__isOpen=!0;let e=this.$refs.__input;requestAnimationFrame(()=>{e.focus({preventScroll:!0}),this.__activateSelectedOrFirst()})},__activateSelectedOrFirst(e=!0){if(!(!this.__isOpen||i.getActiveItem())){if(e&&this.__selectedKeys){let s=this.__isMultiple?this.__selectedKeys[0]:this.__selectedKeys;if(s){this.__activate(s);return}}i.activateFirst()}},__close(){this.__isOpen=!1,this.__deactivate()},__handleSelection(e){let n=this.__getValueByKey(e);if(!this.__isMultiple){this.__selectedKeys=e,this.__state===n?(this.__state=null,this.__selectedKeys=null):this.__state=n,this.__static||this.__close();return}Array.isArray(this.__selectedKeys)||(this.__selectedKeys=[]),Array.isArray(this.__state)||(this.__state=[]);let s=this.__state.findIndex(l=>this.__compare(l,n)),r=this.__selectedKeys.indexOf(e);s===-1?(this.__state.push(n),this.__selectedKeys.push(e)):(this.__state.splice(s,1),this.__selectedKeys.splice(r,1))},__selectActive(){!this.__activedKey||this.__handleSelection(this.__activedKey)},__startTyping(){this.__isTyping=!0},__stopTyping(){this.__isTyping=!1},__resetInput(){let e=this.$refs.__input;if(!e)return;let n=this.__getCurrentValue();e.value=n},__getCurrentValue(){return!this.$refs.__input||!this.__state?"":typeof this.__state=="string"?this.__state:""},__compare(e,n){let s=this.__compareBy;if(!this.__compareBy)s=(r,l)=>Alpine.raw(r)===Alpine.raw(l);else if(typeof this.__compareBy=="string"){let r=this.__compareBy;s=(l,d)=>{if(!l||typeof l!="object"||!d||typeof d!="object")return Alpine.raw(l)===Alpine.raw(d);let p=l,a=d;return p[r]===a[r]}}return s(e,n)},__nextId(){return++this.__uid},__registerEventsDelector(){let e=s=>Alpine.findClosest(s,r=>r.dataset.slot==="option"),n=s=>function(r){if(r.stopPropagation(),!(r.target instanceof Element))return;let l=e(r.target);!l||s(l)};this.$nextTick(()=>{this.__optionsEl=this.$refs.__options,!!this.__optionsEl&&(this.__optionsEl.addEventListener("click",n(s=>{!s.dataset.key||(this.__handleSelection(s.dataset.key),!this.__isMultiple&&!this.__static&&(this.__close(),this.__resetInput()),this.$nextTick(()=>this.$refs.__input.focus({preventScroll:!0})))})),this.__optionsEl.addEventListener("mouseover",n(s=>{!s.dataset.key||this.__activate(s.dataset.key)})),this.__optionsEl.addEventListener("mousemove",n(s=>{this.__isActive(s.dataset.key||"")||!s.dataset.key||this.__activate(s.dataset.key)})),this.__optionsEl.addEventListener("mouseout",n(()=>{this.__keepActivated||this.__deactivate()})))})}}}function x(h){h.directive("combobox",(a,{value:o,modifiers:u},{Alpine:_,effect:c})=>{switch(o){case null:t(_,a,c);break;case"input":i(_,a);break;case"button":r(_,a);break;case"options":e(a);break;case"option":n(_,a);break;case"options-group":s(_,a);break;case"loading":d(_,a,u);break;case"separator":p(_,a);break;case"empty":l(_,a);break;default:console.error("invalid x-combobox value",o,"use input, button, option, options or leave mepty for root level instead");break}}).before("bind");function t(a,o,u){a.bind(o,{"x-data"(){return m({el:o,effect:u})}})}function i(a,o){a.bind(o,{"x-ref":"__input","x-model":"__searchQuery","x-bind:id"(){return this.$id("combobox-input")},role:"combobox",tabindex:"0","aria-autocomplete":"list","x-data"(){return v(a)}})}function e(a){h.bind(a,{"x-ref":"__options","x-bind:id"(){return this.$id("combobox-options")},role:"listbox","x-init"(){return this.$data.__static=h.extractProp(this.$el,"static",!1),h.bound(this.$el,"keepActivated")&&(this.__keepActivated=!0),this.$el.dataset.slot="options"},"x-show"(){return this.$data.__static?!0:this.$data.__isOpen}})}function n(a,o){a.bind(o,{"x-id"(){return["combobox-option"]},"x-bind:id"(){return this.$id("combobox-option")},role:"option","x-show"(){return this.$data.__isVisible(this.$el.dataset.key)},"x-data"(){return f(a,this.__nextId())}})}function s(a,o){a.bind(o,{"x-id"(){return["combobox-options-group"]},"x-bind:id"(){return this.$id("combobox-options-group")},role:"option","x-show"(){return!0}})}function r(a,o){a.bind(o,{"x-ref":"__button","x-bind:id"(){return this.$id("combobox-button")},tabindex:"-1","aria-haspopup":"true","x-on:click"(u){this.__isDisabled||(this.__isOpen?(this.__close(),this.__resetInput()):(u.preventDefault(),this.__open()),requestAnimationFrame(()=>this.$refs.__input.focus({preventScroll:!0})))}})}function l(a,o){a.bind(o,{"x-bind:id"(){return this.$id("combobox-button")},tabindex:"-1","aria-haspopup":"true","x-show"(){return Array.isArray(this.__filteredKeys)&&this.__filteredKeys.length===0}})}function d(a,o,u){let _=a.$data(o);u.filter(c=>c==="hide")}function p(a,o){a.bind(o,{})}}document.addEventListener("alpine:init",()=>{window.Alpine.plugin(x)});})(); +(()=>{function v(u){return{init(){let e=u.extractProp(this.$el,"display-value","");e&&(this.__displayValue=e),this.__handleEvents()},__handleEvents(){this.$el.addEventListener("focus",()=>{this.__startTyping()}),this.$el.addEventListener("input",e=>{e.stopPropagation(),this.__isTyping&&this.__open()}),this.$el.addEventListener("blur",()=>{this.__stopTyping()}),this.$el.addEventListener("keydown",e=>{switch(e.key){case"ArrowDown":if(e.preventDefault(),e.stopPropagation(),!this.__isOpen){this.__open();break}this.__activateNext();break;case"ArrowUp":if(e.preventDefault(),e.stopPropagation(),!this.__isOpen){this.__open();break}this.__activatePrev();break;case"Enter":e.preventDefault(),e.stopPropagation(),this.__selectActive(),this.__stopTyping(),this.__isMultiple||(this.__close(),this.__resetInput());break}})}}}function f(u,e){let i="option";return{__uniqueKey:"option-"+e,init(){this.$el.dataset.slot=i;let t=u.extractProp(this.$el,"value","");this.$el.dataset.key=this.__uniqueKey,this.$el.dataset.value=t;let n=u.extractProp(this.$el,"disabled",!1,!1);this.__add(this.__uniqueKey,t,n),this.$watch("__activedKey",s=>{s===this.__uniqueKey?this.$el.setAttribute("data-active","true"):this.$el.removeAttribute("data-active")}),this.$watch("__selectedKeys",s=>{let r=!1;this.__isMultiple?r=Array.isArray(s)&&s.includes(this.__uniqueKey):r=s===this.__uniqueKey,r?(this.$el.setAttribute("aria-selected","true"),this.$el.setAttribute("data-selected","true")):(this.$el.setAttribute("aria-selected","false"),this.$el.removeAttribute("data-selected"))}),this.$nextTick(()=>{n&&this.$el.setAttribute("tabindex","-1")})},destroy(){this.__forget(this.__uniqueKey)}}}var y=class{constructor(e={}){this.items=[];this.itemsMap=new Map;this.activeNavPos=-1;this.needsReindex=!1;this.navIndex=[];this.lastQuery="";this.isProcessing=!1;this.pending=Alpine.reactive({state:!1}),this.activeIndex=Alpine.reactive({value:void 0}),this.searchThreshold=e.searchThreshold??500}add(e,i,t=!1){if(this.itemsMap.has(e))return;let n={key:e,value:i,disabled:t};this.items.push(n),this.itemsMap.set(e,n),this.invalidate()}forget(e){let i=this.itemsMap.get(e);if(!i)return;let t=this.items.indexOf(i);this.itemsMap.delete(e),this.items.splice(t,1),this.activeIndex.value===t?(this.activeIndex.value=void 0,this.activeNavPos=-1):this.activeIndex.value&&this.activeIndex.value>t&&this.activeIndex.value--,this.invalidate()}activate(e){let i=this.get(e);if(!i||i.disabled)return;this.rebuildIndexes();let t=this.items.indexOf(i);this.activeIndex.value!==t&&(this.activeIndex.value=t,this.activeNavPos=this.navIndex.indexOf(t))}deactivate(){this.activeIndex.value=void 0,this.activeNavPos=-1}isActivated(e){let i=this.get(e);return i?this.items.indexOf(i)===this.activeIndex.value:!1}getActiveItem(){return this.activeIndex.value===void 0?null:this.items[this.activeIndex.value]}invalidate(){this.needsReindex=!0,this.lastQuery="",this.lastResults=[],this.scheduleBatch()}scheduleBatch(){this.isProcessing||(this.isProcessing=!0,this.pending.state=!0,queueMicrotask(()=>{this.rebuildIndexes(),this.isProcessing=!1,this.pending.state=!1}))}toggleIsPending(){this.pending.state=!this.pending.state}rebuildIndexes(){if(!!this.needsReindex){this.navIndex=[];for(let e=0;e({key:e.key,value:String(e.value).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"")})),this.needsReindex=!1}}search(e){if(!e)return this.lastQuery="",this.lastResults=[],this.items;let i=e.toLowerCase();if(this.lastQuery&&i.startsWith(this.lastQuery)&&this.lastResults){let n=this.lastResults.filter(s=>String(s.value).toLowerCase().includes(i));return this.lastQuery=i,this.lastResults=n,n}let t;if(this.searchIndex){let n=i.normalize("NFD").replace(/[\u0300-\u036f]/g,"");t=[];for(let{key:s,value:r}of this.searchIndex)if(r.includes(n)){let l=this.itemsMap.get(s);l&&t.push(l)}}else t=this.items.filter(n=>String(n.value).toLowerCase().includes(i));return this.lastQuery=i,this.lastResults=t,t}get(e){return this.itemsMap.get(e)}getValueByKey(e){return this.get(e)?.value}getKeyByIndex(e){return e==null?null:this.items[e]?.key??null}all(){return this.items}get size(){return this.items.length}activateFirst(){this.rebuildIndexes(),!!this.navIndex.length&&(this.navIndex[0]&&(this.activeIndex.value=this.navIndex[0]),this.activeNavPos=0)}activateLast(){if(this.rebuildIndexes(),!this.navIndex.length)return;this.activeNavPos=this.navIndex.length-1;let e=this.navIndex[this.activeNavPos];typeof e=="number"&&(this.activeIndex.value=e)}activateNext(){if(this.rebuildIndexes(),!!this.navIndex.length){if(this.activeNavPos===-1){this.activateFirst();return}this.activeNavPos=(this.activeNavPos+1)%this.navIndex.length,this.activeIndex.value=this.navIndex[this.activeNavPos]}}activatePrev(){if(this.rebuildIndexes(),!!this.navIndex.length){if(this.activeNavPos===-1){this.activateLast();return}this.activeNavPos=this.activeNavPos===0?this.navIndex.length-1:this.activeNavPos-1,this.activeIndex.value=this.navIndex[this.activeNavPos]}}},b=y;function m({el:u,effect:e}){let i=new b;return{__state:null,__isOpen:!1,__isMultiple:!1,__isTyping:!1,__isLoading:!1,__uid:0,__static:!1,__keepActivated:!0,__optionsEl:void 0,__compareBy:void 0,__activedKey:void 0,__selectedKeys:void 0,__filteredKeys:null,__isDisabled:!1,__searchQuery:"",__add:(t,n,s)=>i.add(t,n,s),__forget:t=>i.forget(t),__activate:t=>i.activate(t),__isActive:t=>i.isActivated(t),__deactivate:()=>i.deactivate(),__getValueByKey:t=>i.getValueByKey(t),__activateNext:()=>i.activateNext(),__activatePrev:()=>i.activatePrev(),__activateFirst:()=>i.activateFirst(),__activateLast:()=>i.activateLast(),__isVisible(t){return this.__searchQuery===""||!this.__filteredKeys?!0:this.__filteredKeys.includes(t)},init(){e(()=>{this.__isLoading=i.pending.state}),e(()=>{this.__activedKey=i.getKeyByIndex(i.activeIndex.value)}),e(()=>{let s=i.search(this.__searchQuery).map(r=>r.key);s.length>=0?this.__filteredKeys=s:this.__filteredKeys=null,this.__activedKey&&this.__filteredKeys&&!this.__filteredKeys.includes(this.__activedKey)&&i.deactivate(),this.__isOpen&&!i.getActiveItem()&&this.__filteredKeys&&this.__filteredKeys.length&&i.activate(this.__filteredKeys[0])}),this.__isMultiple?this.__selectedKeys=[]:this.__selectedKeys=null,this.__isMultiple=Alpine.extractProp(u,"multiple",!1),this.__isDisabled=Alpine.extractProp(u,"disabled",!1),this.__compareBy=Alpine.extractProp(u,"by","");let t=this.__isMultiple?[]:"",n=Alpine.extractProp(u,"initial-value",t);this.__state=n,this.__registerEventsDelector()},__open(){if(this.__isOpen)return;this.__isOpen=!0;let t=this.$refs.__input;requestAnimationFrame(()=>{t.focus({preventScroll:!0}),this.__activateSelectedOrFirst()})},__activateSelectedOrFirst(t=!0){if(!(!this.__isOpen||i.getActiveItem())){if(t&&this.__selectedKeys){let s=this.__isMultiple?this.__selectedKeys[0]:this.__selectedKeys;if(s){this.__activate(s);return}}i.activateFirst()}},__close(){this.__isOpen=!1,this.__deactivate()},__handleSelection(t){let n=this.__getValueByKey(t);if(!this.__isMultiple){this.__selectedKeys=t,this.__state===n?(this.__state=null,this.__selectedKeys=null):this.__state=n,this.__static||this.__close();return}Array.isArray(this.__selectedKeys)||(this.__selectedKeys=[]),Array.isArray(this.__state)||(this.__state=[]);let s=this.__state.findIndex(l=>this.__compare(l,n)),r=this.__selectedKeys.indexOf(t);s===-1?(this.__state.push(n),this.__selectedKeys.push(t)):(this.__state.splice(s,1),this.__selectedKeys.splice(r,1))},__selectActive(){!this.__activedKey||this.__handleSelection(this.__activedKey)},__startTyping(){this.__isTyping=!0},__stopTyping(){this.__isTyping=!1},__resetInput(){let t=this.$refs.__input;if(!t)return;let n=this.__getCurrentValue();t.value=n},__getCurrentValue(){return!this.$refs.__input||!this.__state?"":typeof this.__state=="string"?this.__state:""},__compare(t,n){let s=this.__compareBy;if(!this.__compareBy)s=(r,l)=>Alpine.raw(r)===Alpine.raw(l);else if(typeof this.__compareBy=="string"){let r=this.__compareBy;s=(l,d)=>{if(!l||typeof l!="object"||!d||typeof d!="object")return Alpine.raw(l)===Alpine.raw(d);let p=l,a=d;return p[r]===a[r]}}return s(t,n)},__nextId(){return++this.__uid},__registerEventsDelector(){let t=s=>Alpine.findClosest(s,r=>r.dataset.slot==="option"),n=s=>function(r){if(r.stopPropagation(),!(r.target instanceof Element))return;let l=t(r.target);!l||s(l)};this.$nextTick(()=>{this.__optionsEl=this.$refs.__options,!!this.__optionsEl&&(this.__optionsEl.addEventListener("click",n(s=>{!s.dataset.key||(this.__handleSelection(s.dataset.key),!this.__isMultiple&&!this.__static&&(this.__close(),this.__resetInput()),this.$nextTick(()=>this.$refs.__input.focus({preventScroll:!0})))})),this.__optionsEl.addEventListener("mouseover",n(s=>{!s.dataset.key||this.__activate(s.dataset.key)})),this.__optionsEl.addEventListener("mousemove",n(s=>{this.__isActive(s.dataset.key||"")||!s.dataset.key||this.__activate(s.dataset.key)})),this.__optionsEl.addEventListener("mouseout",n(()=>{this.__keepActivated||this.__deactivate()})))})}}}function x(u){u.directive("rover",(a,{value:o,modifiers:h},{Alpine:_,effect:c})=>{switch(o){case null:e(_,a,c);break;case"input":i(_,a);break;case"button":r(_,a);break;case"options":t(a);break;case"option":n(_,a);break;case"options-group":s(_,a);break;case"loading":d(_,a,h);break;case"separator":p(_,a);break;case"empty":l(_,a);break;default:console.error("invalid x-rover value",o,"use input, button, option, options or leave mepty for root level instead");break}}).before("bind");function e(a,o,h){a.bind(o,{"x-data"(){return m({el:o,effect:h})}})}function i(a,o){a.bind(o,{"x-ref":"__input","x-model":"__searchQuery","x-bind:id"(){return this.$id("rover-input")},role:"combobox",tabindex:"0","aria-autocomplete":"list","x-data"(){return v(a)}})}function t(a){u.bind(a,{"x-ref":"__options","x-bind:id"(){return this.$id("rover-options")},role:"listbox","x-init"(){return this.$data.__static=u.extractProp(this.$el,"static",!1),u.bound(this.$el,"keepActivated")&&(this.__keepActivated=!0),this.$el.dataset.slot="options"},"x-show"(){return this.$data.__static?!0:this.$data.__isOpen}})}function n(a,o){a.bind(o,{"x-id"(){return["rover-option"]},"x-bind:id"(){return this.$id("rover-option")},role:"option","x-show"(){return this.$data.__isVisible(this.$el.dataset.key)},"x-data"(){return f(a,this.__nextId())}})}function s(a,o){a.bind(o,{"x-id"(){return["rover-options-group"]},"x-bind:id"(){return this.$id("rover-options-group")},role:"option","x-show"(){return!0}})}function r(a,o){a.bind(o,{"x-ref":"__button","x-bind:id"(){return this.$id("rover-button")},tabindex:"-1","aria-haspopup":"true","x-on:click"(h){this.__isDisabled||(this.__isOpen?(this.__close(),this.__resetInput()):(h.preventDefault(),this.__open()),requestAnimationFrame(()=>this.$refs.__input.focus({preventScroll:!0})))}})}function l(a,o){a.bind(o,{"x-bind:id"(){return this.$id("rover-button")},tabindex:"-1","aria-haspopup":"true","x-show"(){return Array.isArray(this.__filteredKeys)&&this.__filteredKeys.length===0}})}function d(a,o,h){let _=a.$data(o);h.filter(c=>c==="hide")}function p(a,o){a.bind(o,{})}}document.addEventListener("alpine:init",()=>{window.Alpine.plugin(x)});})(); diff --git a/dist/module.cjs.js b/dist/module.cjs.js index c8489bc..74be511 100644 --- a/dist/module.cjs.js +++ b/dist/module.cjs.js @@ -11,7 +11,7 @@ __export(exports, { default: () => module_default }); -// src/factories/CreateComboboxInput.ts +// src/factories/CreateRoverInput.ts function CreateComboboxInput(Alpine2) { return { init() { @@ -69,7 +69,7 @@ function CreateComboboxInput(Alpine2) { }; } -// src/factories/CreateComboboxOption.ts +// src/factories/CreateRoverOption.ts function CreateComboboxOption(Alpine2, nextId) { const SLOT_NAME = "option"; return { @@ -115,8 +115,8 @@ function CreateComboboxOption(Alpine2, nextId) { }; } -// src/core/ComboboxCollection.ts -var ComboboxCollection = class { +// src/core/RoverCollection.ts +var RoverCollection = class { constructor(options = {}) { this.items = []; this.itemsMap = new Map(); @@ -177,47 +177,6 @@ var ComboboxCollection = class { getActiveItem() { return this.activeIndex.value === void 0 ? null : this.items[this.activeIndex.value]; } - activateFirst() { - this.rebuildIndexes(); - if (!this.navIndex.length) - return; - if (this.navIndex[0]) { - this.activeIndex.value = this.navIndex[0]; - } - this.activeNavPos = 0; - } - activateLast() { - this.rebuildIndexes(); - if (!this.navIndex.length) - return; - this.activeNavPos = this.navIndex.length - 1; - const activeIndex = this.navIndex[this.activeNavPos]; - if (typeof activeIndex === "number") { - this.activeIndex.value = activeIndex; - } - } - activateNext() { - this.rebuildIndexes(); - if (!this.navIndex.length) - return; - if (this.activeNavPos === -1) { - this.activateFirst(); - return; - } - this.activeNavPos = (this.activeNavPos + 1) % this.navIndex.length; - this.activeIndex.value = this.navIndex[this.activeNavPos]; - } - activatePrev() { - this.rebuildIndexes(); - if (!this.navIndex.length) - return; - if (this.activeNavPos === -1) { - this.activateLast(); - return; - } - this.activeNavPos = this.activeNavPos === 0 ? this.navIndex.length - 1 : this.activeNavPos - 1; - this.activeIndex.value = this.navIndex[this.activeNavPos]; - } invalidate() { this.needsReindex = true; this.lastQuery = ""; @@ -271,7 +230,6 @@ var ComboboxCollection = class { if (this.searchIndex) { const normalized = q.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); results = []; - console.log("inside search index", this.searchIndex); for (const {key, value} of this.searchIndex) { if (value.includes(normalized)) { const item = this.itemsMap.get(key); @@ -303,12 +261,53 @@ var ComboboxCollection = class { get size() { return this.items.length; } + activateFirst() { + this.rebuildIndexes(); + if (!this.navIndex.length) + return; + if (this.navIndex[0]) { + this.activeIndex.value = this.navIndex[0]; + } + this.activeNavPos = 0; + } + activateLast() { + this.rebuildIndexes(); + if (!this.navIndex.length) + return; + this.activeNavPos = this.navIndex.length - 1; + const activeIndex = this.navIndex[this.activeNavPos]; + if (typeof activeIndex === "number") { + this.activeIndex.value = activeIndex; + } + } + activateNext() { + this.rebuildIndexes(); + if (!this.navIndex.length) + return; + if (this.activeNavPos === -1) { + this.activateFirst(); + return; + } + this.activeNavPos = (this.activeNavPos + 1) % this.navIndex.length; + this.activeIndex.value = this.navIndex[this.activeNavPos]; + } + activatePrev() { + this.rebuildIndexes(); + if (!this.navIndex.length) + return; + if (this.activeNavPos === -1) { + this.activateLast(); + return; + } + this.activeNavPos = this.activeNavPos === 0 ? this.navIndex.length - 1 : this.activeNavPos - 1; + this.activeIndex.value = this.navIndex[this.activeNavPos]; + } }; -var ComboboxCollection_default = ComboboxCollection; +var RoverCollection_default = RoverCollection; -// src/factories/CreateComboboxRoot.ts +// src/factories/CreateRoverRoot.ts function CreateComboboxRoot({el, effect}) { - const collection = new ComboboxCollection_default(); + const collection = new RoverCollection_default(); return { __state: null, __isOpen: false, @@ -533,8 +532,8 @@ function CreateComboboxRoot({el, effect}) { } // src/index.ts -function combobox(Alpine2) { - Alpine2.directive("combobox", (el, {value, modifiers}, {Alpine: Alpine3, effect}) => { +function rover(Alpine2) { + Alpine2.directive("rover", (el, {value, modifiers}, {Alpine: Alpine3, effect}) => { switch (value) { case null: handleRoot(Alpine3, el, effect); @@ -564,7 +563,7 @@ function combobox(Alpine2) { handleEmptyState(Alpine3, el); break; default: - console.error("invalid x-combobox value", value, "use input, button, option, options or leave mepty for root level instead"); + console.error("invalid x-rover value", value, "use input, button, option, options or leave mepty for root level instead"); break; } }).before("bind"); @@ -580,7 +579,7 @@ function combobox(Alpine2) { "x-ref": "__input", "x-model": "__searchQuery", "x-bind:id"() { - return this.$id("combobox-input"); + return this.$id("rover-input"); }, role: "combobox", tabindex: "0", @@ -594,7 +593,7 @@ function combobox(Alpine2) { Alpine2.bind(el, { "x-ref": "__options", "x-bind:id"() { - return this.$id("combobox-options"); + return this.$id("rover-options"); }, role: "listbox", "x-init"() { @@ -612,10 +611,10 @@ function combobox(Alpine2) { function handleOption(Alpine3, el) { Alpine3.bind(el, { "x-id"() { - return ["combobox-option"]; + return ["rover-option"]; }, "x-bind:id"() { - return this.$id("combobox-option"); + return this.$id("rover-option"); }, role: "option", "x-show"() { @@ -629,10 +628,10 @@ function combobox(Alpine2) { function handleOptionsGroup(Alpine3, el) { Alpine3.bind(el, { "x-id"() { - return ["combobox-options-group"]; + return ["rover-options-group"]; }, "x-bind:id"() { - return this.$id("combobox-options-group"); + return this.$id("rover-options-group"); }, role: "option", "x-show"() { @@ -644,7 +643,7 @@ function combobox(Alpine2) { Alpine3.bind(el, { "x-ref": "__button", "x-bind:id"() { - return this.$id("combobox-button"); + return this.$id("rover-button"); }, tabindex: "-1", "aria-haspopup": "true", @@ -665,7 +664,7 @@ function combobox(Alpine2) { function handleEmptyState(Alpine3, el) { Alpine3.bind(el, { "x-bind:id"() { - return this.$id("combobox-button"); + return this.$id("rover-button"); }, tabindex: "-1", "aria-haspopup": "true", @@ -687,4 +686,4 @@ function combobox(Alpine2) { } // builds/module.js -var module_default = combobox; +var module_default = rover; diff --git a/dist/module.esm.js b/dist/module.esm.js index 7da312f..c61523f 100644 --- a/dist/module.esm.js +++ b/dist/module.esm.js @@ -1,4 +1,4 @@ -// src/factories/CreateComboboxInput.ts +// src/factories/CreateRoverInput.ts function CreateComboboxInput(Alpine2) { return { init() { @@ -56,7 +56,7 @@ function CreateComboboxInput(Alpine2) { }; } -// src/factories/CreateComboboxOption.ts +// src/factories/CreateRoverOption.ts function CreateComboboxOption(Alpine2, nextId) { const SLOT_NAME = "option"; return { @@ -102,8 +102,8 @@ function CreateComboboxOption(Alpine2, nextId) { }; } -// src/core/ComboboxCollection.ts -var ComboboxCollection = class { +// src/core/RoverCollection.ts +var RoverCollection = class { constructor(options = {}) { this.items = []; this.itemsMap = new Map(); @@ -163,47 +163,6 @@ var ComboboxCollection = class { getActiveItem() { return this.activeIndex.value === void 0 ? null : this.items[this.activeIndex.value]; } - activateFirst() { - this.rebuildIndexes(); - if (!this.navIndex.length) - return; - if (this.navIndex[0]) { - this.activeIndex.value = this.navIndex[0]; - } - this.activeNavPos = 0; - } - activateLast() { - this.rebuildIndexes(); - if (!this.navIndex.length) - return; - this.activeNavPos = this.navIndex.length - 1; - const activeIndex = this.navIndex[this.activeNavPos]; - if (typeof activeIndex === "number") { - this.activeIndex.value = activeIndex; - } - } - activateNext() { - this.rebuildIndexes(); - if (!this.navIndex.length) - return; - if (this.activeNavPos === -1) { - this.activateFirst(); - return; - } - this.activeNavPos = (this.activeNavPos + 1) % this.navIndex.length; - this.activeIndex.value = this.navIndex[this.activeNavPos]; - } - activatePrev() { - this.rebuildIndexes(); - if (!this.navIndex.length) - return; - if (this.activeNavPos === -1) { - this.activateLast(); - return; - } - this.activeNavPos = this.activeNavPos === 0 ? this.navIndex.length - 1 : this.activeNavPos - 1; - this.activeIndex.value = this.navIndex[this.activeNavPos]; - } invalidate() { this.needsReindex = true; this.lastQuery = ""; @@ -256,7 +215,6 @@ var ComboboxCollection = class { if (this.searchIndex) { const normalized = q.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); results = []; - console.log("inside search index", this.searchIndex); for (const {key, value} of this.searchIndex) { if (value.includes(normalized)) { const item = this.itemsMap.get(key); @@ -286,12 +244,53 @@ var ComboboxCollection = class { get size() { return this.items.length; } + activateFirst() { + this.rebuildIndexes(); + if (!this.navIndex.length) + return; + if (this.navIndex[0]) { + this.activeIndex.value = this.navIndex[0]; + } + this.activeNavPos = 0; + } + activateLast() { + this.rebuildIndexes(); + if (!this.navIndex.length) + return; + this.activeNavPos = this.navIndex.length - 1; + const activeIndex = this.navIndex[this.activeNavPos]; + if (typeof activeIndex === "number") { + this.activeIndex.value = activeIndex; + } + } + activateNext() { + this.rebuildIndexes(); + if (!this.navIndex.length) + return; + if (this.activeNavPos === -1) { + this.activateFirst(); + return; + } + this.activeNavPos = (this.activeNavPos + 1) % this.navIndex.length; + this.activeIndex.value = this.navIndex[this.activeNavPos]; + } + activatePrev() { + this.rebuildIndexes(); + if (!this.navIndex.length) + return; + if (this.activeNavPos === -1) { + this.activateLast(); + return; + } + this.activeNavPos = this.activeNavPos === 0 ? this.navIndex.length - 1 : this.activeNavPos - 1; + this.activeIndex.value = this.navIndex[this.activeNavPos]; + } }; -var ComboboxCollection_default = ComboboxCollection; +var RoverCollection_default = RoverCollection; -// src/factories/CreateComboboxRoot.ts +// src/factories/CreateRoverRoot.ts function CreateComboboxRoot({el, effect}) { - const collection = new ComboboxCollection_default(); + const collection = new RoverCollection_default(); return { __state: null, __isOpen: false, @@ -516,8 +515,8 @@ function CreateComboboxRoot({el, effect}) { } // src/index.ts -function combobox(Alpine2) { - Alpine2.directive("combobox", (el, {value, modifiers}, {Alpine: Alpine3, effect}) => { +function rover(Alpine2) { + Alpine2.directive("rover", (el, {value, modifiers}, {Alpine: Alpine3, effect}) => { switch (value) { case null: handleRoot(Alpine3, el, effect); @@ -547,7 +546,7 @@ function combobox(Alpine2) { handleEmptyState(Alpine3, el); break; default: - console.error("invalid x-combobox value", value, "use input, button, option, options or leave mepty for root level instead"); + console.error("invalid x-rover value", value, "use input, button, option, options or leave mepty for root level instead"); break; } }).before("bind"); @@ -563,7 +562,7 @@ function combobox(Alpine2) { "x-ref": "__input", "x-model": "__searchQuery", "x-bind:id"() { - return this.$id("combobox-input"); + return this.$id("rover-input"); }, role: "combobox", tabindex: "0", @@ -577,7 +576,7 @@ function combobox(Alpine2) { Alpine2.bind(el, { "x-ref": "__options", "x-bind:id"() { - return this.$id("combobox-options"); + return this.$id("rover-options"); }, role: "listbox", "x-init"() { @@ -595,10 +594,10 @@ function combobox(Alpine2) { function handleOption(Alpine3, el) { Alpine3.bind(el, { "x-id"() { - return ["combobox-option"]; + return ["rover-option"]; }, "x-bind:id"() { - return this.$id("combobox-option"); + return this.$id("rover-option"); }, role: "option", "x-show"() { @@ -612,10 +611,10 @@ function combobox(Alpine2) { function handleOptionsGroup(Alpine3, el) { Alpine3.bind(el, { "x-id"() { - return ["combobox-options-group"]; + return ["rover-options-group"]; }, "x-bind:id"() { - return this.$id("combobox-options-group"); + return this.$id("rover-options-group"); }, role: "option", "x-show"() { @@ -627,7 +626,7 @@ function combobox(Alpine2) { Alpine3.bind(el, { "x-ref": "__button", "x-bind:id"() { - return this.$id("combobox-button"); + return this.$id("rover-button"); }, tabindex: "-1", "aria-haspopup": "true", @@ -648,7 +647,7 @@ function combobox(Alpine2) { function handleEmptyState(Alpine3, el) { Alpine3.bind(el, { "x-bind:id"() { - return this.$id("combobox-button"); + return this.$id("rover-button"); }, tabindex: "-1", "aria-haspopup": "true", @@ -670,7 +669,7 @@ function combobox(Alpine2) { } // builds/module.js -var module_default = combobox; +var module_default = rover; export { module_default as default }; diff --git a/index.html b/index.html index a1b14e1..b79494a 100644 --- a/index.html +++ b/index.html @@ -19,14 +19,14 @@
      -
      +
      - @@ -35,27 +35,27 @@ -
        -
      • +
      • tenjarine
      • -
      • +
      • Apple
      • -
      • +
      • Banana
      • -
      • +
      • Cherry
      • -
      • +
      • No result found
      • diff --git a/src/core/ComboboxCollection.ts b/src/core/RoverCollection.ts similarity index 99% rename from src/core/ComboboxCollection.ts rename to src/core/RoverCollection.ts index 99d7d0b..c08a425 100644 --- a/src/core/ComboboxCollection.ts +++ b/src/core/RoverCollection.ts @@ -1,7 +1,7 @@ import type { Item, Options, Pending, ActiveIndex, SearchIndex } from "src/types"; -export default class ComboboxCollection { +export default class RoverCollection { private items: Array = []; private itemsMap = new Map() diff --git a/src/factories/CreateComboboxInput.ts b/src/factories/CreateRoverInput.ts similarity index 100% rename from src/factories/CreateComboboxInput.ts rename to src/factories/CreateRoverInput.ts diff --git a/src/factories/CreateComboboxOption.ts b/src/factories/CreateRoverOption.ts similarity index 100% rename from src/factories/CreateComboboxOption.ts rename to src/factories/CreateRoverOption.ts diff --git a/src/factories/CreateComboboxRoot.ts b/src/factories/CreateRoverRoot.ts similarity index 98% rename from src/factories/CreateComboboxRoot.ts rename to src/factories/CreateRoverRoot.ts index 05f94d4..b79efd4 100644 --- a/src/factories/CreateComboboxRoot.ts +++ b/src/factories/CreateRoverRoot.ts @@ -1,4 +1,4 @@ -import ComboboxCollection from "../core/ComboboxCollection"; +import RoverCollection from "../core/RoverCollection"; import type { default as AlpineType } from "alpinejs"; import { ComboboxRootData } from "src/types"; @@ -7,7 +7,7 @@ export default function CreateComboboxRoot( { el, effect }: { el: AlpineType.ElementWithXAttributes, effect: AlpineType.DirectiveUtilities['effect'] } ): ComboboxRootData { - const collection = new ComboboxCollection(); + const collection = new RoverCollection(); type CompareByFn = (a: unknown, b: unknown) => boolean; diff --git a/src/index.ts b/src/index.ts index a2e1cdc..ec822da 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,10 @@ import { Alpine } from "alpinejs"; import type { default as AlpineType } from "alpinejs"; -import CreateComboboxInput from "./factories/CreateComboboxInput"; -import CreateComboboxOption from "./factories/CreateComboboxOption"; -import CreateComboboxRoot from "./factories/CreateComboboxRoot"; +import CreateRoverInput from "./factories/CreateRoverInput"; +import CreateRoverOption from "./factories/CreateRoverOption"; +import CreateRoverRoot from "./factories/CreateRoverRoot"; -type ComboboxValue = +type RoverValue = | null | 'input' | 'button' @@ -15,10 +15,10 @@ type ComboboxValue = | 'separator' | 'empty'; -export default function combobox(Alpine: Alpine): void { +export default function rover(Alpine: Alpine): void { - Alpine.directive('combobox', (el: AlpineType.ElementWithXAttributes, { value, modifiers }: AlpineType.DirectiveData, { Alpine, effect }: AlpineType.DirectiveUtilities) => { - switch (value as ComboboxValue) { + Alpine.directive('rover', (el: AlpineType.ElementWithXAttributes, { value, modifiers }: AlpineType.DirectiveData, { Alpine, effect }: AlpineType.DirectiveUtilities) => { + switch (value as RoverValue) { case null: handleRoot(Alpine, el, effect); break; case 'input': handleInput(Alpine, el); @@ -38,7 +38,7 @@ export default function combobox(Alpine: Alpine): void { case 'empty': handleEmptyState(Alpine, el); break; default: - console.error('invalid x-combobox value', value, 'use input, button, option, options or leave mepty for root level instead'); + console.error('invalid x-rover value', value, 'use input, button, option, options or leave mepty for root level instead'); break; } }).before('bind'); @@ -51,7 +51,7 @@ export default function combobox(Alpine: Alpine): void { Alpine.bind(el, { 'x-data'() { - return CreateComboboxRoot({ el, effect }) + return CreateRoverRoot({ el, effect }) } }) } @@ -63,13 +63,13 @@ export default function combobox(Alpine: Alpine): void { Alpine.bind(el, { 'x-ref': '__input', 'x-model': '__searchQuery', - 'x-bind:id'() { return this.$id('combobox-input') }, + 'x-bind:id'() { return this.$id('rover-input') }, 'role': 'combobox', 'tabindex': '0', 'aria-autocomplete': 'list', 'x-data'() { - return CreateComboboxInput(Alpine) + return CreateRoverInput(Alpine) } }) } @@ -78,7 +78,7 @@ export default function combobox(Alpine: Alpine): void { Alpine.bind(el, { 'x-ref': '__options', - 'x-bind:id'() { return this.$id('combobox-options') }, + 'x-bind:id'() { return this.$id('rover-options') }, 'role': 'listbox', 'x-init'() { @@ -98,15 +98,15 @@ export default function combobox(Alpine: Alpine): void { function handleOption(Alpine: Alpine, el: AlpineType.ElementWithXAttributes) { Alpine.bind(el, { - 'x-id'() { return ['combobox-option'] }, - 'x-bind:id'() { return this.$id('combobox-option') }, + 'x-id'() { return ['rover-option'] }, + 'x-bind:id'() { return this.$id('rover-option') }, 'role': 'option', 'x-show'() { return this.$data.__isVisible(this.$el.dataset.key); }, 'x-data'() { // @todo: move to constructor function here for memory gains - return CreateComboboxOption(Alpine, this.__nextId()); + return CreateRoverOption(Alpine, this.__nextId()); }, }); } @@ -114,8 +114,8 @@ export default function combobox(Alpine: Alpine): void { function handleOptionsGroup(Alpine: Alpine, el: AlpineType.ElementWithXAttributes) { Alpine.bind(el, { - 'x-id'() { return ['combobox-options-group'] }, - 'x-bind:id'() { return this.$id('combobox-options-group') }, + 'x-id'() { return ['rover-options-group'] }, + 'x-bind:id'() { return this.$id('rover-options-group') }, 'role': 'option', 'x-show'() { // we need to hide it if it's doesnt have any child @@ -128,7 +128,7 @@ export default function combobox(Alpine: Alpine): void { Alpine.bind(el, { 'x-ref': '__button', - 'x-bind:id'() { return this.$id('combobox-button') }, + 'x-bind:id'() { return this.$id('rover-button') }, 'tabindex': '-1', 'aria-haspopup': 'true', @@ -152,7 +152,7 @@ export default function combobox(Alpine: Alpine): void { function handleEmptyState(Alpine: Alpine, el: AlpineType.ElementWithXAttributes) { Alpine.bind(el, { - 'x-bind:id'() { return this.$id('combobox-button') }, + 'x-bind:id'() { return this.$id('rover-button') }, 'tabindex': '-1', 'aria-haspopup': 'true',