summaryrefslogtreecommitdiffstats
path: root/web_src/js/features
diff options
context:
space:
mode:
authorsilverwind <me@silverwind.io>2023-04-09 18:18:45 +0200
committerGitHub <noreply@github.com>2023-04-09 12:18:45 -0400
commit9f6bc7c6f4f657c3245db7cea44c85454d5d8606 (patch)
treeb76705594765c611586d98e1976c90300438fbf9 /web_src/js/features
parent8bc8ca1953e9edfe1677378472c321b3d33875fd (diff)
downloadgitea-9f6bc7c6f4f657c3245db7cea44c85454d5d8606.tar.gz
gitea-9f6bc7c6f4f657c3245db7cea44c85454d5d8606.zip
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">
Diffstat (limited to 'web_src/js/features')
-rw-r--r--web_src/js/features/comp/ComboMarkdownEditor.js78
1 files changed, 74 insertions, 4 deletions
diff --git a/web_src/js/features/comp/ComboMarkdownEditor.js b/web_src/js/features/comp/ComboMarkdownEditor.js
index c1607a1da8..13b28da828 100644
--- a/web_src/js/features/comp/ComboMarkdownEditor.js
+++ b/web_src/js/features/comp/ComboMarkdownEditor.js
@@ -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) {