Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | 2x 2x 2x 8x 8x 8x 4x 2x 2x 5x 5x 5x 5x 5x 5x 5x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 2x | export function groupData (items) { const nogroup = []; const _groups = {}; items.forEach(item => { Iif (!item.group) return nogroup.push(item); _groups[item.group] = _groups[item.group] || { name: item.group, items: [] }; _groups[item.group].items.push(item); }); const groups = Object.values(_groups).filter(g => !!g.items.length); Iif (nogroup.length) groups.unshift({ items: nogroup }); return groups; } export function highlight (listEl) { requestAnimationFrame(() => { const selectedEl = listEl.querySelector('.selected'); Iif (!selectedEl) return; // going up let top = selectedEl.offsetTop; Iif (listEl.scrollTop > top) listEl.scrollTo({ top }); // going down else { top = selectedEl.offsetTop + selectedEl.offsetHeight - listEl.offsetHeight; Iif (listEl.scrollTop < top) listEl.scrollTo({ top }); } }); } // quick and instant recalc to minimise visual flyover of the dropdown across the screen export function quickPositionRecalc (listEl, inputEl) { const inputBox = inputEl.getBoundingClientRect(); listEl.style.top = (inputBox.top + inputBox.height + 3) + 'px'; listEl.style.left = inputBox.left + 'px'; } export function recalculateListPosition (listEl, inputEl, elevated) { Iif (elevated) quickPositionRecalc(listEl, inputEl); requestAnimationFrame(() => { Iif (!listEl || !listEl.style) return; const inputBox = inputEl.getBoundingClientRect(); Iif (elevated) { listEl.style.top = (inputBox.top + inputBox.height + 3) + 'px'; listEl.style.left = inputBox.left + 'px'; } else { listEl.style.top = (inputBox.height + 3) + 'px'; } listEl.style.minWidth = inputBox.width + 'px'; listEl.style.height = 'auto'; const listBox = listEl.getBoundingClientRect(); const listT = listBox.top; const listH = listBox.height; const winH = window.innerHeight; let maxH = 0; Iif (listT + listH + 10 > winH) { maxH = Math.max(winH - listT - 10, 100); listEl.style.height = maxH + 'px'; } Iif (listT + maxH + 10 > winH) { listEl.style.height = listBox.height + 'px'; if (elevated) listEl.style.top = (inputBox.top - listBox.height - 3) + 'px'; else listEl.style.top = (-listBox.height - 3) + 'px'; } }); } export function deepCopy (o) { return structuredClone(o); } export function fuzzy (hay = '', s = '') { if (s.length === 0) return true; if (hay.length === 0) return false; if (s.length > hay.length) return false; if (s === hay) return true; hay = hay.toLowerCase(); s = s.toLowerCase(); let n = -1; for (const l of s) if (!~(n = hay.indexOf(l, n + 1))) return false; return true; } export function emphasize (str, q) { if (!q) return str; str = '' + str; let idx = 0; const low = str.toLowerCase(); // string includes the whole query block if (low.includes(q)) return str.replace(new RegExp(`(${q})`, 'ig'), '<b>$1</b>'); // string includes the scattered query const stra = str.split(''); q = q.toLowerCase(); for (const l of q) { idx = low.indexOf(l, idx); const letter = stra[idx]; if (letter) { stra.splice(idx, 1, `<b>${letter}</b>`); idx += 1; } } return stra.join(''); } |