1import {rgElement} from '../rgElement/rg-element.js' 2export class rgSelectcontent extends rgElement { 3 4 $element = null; 5 $hiddenelement = null; 6 $listelement = null; 7 #trclass = null; 8 #rootnode = null; 9 #fetching = false; 10 #timer = null; 11 #oldValue = { 12 value: '', 13 displayText: '' 14 }; 15 #start = 0; 16 #limit = 10; 17 #listElementClick = false; 18 19 constructor() { 20 super(); 21 } 22 23 connectedCallback() { 24 this.setUpElement(); 25 !this.shadowRoot && this.attachShadow({mode: 'open'}); 26 this.style.display = 'block'; 27 28 this.#trclass = this.dataset.trtype; 29 this.#rootnode = this.dataset.root; 30 /*if (this.form && this.dataset.name) { 31 this.$element = this.form.querySelector('*[name="' + this.dataset.name + '"]'); 32 } else if (this.dataset.name) { 33 this.$element = document.querySelector('*[name="' + this.dataset.name + '"]'); 34 }*/ 35 if (!this.$element) { 36 let input = document.createElement('input'); 37 input.setAttribute('type', 'text'); 38 input.setAttribute('value', this.dataset.displayvalue); 39 input.setAttribute('name', 'livesearch' + this.dataset.name); 40 this.$element = this.shadowRoot.host.parentElement.insertBefore(input, this.shadowRoot.host); 41 } 42 const linkElem = document.createElement('link'); 43 linkElem.setAttribute('rel', 'stylesheet'); 44 linkElem.setAttribute('href', this.rootpath + '/css/rg-selectcontent.css'); 45 let hiddeninput = document.createElement('input'); 46 hiddeninput.setAttribute('type', 'hidden'); 47 hiddeninput.setAttribute('name', this.dataset.name); 48 hiddeninput.setAttribute('value', this.dataset.value); 49 this.$hiddenelement = this.shadowRoot.host.parentElement.insertBefore(hiddeninput, this.shadowRoot.host); 50 51 const adoptedCSSTag = document.createElement('style'); 52 adoptedCSSTag.innerHTML = this.copyStyles('.selectcontent'); 53 //append them 54 this.shadowRoot.append(linkElem, adoptedCSSTag); 55 this.$element.addEventListener('keydown', (ev) => { 56 if([ 57 'Enter', 58 'NumpadEnter', 59 'ArrowUp', 60 'ArrowDown' 61 ].includes(ev.code)) { 62 ev.preventDefault(); 63 return false; 64 } 65 }); 66 this.$element.addEventListener('keyup', this.#handleKey.bind(this)); 67 this.$element.addEventListener('click', function (ev) { 68 if (this.$listelement && this.$listelement.hasChildNodes()) { 69 this.$listelement.style.display = 'block'; 70 } 71 }.bind(this)); 72 this.#oldValue.displayText = this.$element.value; 73 this.#oldValue.value = this.$hiddenelement.value; 74 this.$element.addEventListener('blur', function (ev) { 75 if(this.#listElementClick) { 76 return; 77 } 78 if (this.$listelement) { 79 this.$listelement.style.display = 'none'; 80 } 81 if (this.$hiddenelement.value === '') { 82 this.$element.value = this.#oldValue.displayText; 83 this.$hiddenelement.value = this.#oldValue.value; 84 } 85 }.bind(this)); 86 if (typeof recon === 'object') { 87 recon.on('categorySelect', function (config) { 88 if (config.contentroot && config.contentroot[this.$hiddenelement.name]) { 89 //Evtl. per attribute data-root??? 90 this.#rootnode = config.contentroot[this.$hiddenelement.name]; 91 /*const event = new KeyboardEvent('keyup', { 92 bubbles: true, 93 cancelable: true 94 }); 95 this.#showLoadingResults(); 96 this.$element.dispatchEvent(event);*/ 97 } 98 }.bind(this)); 99 } 100 101 this.setupComplete(); 102 } 103 104 static get observedAttributes() { 105 return ['data-value', 'data-displayvalue', 'data-trtype']; 106 } 107 108 109 attributeChangedCallback(name, oldValue, newValue) { 110 if (this.$element) { 111 debugger; 112 } 113 } 114 115 #getUrl() { 116 let newValue = this.$element.value.trim(), 117 nodeFilter = this.#rootnode ? '&r=' + this.#rootnode : '', 118 match = newValue.match(/(^\d+)\s+/); 119 if (match) { 120 newValue = match[1]; 121 } 122 return `livesearch.js?_func=fndCnt&trcl=${this.#trclass}&viewmode=list&_trid=-1&start=${this.#start}&limit=${this.#limit}&filter=${newValue}${nodeFilter}`; 123 } 124 125 126 #handleKey(ev) { 127 switch (ev.key) { 128 case 'Escape': 129 if (this.$listelement) { 130 this.$listelement.style.display = 'none'; 131 } 132 break; 133 case 'Enter': 134 case 'NumpadEnter': 135 if (this.$listelement && this.$listelement.style.display === 'block') { 136 let activeListElement = this.$listelement.querySelector('.active'); 137 if (activeListElement) { 138 let event = new MouseEvent('pointerdown', { 139 bubbles: true, 140 cancelable: true 141 }); 142 activeListElement.dispatchEvent(event); 143 } 144 } 145 break; 146 case 'ArrowLeft': 147 case 'ArrowRight': 148 break; 149 case 'ArrowDown': 150 case 'ArrowUp': 151 if (this.$listelement) { 152 this.$listelement.style.display = 'block'; 153 let activeListElement = this.$listelement.querySelector('.active'), 154 nextElementToSelect, 155 event = new MouseEvent('mouseover', { 156 bubbles: true, 157 cancelable: true 158 }); 159 if (activeListElement) { 160 activeListElement.classList.remove('active'); 161 if (ev.key === 'ArrowDown') { 162 nextElementToSelect = activeListElement.nextElementSibling || activeListElement; 163 } else { 164 nextElementToSelect = activeListElement.previousElementSibling || activeListElement; 165 } 166 nextElementToSelect.dispatchEvent(event); 167 } else { 168 this.$listelement.firstChild.dispatchEvent(event); 169 } 170 } 171 break; 172 default: 173 this.$hiddenelement.value = ''; 174 this.#start = 0; 175 let newValue = this.$element.value.trim(); 176 if (!this.#fetching && newValue.length >= 3) { 177 if (this.#timer) { 178 clearTimeout(this.#timer); 179 } 180 this.#timer = setTimeout(function () { 181 this.#fetch(); 182 }.bind(this), 500); 183 } 184 break; 185 } 186 } 187 188 #fetch() { 189 this.#fetching = true; 190 this.#showLoadingResults(); 191 fetch(this.#getUrl(), { 192 method: 'GET',
193 mode: 'same-origin', 194 cache: 'no-cache', 195 headers: { 196 'X-Requested-With': 'XMLHttpRequest' 197 }, 198 redirect: 'follow' 199 }).then((response) => response.json()).then(function (data) { 200 this.#fetching = false; 201 this.#showResluts(data); 202 this.#listElementClick = false; 203 }.bind(this)).catch( (ex) => { 204 console.error(ex); 205 } ) 206 } 207 208 #createListElement() { 209 if (!this.$listelement) { 210 this.$listelement = document.createElement('div'); 211 this.$listelement.classList.add('rg-selectcontent'); 212 this.shadowRoot.append(this.$listelement); 213 let bounds = this.$element.getBoundingClientRect(); 214 this.$listelement.style.minWidth = bounds.width + 'px'; 215 } 216 } 217 218 #showLoadingResults() { 219 this.#clearResults(); 220 let loadingDiv = document.createElement('div'); 221 loadingDiv.classList.add('rg-selectcontent__loading'); 222 loadingDiv.innerHTML = '<div></div><div></div><div></div><div></div>'; 223 this.$listelement.append(loadingDiv); 224 } 225 226 #clearResults() { 227 this.#createListElement(); 228 let childNode = this.$listelement.firstChild; 229 while (childNode) { 230 this.$listelement.removeChild(childNode); 231 childNode = this.$listelement.firstChild 232 } 233 } 234 235 #showResluts(data) { 236 this.#createListElement(); 237 this.#clearResults(); 238 for (const [key, value] of Object.entries(data.contents)) { 239 let entry = document.createElement('div'); 240 entry.classList.add('rg-selectcontent__listelement'); 241 entry.innerHTML = value.title; 242 entry.setAttribute('data-id', value.id); 243 entry.setAttribute('data-title', value.title); 244 this.$listelement.append(entry); 245 entry.addEventListener('pointerdown', function (ev) { 246 this.$hiddenelement.value = ev.target.dataset.id; 247 this.$element.value = ev.target.dataset.title; 248 this.#oldValue.value = ev.target.dataset.id; 249 this.#oldValue.displayText = ev.target.dataset.title; 250 this.$listelement.style.display = 'none'; 251 }.bind(this)); 252 entry.addEventListener('mouseover', function (ev) { 253 this.classList.add('active'); 254 }); 255 entry.addEventListener('mouseout', function (ev) { 256 this.classList.remove('active'); 257 }); 258 } 259 let totalCount = Number(data.totalCount); 260 if (totalCount === 0) { 261 this.$listelement.style.display = 'none'; 262 return; 263 } 264 if (totalCount > 10) { 265 let pager = document.createElement('div'), 266 arrowLeft = document.createElement('div'), 267 arrowRight = document.createElement('div'); 268 arrowLeft.innerHTML = '<<'; 269 arrowRight.innerHTML = '>>'; 270 if (this.#start === 0) { 271 arrowLeft.classList.add('hidden'); 272 } 273 if (this.#start + this.#limit >= totalCount) { 274 arrowRight.classList.add('hidden'); 275 } 276 arrowLeft.addEventListener('pointerdown', function (ev) { 277 this.#listElementClick = true; 278 this.#start -= this.#limit; 279 if (this.#start < 0) { 280 this.#start = 0; 281 } 282 this.#fetch(); 283 }.bind(this)); 284 arrowRight.addEventListener('pointerdown', function (ev) { 285 this.#listElementClick = true; 286 this.#start += this.#limit; 287 this.#fetch(); 288 }.bind(this)); 289 pager.append(arrowLeft, arrowRight); 290 pager.classList.add('rg-selectcontent__pager') 291 this.$listelement.append(pager); 292 } 293 this.$listelement.style.display = 'block'; 294 } 295}
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.