@vanillawc/wc-menu-wrapper
Version:
A web component that wraps HTML elements and forms a drop-down menu out of them.
2 lines (1 loc) • 4.31 kB
JavaScript
export class Custommenu extends HTMLElement{constructor(){super(),this.t=!1,this.position="bottom",this.direction="down",this.mode="click",this.closeSubmenusOnClosing=!1,this.closeSubmenusOnHeadingClick=!1,this.i=!1,this.s=[],this.initStateOpen=!1,this.closingDelay=500,this.item="item",this.heading="heading"}static get observedAttributes(){return["position","direction","mode","init-state-open","closing-delay","close-submenus-on-closing","close-submenus-on-heading-click","heading-class","item","heading"]}disconnectedCallback(){}h(t,i,s){if(t!==i&&t!==s)throw`Invalid attribute value: ${t}`}attributeChangedCallback(t,i,s){"position"===t?(this.h(s,"bottom","right"),this.position=s):"direction"===t?(this.h(s,"down","right"),this.direction=s):"mode"===t?(this.h(s,"click","hover"),this.mode=s):"init-state-open"===t?this.initStateOpen=!0:"closing-delay"===t?this.closingDelay=Number(s):"close-submenus-on-closing"===t?this.closeSubmenusOnClosing=!0:"close-submenus-on-heading-click"===t?this.closeSubmenusOnHeadingClick=!0:"heading-class"===t?this.headingClass=s:"item"===t?this.item=s:"heading"===t&&(this.heading=s)}connectedCallback(){setTimeout(()=>{this.o()},0)}l(t,i){t.style.display="flex",t.tabIndex=0,t.onblur=this.u,t.onblur=t.onblur.bind(this);let s=t.querySelectorAll("*").entries(),h=s.next();for(;!1===h.done;)h.value[1].onblur=this.u,h.value[1].onblur=h.value[1].onblur.bind(this),h=s.next();let e=t.querySelector("wc-menu-wrapper");e&&this.s.push(e),this.m(i)?this.g.insertBefore(t,this.g.children[i]):this.g.appendChild(t)}o(t){if(this.t)return;let i=this.querySelectorAll("."+this.item).entries(),s=i.next();if(!0===s.done){let i=this.querySelector("wc-menu-wrapper > *");if(null===i)return;if(t)throw"No valid menu items found!";const s=document.createElement("template");return s.innerHTML=i.menuContent,this.appendChild(s.content),void this.o(!0)}for(this.t=!0,this.g=this.appendChild(document.createElement("div")),this.g.style.display="none",this.g.style.position="absolute";!1===s.done;)s.value[1].parentElement.isSameNode(this)&&this.l(s.value[1]),s=i.next();this.p=this.querySelector("."+this.heading),this.p.tabIndex=0,this.p.style.display="inline-flex","click"===this.mode?(this.p.onclick=this._,this.p.onclick=this.p.onclick.bind(this),this.p.onblur=this.u,this.p.onblur=this.p.onblur.bind(this)):"hover"===this.mode&&(this.p.onmouseenter=this.v,this.p.onmouseenter=this.p.onmouseenter.bind(this),this.p.onmouseleave=this.C,this.p.onmouseleave=this.p.onmouseleave.bind(this),this.g.onmouseenter=this.v,this.g.onmouseenter=this.g.onmouseenter.bind(this),this.g.onmouseleave=this.C,this.g.onmouseleave=this.g.onmouseleave.bind(this)),this.p.onkeydown=this.k,this.p.onkeydown=this.p.onkeydown.bind(this),this.appendChild(this.p),this.appendChild(this.g),this.style.display="block",this.addEventListener("rootMenuClose",this.H,!0),this.addEventListener("menuClose",this.M,!1),this.initStateOpen&&this.T()}k(t){"Enter"===t.key&&this._()}M(t){t.stopPropagation(),this.N(this.closeSubmenusOnClosing),this.p.focus()}H(t){let i=!0;this.closeSubmenusOnClosing||(t.stopPropagation(),i=!1),this.N(i)}addItem(t,i){return!!this.t&&(this.l(t,i),!0)}deleteItem(t){if(this.t){let i=null;return void 0===t?i=this.g.childNodes.length>0?this.g.childNodes[this.g.childNodes.length-1]:null:"string"==typeof t?i=this.querySelector("#"+t):"number"==typeof t&&(i=this.g.childNodes[t]),null!==i&&this.g.removeChild(i),!0}return!1}u(){setTimeout(()=>{!function(t){let i=!1;t.I&&(i=t.I.contains(document.activeElement));t.contains(document.activeElement)||i||t.dispatchEvent(new CustomEvent("rootMenuClose"))}(this)},0)}N(t){this.i&&(this.g.style.display="none",this.i=!1,t&&this.s.forEach(t=>{t.N(!0)}),this.headingClass&&this.p.classList.remove(this.headingClass))}T(){this.i||(this.g.style.display="bottom"===this.position?"flex":"inline-flex",this.i=!0,this.s.forEach(t=>{t.A(void 0===this.I?this:this.I)}),this.headingClass&&this.p.classList.add(this.headingClass),this.g.style.flexDirection="right"===this.direction?"row":"column")}A(t){this.I=t}_(){this.i?this.N(this.closeSubmenusOnHeadingClick):this.T()}v(){clearTimeout(this.L),this.T()}C(){this.L=setTimeout(()=>{this.N()},this.closingDelay)}m(t){return!(isNaN(t)||"number"!=typeof t||t<0)}}customElements.define("wc-menu-wrapper",Custommenu);