1/* ============================================================ 2 DESKTOP SEARCH OVERLAY (â¥1280px) 3 ÐболоÑка: оÑкÑÑÑие/закÑÑÑие, овеÑлей, ÑокÑÑ-ловÑÑка, Esc. 4 Ðогика Ñамого поиÑка â в components/search-core/search-core.js 5 (window.TagankaSearch), подклÑÑаеÑÑÑ ÐÐ ÑÑого Ñайла. 6 ============================================================ */ 7(function () { 8 'use strict'; 9 10 class SearchDesktop { 11 constructor() { 12 this._overlay = document.getElementById('searchDesktop'); 13 this._input = document.getElementById('searchDesktopInput'); 14 this._clear = document.getElementById('searchDesktopClear'); 15 this._state = document.getElementById('searchDesktopState'); 16 this._chips = document.getElementById('searchDesktopChips'); 17 this._results = document.getElementById('searchDesktopResults'); 18 this._list = document.getElementById('searchDesktopList'); 19 this._count = document.getElementById('searchDesktopCount'); 20 this._closeBtn = document.getElementById('searchDesktopClose'); 21 22 this._openBtns = []; /* вÑе кнопки-ÑÑиггеÑÑ (Ð¿Ð¾Ð»Ð½Ð°Ñ Ð¸ ÑокÑаÑÑÐ½Ð½Ð°Ñ Ñапки) */ 23 24 this._isOpen = false; 25 this._previousFocus = null; 26 27 this._menuOverlay = document.querySelector('.menu-overlay'); 28 this._boundKeydown = this._onKeydown.bind(this); 29 30 // ÐÑÑ Ð¼ÐµÑ Ð°Ð½Ð¸ÐºÐ° поиÑка â обÑий конÑÑоллеÑ. 31 // layout: 'columns' â ÑекÑÑовÑе гÑÑÐ¿Ð¿Ñ Ð² пÑавÑÑ ÐºÐ¾Ð»Ð¾Ð½ÐºÑ, оÑÑалÑнÑе в левÑÑ. 32 this._search = new window.TagankaSearch.SearchController({ 33 input: this._input, 34 clearBtn: this._clear, 35 chips: this._chips, 36 results: this._results, 37 list: this._list, 38 count: this._count, 39 layout: 'columns' 40 }); 41 42 this._init(); 43 } 44 45 _init() { 46 47 document.querySelectorAll('[data-search-trigger]').forEach(btn => { 48 this._openBtns.push(btn); 49 }); 50 51 const idBtn = document.getElementById('searchOpenBtnDesktop'); 52 if (idBtn && this._openBtns.indexOf(idBtn) === -1) { 53 this._openBtns.push(idBtn); 54 } 55 56 document.querySelectorAll('.header__item').forEach(btn => { 57 const text = btn.querySelector('.header__item-text'); 58 if (text && text.textContent.trim() === 'ÐоиÑк' && 59 this._openBtns.indexOf(btn) === -1) { 60 this._openBtns.push(btn); 61 } 62 }); 63 64 this._openBtns.forEach(btn => { 65 btn.setAttribute('aria-expanded', 'false'); 66 btn.setAttribute('aria-controls', 'searchDesktop'); 67 btn.addEventListener('click', () => this.open()); 68 }); 69 70 if (this._closeBtn) { 71 this._closeBtn.addEventListener('click', () => this.close()); 72 } 73 74 // Ðлик по овеÑÐ»ÐµÑ Ð·Ð°ÐºÑÑÐ²Ð°ÐµÑ Ð¿Ð¾Ð¸Ñк 75 if (this._menuOverlay) { 76 this._menuOverlay.addEventListener('click', () => { 77 if (this._isOpen) this.close(); 78 }); 79 } 80 } 81 82 // aria-expanded â на вÑÐµÑ ÑÑиггеÑÐ°Ñ , а не ÑолÑко на пеÑвом найденном 83 _setExpanded(value) { 84 this._openBtns.forEach(btn => btn.setAttribute('aria-expanded', String(value))); 85 } 86 87 open() { 88 if (!window.matchMedia('(min-width: 1280px)').matches) return; 89 90 this._previousFocus = document.activeElement; 91 this._isOpen = true; 92 93 this._overlay.classList.add('is-open'); 94 this._setExpanded(true); 95 if (this._menuOverlay) this._menuOverlay.classList.add('is-visible'); 96 97 if (this._state) this._state.hidden = false; 98 this._search.showChips(); 99 100 document.addEventListener('keydown', this._boundKeydown); 101 102 requestAnimationFrame(() => { 103 requestAnimationFrame(() => { 104 if (this._input) this._input.focus(); 105 }); 106 }); 107 } 108 109 close() { 110 this._isOpen = false; 111 this._overlay.classList.remove('is-open'); 112 113 this._setExpanded(false); 114 if (this._menuOverlay) this._menuOverlay.classList.remove('is-visible'); 115 116 document.removeEventListener('keydown', this._boundKeydown); 117 118 this._search.reset(); 119 if (this._state) this._state.hidden = true; 120 121 if (this._previousFocus) { 122 this._previousFocus.focus(); 123 this._previousFocus = null; 124 } 125 } 126 127 _onKeydown(e) { 128 if (e.key === 'Escape') { 129 this.close(); 130 return; 131 } 132 133 if (e.key === 'Tab') { 134 const focusable = [...this._overlay.querySelectorAll( 135 'button:not([disabled]), [href], input, [tabindex="0"]' 136 )].filter(el => el.offsetParent !== null); 137 138 if (!focusable.length) return; 139 const first = focusable[0];
140 const last = focusable[focusable.length - 1]; 141 142 if (e.shiftKey && document.activeElement === first) { 143 e.preventDefault(); 144 last.focus(); 145 } else if (!e.shiftKey && document.activeElement === last) { 146 e.preventDefault(); 147 first.focus(); 148 } 149 } 150 } 151 152 destroy() { 153 document.removeEventListener('keydown', this._boundKeydown); 154 this._search.destroy(); 155 } 156 } 157 158 document.addEventListener('DOMContentLoaded', () => { 159 if (document.getElementById('searchDesktop')) { 160 window._searchDesktop = new SearchDesktop(); 161 } 162 }); 163 164})();
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.