UNPKG

@kmenu/react

Version:

React adapter for kmenu

2 lines 19.9 kB
'use strict';var react=require('react'),jsxRuntime=require('react/jsx-runtime');var $=Object.create;var F=Object.defineProperty;var G=Object.getOwnPropertyDescriptor;var q=Object.getOwnPropertyNames;var V=Object.getPrototypeOf,U=Object.prototype.hasOwnProperty;var W=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var z=(t,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of q(e))!U.call(t,s)&&s!==n&&F(t,s,{get:()=>e[s],enumerable:!(i=G(e,s))||i.enumerable});return t};var Y=(t,e,n)=>(n=t!=null?$(V(t)):{},z(!t||!t.__esModule?F(n,"default",{value:t,enumerable:true}):n,t));var B=W(E=>{var R=class{currentState;transitions=new Map;listeners=new Map;constructor(t="idle"){this.currentState=t;}defineTransition(t,e){this.transitions.set(t,e);}transition(t){let e=this.transitions.get(t);if(!e||!(Array.isArray(e.from)?e.from:[e.from]).includes(this.currentState)||e.guard&&!e.guard())return false;let n=this.currentState;return this.currentState=e.to,this.notifyListeners(n),this.notifyListeners(this.currentState),true}getState(){return this.currentState}onStateEnter(t,e){return this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(e),()=>{this.listeners.get(t)?.delete(e);}}notifyListeners(t){this.listeners.get(t)?.forEach(e=>e());}};function j(t){return t.toLowerCase().replace(/[^a-z0-9\s]/g,"").replace(/\s+/g,"-").replace(/^-+|-+$/g,"").substring(0,50)}var J=class{stateMachine;state;listeners=new Map;filter;keydownHandler;globalKeyHandler;inputElement;listElement;optionElements=new Map;destroyed=false;lastScrollTime=0;constructor(t={}){this.stateMachine=new R("idle"),this.state={open:false,input:"",activeId:void 0,activeIndex:-1,filtered:[],options:[],groups:new Map,menuStack:[],currentLevel:0,breadcrumbs:[],allOptions:[],currentOptions:[]},this.filter=t.filter||this.defaultFilter,this.setupStateMachine(),this.setupGlobalKeyboardShortcut(),t.onOpen&&this.on("open",t.onOpen),t.onClose&&this.on("close",t.onClose),t.onChange&&this.on("change",e=>e.type==="change"&&t.onChange(e.input)),t.onSelect&&this.on("select",e=>e.type==="select"&&t.onSelect(e.option));}setupStateMachine(){this.stateMachine.defineTransition("open",{from:"idle",to:"open"}),this.stateMachine.defineTransition("startNavigating",{from:["open","filtering"],to:"navigating"}),this.stateMachine.defineTransition("startFiltering",{from:["open","navigating"],to:"filtering"}),this.stateMachine.defineTransition("select",{from:["navigating","filtering"],to:"selected"}),this.stateMachine.defineTransition("close",{from:["open","navigating","filtering","selected"],to:"idle"}),this.stateMachine.onStateEnter("open",()=>{this.state.open=true,this.emit({type:"open"}),this.updateFiltered(),this.selectFirstAvailableOption(),requestAnimationFrame(()=>this.inputElement?.focus());}),this.stateMachine.onStateEnter("idle",()=>{this.state.open=false,this.state.input="",this.state.activeId=void 0,this.state.activeIndex=-1,this.emit({type:"close"});}),this.stateMachine.onStateEnter("selected",()=>{this.stateMachine.transition("close");});}setupGlobalKeyboardShortcut(){this.globalKeyHandler=t=>{(t.metaKey||t.ctrlKey)&&t.key==="k"&&(t.preventDefault(),this.toggle());},typeof window<"u"&&window.addEventListener("keydown",this.globalKeyHandler);}defaultFilter=(t,e)=>{if(!e)return t;let n=e.toLowerCase();return t.filter(i=>{if(i.disabled)return false;let s=i.label.toLowerCase().includes(n),a=i.keywords?.some(r=>r.toLowerCase().includes(n));return s||a})};open(){this.destroyed||this.stateMachine.transition("open");}close(){this.destroyed||this.stateMachine.transition("close");}toggle(){this.destroyed||(this.state.open?this.close():this.open());}setInput(t){this.destroyed||(this.state.input=t,this.stateMachine.transition("startFiltering"),this.updateFiltered(),this.emit({type:"change",input:t}));}registerOptions(t){if(this.destroyed)return;let e=this.processOptionsWithIds(t);this.state.options=[...e],this.state.groups.clear(),this.state.allOptions=this.flattenOptions(e),this.state.currentOptions=[...e],this.state.menuStack=[],this.state.currentLevel=0,this.state.breadcrumbs=[],this.rebuildGroups(),this.updateFiltered();}processOptionsWithIds(t){let e=new Set,n=i=>{let s=i.id;if(!s){let r=j(i.label);s=this.ensureUniqueId(r,e);}e.add(s);let a=i.children?i.children.map(r=>n(r)):void 0;return {...i,id:s,children:a}};return t.map(i=>n(i))}ensureUniqueId(t,e){if(!e.has(t))return t;let n=1,i=`${t}-${n}`;for(;e.has(i);)n++,i=`${t}-${n}`;return i}rebuildGroups(){this.state.groups.clear(),this.state.currentOptions.forEach(t=>{t.group&&(this.state.groups.has(t.group)||this.state.groups.set(t.group,[]),this.state.groups.get(t.group).push(t));});}reorderByGroups(t){if(this.state.groups.size===0)return t;let e=[],n=t.filter(s=>!s.group);e.push(...n);let i=[];for(let s of this.state.currentOptions)s.group&&!i.includes(s.group)&&i.push(s.group);for(let s of i){let a=t.filter(r=>r.group===s);e.push(...a);}return e}flattenOptions(t,e){let n=[];return t.forEach(i=>{if(e&&(i.parent=e),n.push(i),i.children&&i.children.length>0){let s=this.flattenOptions(i.children,i.id);n.push(...s);}}),n}setActiveByIndex(t,e){if(this.destroyed)return;let n=this.state.filtered[t];!n||n.disabled||(this.state.activeIndex=t,this.state.activeId=n.id,this.stateMachine.transition("startNavigating"),this.emit({type:"navigate",activeId:n.id,activeIndex:t}),this.updateAriaActiveDescendant(),this.scrollActiveIntoView(e));}scrollActiveIntoView(t){if(!this.state.activeId||!this.listElement)return;let e=this.optionElements.get(this.state.activeId);if(!e)return;let n=this.listElement.getBoundingClientRect(),i=e.getBoundingClientRect(),s=Date.now(),a=s-this.lastScrollTime<100?"auto":"smooth";this.lastScrollTime=s;let r=8;if(t==="up"){let o=n.top+n.height*.25;if(i.top<o){let u=i.height,d=this.listElement.scrollTop-u*2-r;this.listElement.scrollTo({top:Math.max(0,d),behavior:a});}}else if(t==="down"){if(i.bottom>n.bottom){let o=e.offsetTop,u=e.offsetHeight,d=this.listElement.clientHeight,h=o-d+u+r;this.listElement.scrollTo({top:Math.max(0,h),behavior:a});}}else if(i.top<n.top){let o=e.offsetTop-r;this.listElement.scrollTo({top:Math.max(0,o),behavior:a});}else if(i.bottom>n.bottom){let o=e.offsetTop,u=e.offsetHeight,d=this.listElement.clientHeight,h=o-d+u+r;this.listElement.scrollTo({top:Math.max(0,h),behavior:a});}}setActiveById(t){if(this.destroyed)return;let e=this.state.filtered.findIndex(n=>n.id===t);e>=0&&this.setActiveByIndex(e);}navigateUp(){if(this.destroyed)return;let t=this.state.activeIndex-1;for(;t>=0&&this.state.filtered[t]?.disabled;)t--;t>=0&&this.setActiveByIndex(t,"up");}navigateDown(){if(this.destroyed)return;let t=this.state.activeIndex+1;for(;t<this.state.filtered.length&&this.state.filtered[t]?.disabled;)t++;t<this.state.filtered.length&&this.setActiveByIndex(t,"down");}on(t,e){return this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(e),()=>{this.listeners.get(t)?.delete(e);}}getState(){return {...this.state}}emit(t){this.listeners.get(t.type)?.forEach(e=>e(t));}updateFiltered(){let t;this.state.input.trim()?t=this.state.currentLevel===0?this.state.allOptions:this.state.currentOptions:t=this.state.currentOptions;let e=this.filter(t,this.state.input);this.state.filtered=this.reorderByGroups(e),this.state.activeId&&!this.state.filtered.some(n=>n.id===this.state.activeId)?(this.state.activeId=void 0,this.state.activeIndex=-1,this.selectFirstAvailableOption()):this.state.activeId?this.state.activeIndex=this.state.filtered.findIndex(n=>n.id===this.state.activeId):this.state.open&&this.selectFirstAvailableOption();}selectFirstAvailableOption(){let t=this.state.filtered.findIndex(e=>!e.disabled);t>=0&&this.setActiveByIndex(t);}enterSubmenu(t){this.destroyed||!t.children||t.children.length===0||(this.state.menuStack.push(t.id),this.state.currentLevel++,this.state.breadcrumbs.push({id:t.id,label:t.group||t.label}),this.state.currentOptions=[...t.children],this.rebuildGroups(),this.state.input="",this.state.activeId=void 0,this.state.activeIndex=-1,this.updateFiltered(),this.selectFirstAvailableOption(),this.emit({type:"submenu",option:t,level:this.state.currentLevel}),this.emit({type:"change",input:this.state.input}));}goBack(){if(this.destroyed||this.state.menuStack.length===0)return false;if(this.state.menuStack.pop(),this.state.currentLevel--,this.state.breadcrumbs.pop(),this.state.currentLevel===0)this.state.currentOptions=[...this.state.options];else {let t=this.state.menuStack[this.state.menuStack.length-1];if(t){let e=this.findOptionById(t);e&&e.children?this.state.currentOptions=[...e.children]:(this.state.currentOptions=[...this.state.options],this.state.menuStack=[],this.state.currentLevel=0,this.state.breadcrumbs=[]);}else this.state.currentOptions=[...this.state.options],this.state.menuStack=[],this.state.currentLevel=0,this.state.breadcrumbs=[];}return this.rebuildGroups(),this.state.input="",this.state.activeId=void 0,this.state.activeIndex=-1,this.updateFiltered(),this.emit({type:"change",input:this.state.input}),this.emit({type:"back",level:this.state.currentLevel}),true}findOptionById(t){return this.state.allOptions.find(e=>e.id===t)}updateAriaActiveDescendant(){this.inputElement&&this.state.activeId?this.inputElement.setAttribute("aria-activedescendant",`kmenu-option-${this.state.activeId}`):this.inputElement&&this.inputElement.removeAttribute("aria-activedescendant");}getComboboxProps(){return {role:"combobox","aria-expanded":this.state.open,"aria-haspopup":"listbox","aria-controls":"kmenu-listbox"}}getInputProps(){return {ref:t=>{t&&(this.inputElement=t);},role:"combobox","aria-autocomplete":"list","aria-expanded":this.state.open,"aria-controls":"kmenu-listbox","aria-activedescendant":this.state.activeId?`kmenu-option-${this.state.activeId}`:void 0,value:this.state.input,onInput:t=>{let e=t.target;this.setInput(e.value);},onKeyDown:t=>{this.handleKeyDown(t);}}}getListboxProps(){return {ref:t=>{t&&(this.listElement=t);},id:"kmenu-listbox",role:"listbox","aria-label":"Commands",tabIndex:-1}}getOptionProps(t){let e=this.state.allOptions.find(s=>s.id===t),n=this.state.activeId===t,i=this.state.filtered.findIndex(s=>s.id===t);return {ref:s=>{s?this.optionElements.set(t,s):this.optionElements.delete(t);},id:`kmenu-option-${t}`,role:"option","aria-selected":n,"aria-disabled":e?.disabled,tabIndex:-1,onClick:()=>{e&&!e.disabled&&(this.state.activeId=t,this.state.activeIndex=i,this.selectActive());},onMouseEnter:()=>{e&&!e.disabled&&i>=0&&this.setActiveByIndex(i);}}}handleKeyDown(t){switch(t.key){case "ArrowUp":t.preventDefault(),this.navigateUp();break;case "ArrowDown":t.preventDefault(),this.navigateDown();break;case "Tab":t.preventDefault(),t.shiftKey?this.navigateUp():this.navigateDown();break;case "Enter":t.preventDefault(),this.selectActive();break;case "Escape":t.preventDefault(),this.close();break;case "Backspace":this.state.input===""&&this.state.currentLevel>0&&(t.preventDefault(),this.goBack());break;case "Home":if(t.ctrlKey||t.metaKey){t.preventDefault();let e=this.state.filtered.findIndex(n=>!n.disabled);e>=0&&this.setActiveByIndex(e);}break;case "End":if(t.ctrlKey||t.metaKey){t.preventDefault();for(let e=this.state.filtered.length-1;e>=0;e--)if(!this.state.filtered[e]?.disabled){this.setActiveByIndex(e);break}}break}}selectActive(){if(this.destroyed||!this.state.activeId)return;let t=this.state.filtered.find(e=>e.id===this.state.activeId);if(!(!t||t.disabled)){if(t.children&&t.children.length>0)this.enterSubmenu(t);else if(this.emit({type:"select",option:t}),this.stateMachine.transition("select"),t.action){let e=t.action();if(e instanceof Promise)e.catch(console.error);else if(Array.isArray(e)){t.children=e,this.enterSubmenu(t);return}}}}destroy(){this.destroyed||(this.destroyed=true,this.close(),this.globalKeyHandler&&typeof window<"u"&&window.removeEventListener("keydown",this.globalKeyHandler),this.listeners.clear(),this.optionElements.clear(),this.inputElement=void 0,this.listElement=void 0);}},Q=(t,e)=>{if(!e)return t;let n=e.toLowerCase();return t.filter(i=>{if(i.disabled)return false;let s=i.label.toLowerCase().includes(n),a=i.keywords?.some(r=>r.toLowerCase().includes(n));return s||a})},X=(t,e)=>{if(!e)return t;let n=e.toLowerCase();return t.filter(i=>{if(i.disabled)return false;let s=i.label.toLowerCase().startsWith(n),a=i.keywords?.some(r=>r.toLowerCase().startsWith(n));return s||a})},Z=(t,e)=>{if(!e)return t;let n=e.toLowerCase().split("");return t.filter(i=>!i.disabled).map(i=>{let s=`${i.label} ${i.keywords?.join(" ")||""}`.toLowerCase(),a=0,r=-1,o=0;for(let u of n){let d=s.indexOf(u,r+1);if(d===-1)return null;d===r+1?(o++,a+=10*o):(o=0,a+=1),(d===0||s[d-1]===" ")&&(a+=5),r=d;}return a-=s.length*.1,{option:i,score:a}}).filter(i=>i!==null).sort((i,s)=>s.score-i.score).map(i=>i.option)};function _(t,e){return (n,i)=>{if(!i)return n;try{let s=new RegExp(t.replace("{{query}}",i),e);return n.filter(a=>{if(a.disabled)return !1;let r=`${a.label} ${a.keywords?.join(" ")||""}`;return s.test(r)})}catch{return []}}}E.CommandCore=J;E.StateMachine=R;E.createRegexFilter=_;E.fuzzyFilter=Z;E.simpleFilter=Q;E.startsWithFilter=X;});var N=Y(B());var P=react.createContext(null);function S(){let t=react.useContext(P);if(!t)throw new Error("useCommand must be used within a <Command> component");return t}var it=react.forwardRef(function({children:e,value:n,onValueChange:i,open:s,onOpenChange:a,options:r=[],filter:o,onSelect:u,shouldFilter:d=true,...h},L){let l=react.useRef(null),y=react.useRef(null),[g,v]=react.useState(()=>({open:s??false,input:n??"",activeId:void 0,activeIndex:-1,filtered:r,options:r,menuStack:[],currentLevel:0,breadcrumbs:[]}));react.useEffect(()=>{let f={filter:d?o:void 0,onOpen:()=>{v(c=>({...c,open:true})),a?.(true);},onClose:()=>{v(c=>({...c,open:false,input:"",activeId:void 0,activeIndex:-1})),a?.(false);},onChange:c=>{v(m=>({...m,input:c})),i?.(c);},onSelect:c=>{u?.(c);}},p=new N.CommandCore(f);l.current=p;let M=p.on("navigate",c=>{c.type==="navigate"&&v(m=>({...m,activeId:c.activeId,activeIndex:c.activeIndex}));}),O=p.on("submenu",c=>{if(c.type==="submenu"){let m=p.getState();v(A=>({...A,menuStack:[...m.menuStack],currentLevel:m.currentLevel,breadcrumbs:[...m.breadcrumbs],input:m.input,activeId:m.activeId,activeIndex:m.activeIndex,filtered:[...m.filtered],options:[...m.currentOptions]}));}}),w=p.on("back",c=>{if(c.type==="back"){let m=p.getState();v(A=>({...A,menuStack:[...m.menuStack],currentLevel:m.currentLevel,breadcrumbs:[...m.breadcrumbs],input:m.input,activeId:m.activeId,activeIndex:m.activeIndex,filtered:[...m.filtered],options:[...m.currentOptions]}));}});p.registerOptions(r);let b=p.getState();return v({open:b.open,input:b.input,activeId:b.activeId,activeIndex:b.activeIndex,filtered:b.filtered,options:b.currentOptions,menuStack:b.menuStack,currentLevel:b.currentLevel,breadcrumbs:b.breadcrumbs}),s!==void 0&&s&&p.open(),()=>{M(),O(),w(),p.destroy(),l.current=null;}},[]),react.useEffect(()=>{l.current&&(l.current.registerOptions(r),v(f=>({...f,options:r,filtered:l.current.getState().filtered})));},[r]),react.useEffect(()=>{l.current&&n!==void 0&&n!==g.input&&l.current.setInput(n);},[n]),react.useEffect(()=>{l.current&&s!==void 0&&(s&&!g.open?l.current.open():!s&&g.open&&l.current.close());},[s]),react.useEffect(()=>{if(!l.current)return;let f=()=>{let c=l.current.getState();v({open:c.open,input:c.input,activeId:c.activeId,activeIndex:c.activeIndex,filtered:c.filtered,options:c.currentOptions,menuStack:c.menuStack,currentLevel:c.currentLevel,breadcrumbs:c.breadcrumbs});},p=l.current.on("change",f),M=l.current.on("navigate",f),O=l.current.on("submenu",f),w=l.current.on("back",f),b=l.current.on("open",f);return ()=>{p(),M(),O(),w(),b();}},[l.current]),react.useEffect(()=>{if(!g.open||!y.current)return;let f=y.current,p=f.querySelectorAll('input, button, [tabindex]:not([tabindex="-1"]), [contenteditable]'),M=p[0];p[p.length-1];let w=b=>{b.key;};return M&&M.focus(),f.addEventListener("keydown",w),()=>{f.removeEventListener("keydown",w);}},[g.open]),react.useImperativeHandle(L,()=>({command:l.current}));let I=l.current?.getComboboxProps()||{};return jsxRuntime.jsx(P.Provider,{value:{command:l.current,state:g},children:jsxRuntime.jsx("div",{ref:y,...h,...I,children:e})})}),st=react.forwardRef(function({value:e,onValueChange:n,...i},s){let{command:a,state:r}=S(),o=react.useRef(null);react.useImperativeHandle(s,()=>o.current);let u=a?.getInputProps()||{},{ref:d,...h}=u;react.useEffect(()=>{o.current&&d&&d(o.current);},[d]);let L=react.useCallback(l=>{let y=l.currentTarget.value;a?.setInput(y),n?.(y);},[a,n]);return jsxRuntime.jsx("input",{ref:o,"data-kmenu-input":true,...h,...i,value:e??r.input,onInput:L,onKeyDown:l=>{h.onKeyDown?.(l),i.onKeyDown?.(l);}})}),rt=react.forwardRef(function({children:e,indicatorOffsetY:n=0,...i},s){let{command:a,state:r}=S(),o=react.useRef(null),[u,d]=react.useState({opacity:0,transform:"translateY(0px)",width:"0px",height:"0px"});react.useImperativeHandle(s,()=>o.current);let h=a?.getListboxProps()||{},{ref:L,...l}=h;return react.useEffect(()=>{o.current&&L&&L(o.current);},[L]),react.useEffect(()=>{if(!o.current||!r.activeId){d(I=>({...I,opacity:0}));return}let y=()=>{if(!o.current||!r.activeId){d(p=>({...p,opacity:0}));return}let I=o.current.querySelector(`[data-command-option-id="${r.activeId}"]`);if(I||(I=o.current.querySelector('[data-active="true"]')),!I){d(p=>({...p,opacity:0}));return}let f=I.offsetTop+n;d({opacity:1,transform:`translateY(${f}px)`,width:`${I.offsetWidth}px`,height:`${I.offsetHeight}px`});},g,v;return g=requestAnimationFrame(()=>{v=requestAnimationFrame(y);}),()=>{g&&cancelAnimationFrame(g),v&&cancelAnimationFrame(v);}},[r.activeId,r.filtered,e,n]),jsxRuntime.jsxs("div",{ref:o,...l,...i,children:[jsxRuntime.jsx("div",{className:"command-active-indicator",style:u}),e]})}),at=react.forwardRef(function({value:e,disabled:n,children:i,...s},a){let{command:r,state:o}=S(),u=react.useRef(null),d=o.activeId===e.id,h=o.filtered.some(g=>g.id===e.id),L=r?.getOptionProps(e.id)||{},{ref:l,...y}=L;return react.useImperativeHandle(a,()=>u.current),react.useEffect(()=>{u.current&&l&&h&&l(u.current);},[l,h]),h?jsxRuntime.jsx("div",{ref:u,...y,...s,"data-active":d,"data-disabled":n||e.disabled,"data-command-option-id":e.id,children:i||e.label}):null}),ot=react.forwardRef(function({heading:e,children:n,...i},s){return jsxRuntime.jsxs("div",{ref:s,role:"group",...i,children:[e&&jsxRuntime.jsx("div",{role:"presentation","aria-hidden":"true",children:e}),n]})}),lt=react.forwardRef(function({children:e,...n},i){let{state:s}=S();return s.filtered.length>0?null:jsxRuntime.jsx("div",{ref:i,role:"presentation",...n,children:e||"No results found."})}),dt=react.forwardRef(function({children:e,...n},i){let{command:s,state:a}=S(),r=o=>{if(!s)return;let u=o+1,d=s.getState().currentLevel;for(;d>u&&s.goBack();)d=s.getState().currentLevel;requestAnimationFrame(()=>{document.querySelector("[data-kmenu-input]")?.focus();});};return jsxRuntime.jsxs("div",{ref:i,role:"navigation","aria-label":"Menu breadcrumbs",...n,children:[jsxRuntime.jsx("span",{onClick:()=>r(-1),style:{cursor:"pointer"},children:"Home"}),a.breadcrumbs.map((o,u)=>jsxRuntime.jsx("span",{children:jsxRuntime.jsx("span",{onClick:()=>r(u),style:{cursor:"pointer"},children:o.label})},o.id))]})}),ct=react.forwardRef(function({children:e,...n},i){return jsxRuntime.jsx("div",{ref:i,role:"status","aria-live":"polite","aria-busy":"true",...n,children:e||"Loading..."})}),ut=react.forwardRef(function(e,n){return jsxRuntime.jsx("div",{ref:n,role:"separator","aria-orientation":"horizontal",...e})});exports.Command=it;exports.CommandBreadcrumbs=dt;exports.CommandEmpty=lt;exports.CommandGroup=ot;exports.CommandInput=st;exports.CommandList=rt;exports.CommandLoading=ct;exports.CommandOption=at;exports.CommandSeparator=ut;exports.useCommand=S;//# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map