code.gitea.io/gitea@v1.22.3/web_src/js/features/comp/TextExpander.js (about) 1 import {matchEmoji, matchMention} from '../../utils/match.js'; 2 import {emojiString} from '../emoji.js'; 3 4 export function initTextExpander(expander) { 5 expander?.addEventListener('text-expander-change', ({detail: {key, provide, text}}) => { 6 if (key === ':') { 7 const matches = matchEmoji(text); 8 if (!matches.length) return provide({matched: false}); 9 10 const ul = document.createElement('ul'); 11 ul.classList.add('suggestions'); 12 for (const name of matches) { 13 const emoji = emojiString(name); 14 const li = document.createElement('li'); 15 li.setAttribute('role', 'option'); 16 li.setAttribute('data-value', emoji); 17 li.textContent = `${emoji} ${name}`; 18 ul.append(li); 19 } 20 21 provide({matched: true, fragment: ul}); 22 } else if (key === '@') { 23 const matches = matchMention(text); 24 if (!matches.length) return provide({matched: false}); 25 26 const ul = document.createElement('ul'); 27 ul.classList.add('suggestions'); 28 for (const {value, name, fullname, avatar} of matches) { 29 const li = document.createElement('li'); 30 li.setAttribute('role', 'option'); 31 li.setAttribute('data-value', `${key}${value}`); 32 33 const img = document.createElement('img'); 34 img.src = avatar; 35 li.append(img); 36 37 const nameSpan = document.createElement('span'); 38 nameSpan.textContent = name; 39 li.append(nameSpan); 40 41 if (fullname && fullname.toLowerCase() !== name) { 42 const fullnameSpan = document.createElement('span'); 43 fullnameSpan.classList.add('fullname'); 44 fullnameSpan.textContent = fullname; 45 li.append(fullnameSpan); 46 } 47 48 ul.append(li); 49 } 50 51 provide({matched: true, fragment: ul}); 52 } 53 }); 54 expander?.addEventListener('text-expander-value', ({detail}) => { 55 if (detail?.item) { 56 // add a space after @mentions as it's likely the user wants one 57 const suffix = detail.key === '@' ? ' ' : ''; 58 detail.value = `${detail.item.getAttribute('data-value')}${suffix}`; 59 } 60 }); 61 }