mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 03:18:24 +00:00 
			
		
		
		
	Replace tribute with text-expander-element for textarea (#23985)
The completion popup now behaves now much more as expected than before for the raw textarea: - You can press <kbd>Tab</kbd> or <kbd>Enter</kbd> once the completion popup is open to accept the selected item - The menu does not close automatically when moving the cursor - When you delete text, previously correct suggestions are shown again - If you delete all text until the opening char (`@` or `:`) after applying a suggestion, the popup reappears again - Menu UI has been improved <img width="278" alt="Screenshot 2023-04-07 at 19 43 42" src="https://user-images.githubusercontent.com/115237/230653601-d6517b9f-0988-445e-aa57-5ebfaf5039f3.png">
This commit is contained in:
		| @@ -1,4 +1,5 @@ | ||||
| import '@github/markdown-toolbar-element'; | ||||
| import '@github/text-expander-element'; | ||||
| import $ from 'jquery'; | ||||
| import {attachTribute} from '../tribute.js'; | ||||
| import {hideElem, showElem, autosize} from '../../utils/dom.js'; | ||||
| @@ -6,8 +7,10 @@ import {initEasyMDEImagePaste, initTextareaImagePaste} from './ImagePaste.js'; | ||||
| import {initMarkupContent} from '../../markup/content.js'; | ||||
| import {handleGlobalEnterQuickSubmit} from './QuickSubmit.js'; | ||||
| import {attachRefIssueContextPopup} from '../contextpopup.js'; | ||||
| import {emojiKeys, emojiString} from '../emoji.js'; | ||||
|  | ||||
| let elementIdCounter = 0; | ||||
| const maxExpanderMatches = 6; | ||||
|  | ||||
| /** | ||||
|  * validate if the given textarea is non-empty. | ||||
| @@ -40,13 +43,10 @@ class ComboMarkdownEditor { | ||||
|  | ||||
|   async init() { | ||||
|     this.prepareEasyMDEToolbarActions(); | ||||
|  | ||||
|     this.setupTab(); | ||||
|     this.setupDropzone(); | ||||
|  | ||||
|     this.setupTextarea(); | ||||
|  | ||||
|     await attachTribute(this.textarea, {mentions: true, emoji: true}); | ||||
|     this.setupExpander(); | ||||
|  | ||||
|     if (this.userPreferredEditor === 'easymde') { | ||||
|       await this.switchToEasyMDE(); | ||||
| @@ -83,6 +83,76 @@ class ComboMarkdownEditor { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   setupExpander() { | ||||
|     const expander = this.container.querySelector('text-expander'); | ||||
|     expander?.addEventListener('text-expander-change', ({detail: {key, provide, text}}) => { | ||||
|       if (key === ':') { | ||||
|         const matches = []; | ||||
|         for (const name of emojiKeys) { | ||||
|           if (name.includes(text)) { | ||||
|             matches.push(name); | ||||
|             if (matches.length >= maxExpanderMatches) break; | ||||
|           } | ||||
|         } | ||||
|         if (!matches.length) return provide({matched: false}); | ||||
|  | ||||
|         const ul = document.createElement('ul'); | ||||
|         ul.classList.add('suggestions'); | ||||
|         for (const name of matches) { | ||||
|           const emoji = emojiString(name); | ||||
|           const li = document.createElement('li'); | ||||
|           li.setAttribute('role', 'option'); | ||||
|           li.setAttribute('data-value', emoji); | ||||
|           li.textContent = `${emoji} ${name}`; | ||||
|           ul.append(li); | ||||
|         } | ||||
|  | ||||
|         provide({matched: true, fragment: ul}); | ||||
|       } else if (key === '@') { | ||||
|         const matches = []; | ||||
|         for (const obj of window.config.tributeValues) { | ||||
|           if (obj.key.includes(text)) { | ||||
|             matches.push(obj); | ||||
|             if (matches.length >= maxExpanderMatches) break; | ||||
|           } | ||||
|         } | ||||
|         if (!matches.length) return provide({matched: false}); | ||||
|  | ||||
|         const ul = document.createElement('ul'); | ||||
|         ul.classList.add('suggestions'); | ||||
|         for (const {value, name, fullname, avatar} of matches) { | ||||
|           const li = document.createElement('li'); | ||||
|           li.setAttribute('role', 'option'); | ||||
|           li.setAttribute('data-value', `${key}${value}`); | ||||
|  | ||||
|           const img = document.createElement('img'); | ||||
|           img.src = avatar; | ||||
|           li.append(img); | ||||
|  | ||||
|           const nameSpan = document.createElement('span'); | ||||
|           nameSpan.textContent = name; | ||||
|           li.append(nameSpan); | ||||
|  | ||||
|           if (fullname && fullname.toLowerCase() !== name) { | ||||
|             const fullnameSpan = document.createElement('span'); | ||||
|             fullnameSpan.classList.add('fullname'); | ||||
|             fullnameSpan.textContent = fullname; | ||||
|             li.append(fullnameSpan); | ||||
|           } | ||||
|  | ||||
|           ul.append(li); | ||||
|         } | ||||
|  | ||||
|         provide({matched: true, fragment: ul}); | ||||
|       } | ||||
|     }); | ||||
|     expander?.addEventListener('text-expander-value', ({detail}) => { | ||||
|       if (detail?.item) { | ||||
|         detail.value = detail.item.getAttribute('data-value'); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   setupDropzone() { | ||||
|     const dropzoneParentContainer = this.container.getAttribute('data-dropzone-parent-container'); | ||||
|     if (dropzoneParentContainer) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user