1class AtomicDropdown extends HTMLElement { 2 constructor() { 3 super(); 4 this.attachShadow({mode: "open"}); 5 this.setAttribute("closed",""); 6 } 7 8 get clickElement(){ 9 return this.querySelector("[slot='click-element']"); 10 } 11 12 get animation(){ 13 return this.getAttribute("animation"); 14 } 15 16 openDropdown(){ 17 if(this.isOpen){ 18 return; 19 }else{ 20 this.removeAttribute("closed"); 21 this.removeAttribute("closing"); 22 this.setAttribute("opening",""); 23 } 24 } 25 26 closeDropdown(e){ 27 // prevend bubble up events to close dropdown 28 if(e){ 29 let related = e.relatedTarget; 30 31 while (related && related !== this && related !== document) { 32 related = related.parentNode; 33 } 34 if (related === this) { 35 return; 36 } 37 } 38 39 if(this.isClosed){ 40 return; 41 } 42 this.removeAttribute("opened"); 43 this.removeAttribute("opening"); 44 this.setAttribute("closing",""); 45 } 46 47 get group(){ 48 return this.getAttribute("group"); 49 } 50 51 get isOpenedByHover(){ 52 return this.hasAttribute("openOnHover"); 53 } 54 55 get isOpen(){ 56 return this.hasAttribute("opened"); 57 } 58 59 get isClosed(){ 60 return this.hasAttribute("closed"); 61 } 62 63 get getLinkInClickElement(){ 64 return this.clickElement.querySelector("a"); 65 } 66 67 get slideContent(){ 68 return this.shadowRoot.querySelector(".slide-content"); 69 } 70 71 connectedCallback() { 72 this.render(); 73 this._setClickEvent(); 74 this._setHoverEvent(); 75 this._setTransitionEvents(); 76 77 const closeModalBtn = this.shadowRoot.querySelector('#close-modal-btn'); 78 79 closeModalBtn.addEventListener('click', (e) => { 80 e.stopPropagation(); 81 this.closeDropdown(); 82 }); 83 } 84 85 disconnectedCallback() { 86 // this.clickElement.removeEventListener('click', this.handleClickElementClick); 87 // this.slideContent.removeEventListener('transitionend', this.handleTransitionEnd); 88 } 89 90 handleClickElementClick(event){ 91 this.disableDefaultClickBehaviour(); 92 event.stopPropagation(); 93 if(this.isOpen){ 94 this.closeDropdown(event); 95 document.removeEventListener('click', this.boundHandleClickOutside); 96 } else { 97 this.openDropdown(); 98 this.boundHandleClickOutside = this.handleClickOutside.bind(this); 99 setTimeout(() => { // Dies stellt sicher, dass der Listener nicht sofort ausgelöst wird 100 document.addEventListener('click', this.boundHandleClickOutside, false); 101 }, 0); 102 } 103 if(this.group){ 104 this.closeGroupMembers(); 105 } 106 }; 107 108 handleClickOutside(event){ 109 if (this.contains(event.target) === false) { 110 this.closeDropdown(event); 111 document.removeEventListener('click', this.boundHandleClickOutside); 112 } 113 }; 114 115 handleTransitionEnd(){ 116 if(this.hasAttribute("opening")){ 117 this.removeAttribute("opening"); 118 this.removeAttribute("closing"); 119 this.setAttribute("opened",""); 120 } else if(this.hasAttribute("closing")){ 121 this.removeAttribute("closing"); 122 this.removeAttribute("opening"); 123 this.setAttribute("closed",""); 124 } 125 }; 126 127 disableDefaultClickBehaviour(){ 128 if(this.clickElement.tagName === "A"){ 129 this.clickElement.addEventListener('click', (e) => { 130 e.preventDefault(); 131 e.stopPropagation(); 132 }); 133 } 134 if(!this.getLinkInClickElement) return; 135 this.getLinkInClickElement.style.pointerEvents = "none"; 136 this.getLinkInClickElement.addEventListener('click', (e) => { 137 e.preventDefault(); 138 e.stopPropagation(); 139 }); 140 } 141 142 closeGroupMembers(){ 143 if(!this.group) return; 144 const members = document.querySelectorAll(`atomic-dropdown[group="${this.group}"]`); 145 if(members.length === 0) return;
146 members.forEach((member)=>{ 147 if(member !== this){ 148 member.closeDropdown(); 149 } 150 }) 151 }; 152 153 _setClickEvent(){ 154 if(this.isOpenedByHover) return; 155 this.disableDefaultClickBehaviour.bind(this)(); 156 this.clickElement.addEventListener('click', this.handleClickElementClick.bind(this), false); 157 } 158 159 _setTransitionEvents(){ 160 this.slideContent.addEventListener('transitionend', this.handleTransitionEnd.bind(this)); 161 } 162 163 _setHoverEvent() { 164 if(!this.isOpenedByHover) return; 165 this.addEventListener('mouseover', this.openDropdown.bind(this)); 166 this.addEventListener('mouseout', this.closeDropdown.bind(this)); 167 } 168 169 _activateClickOutside(){ 170 document.addEventListener('click', this.handleClickOutside.bind(this), false); 171 } 172 173 render() { 174 this.shadowRoot.innerHTML = ` 175 <style> 176 :host{ 177 position: relative; 178 display:inline-block; 179 width:var(--atomic-dropdown-width, auto); 180 z-index:100; 181 } 182 183 ::slotted([slot="click-element"]){ 184 cursor:pointer; 185 user-select: none; 186 z-index: 10; 187 position: relative; 188 } 189 190 .slide-wrapper{ 191 position: fixed; 192 top: -50%; 193 left: -50%; 194 width: 500px; 195 max-width: 90vw; 196 transform: translate(-50%, -50%); 197 z-index: 999999; 198 } 199 .slide-wrapper-header { 200 background-color: #45B0B9; 201 height: 50px; 202 border-top-left-radius: 5px; 203 border-top-right-radius: 5px; 204 display: flex; 205 justify-content: flex-end; 206 207 } 208 #close-modal-btn { 209 padding-right: 10px; 210 border: none; 211 background: transparent; 212 color: #454545; 213 font-size: 24px; 214 font-weight: bold; 215 } 216 :host([open-left]) .slide-wrapper{ 217 right:0; 218 } 219 220 :host([closed]) .slide-wrapper{ 221 overflow: hidden; 222 } 223 :host([opening]) .slide-wrapper, 224 :host([opened]) .slide-wrapper{ 225 overflow: visible; 226 z-index: 200; 227 top: 50%; 228 left: 50%; 229 } 230 :host([opening]), 231 :host([opened]){ 232 z-index: 200; 233 } 234 235 236 .slide{ 237 transition: max-height 0.3s ease-in-out, opacity 0.3s ease-in-out; 238 position: absolute; 239 top:0; 240 box-sizing: border-box; 241 display:block; 242 overflow: hidden; 243 max-height: 0px; 244 opacity: 0; 245 padding: 0; 246 margin-left: 0; 247 padding-top: var(--atomic-dropdown-space-top, 10px); 248 } 249 :host([openleft]) .slide{ 250 right:calc(var(--atomic-dropdown-outer-padding, 30px) * -1); 251 } 252 253 :host([modal]) .slide{ 254 position: relative; 255 max-height: none; 256 overflow: visible; 257 } 258 259 :host([modal]) .dropdown-content { 260 width: auto; 261 padding: 20px; 262 } 263 264 .slide-content{ 265 transition-timing-function: var(--atomic-dropdown-animation-function,ease-in-out); 266 transition:var(--atomic-dropdown-animation-time,0.5s); 267 transform: translateY(calc(-100% - var(--atomic-dropdown-space-top, 10px))); 268 width:var(--atomic-dropdown-width, auto); 269 box-sizing: border-box; 270 background:var(--atomic-dropdown-content-background, white); 271 padding: 0; 272 border:var(--atomic-dropdown-border, none); 273 margin-bottom: var(--atomic-dropdown-content-margin-bottom, 10px); 274 border-top-left-radius: 5px; 275 border-top-right-radius: 5px; 276 } 277 278 :host([animation="ease-in-out"]) .slide{ 279 transition: max-height 1s ease-in-out; 280 } 281 :host([opening]) .slide-content, 282 :host([opened]) .slide-content{ 283 transform: translateY(0%); 284 } 285 286 :host([animation="ease-in-out"][opened]) .slide{ 287 288 } 289 :host([relative][opening]) .slide-wrapper, 290 :host([relative][opened]) .slide-wrapper{ 291 transform: translateY(0%); 292 } 293 :host([relative][opening]) .slide, 294 :host([relative][opened]) .slide{ 295 display: inline-block; 296 } 297 :host([opening]) .slide, 298 :host([opened]) .slide{ 299 max-height: 100vh !important; 300 opacity: 1; 301 } 302 :host([relative][opening]) .slide-wrapper, 303 :host([relative][opening]) .slide, 304 :host([relative][opening]) .slide-content, 305 :host([relative][opened]) .slide-wrapper, 306 :host([relative][opened]) .slide, 307 :host([relative][opened]) .slide-content{ 308 position: relative; 309 } 310 ::slotted(ul){ 311 list-style: none; 312 margin:0; 313 } 314 </style> 315 <slot name="click-element" part="click-element"></slot> 316 317 <div class="slide-wrapper"> 318 319 320 <div class="slide" part="list"> 321 322 <div class="slide-content" part="list-inner"> 323 324 <div class="slide-wrapper-header"> 325 <button id="close-modal-btn" type="button" aria-label="Close">â</button> 326 </div> 327 <slot></slot></div> 328 </div> 329 </div> 330 `; 331 } 332} 333 334if (customElements.get('atomic-dropdown') === undefined) { 335 customElements.define('atomic-dropdown', AtomicDropdown); 336}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.