|
1 | 1 | import '@github/markdown-toolbar-element';
|
| 2 | +import '@github/text-expander-element'; |
2 | 3 | import $ from 'jquery';
|
3 | 4 | import {attachTribute} from '../tribute.js';
|
4 | 5 | import {hideElem, showElem, autosize} from '../../utils/dom.js';
|
5 | 6 | import {initEasyMDEImagePaste, initTextareaImagePaste} from './ImagePaste.js';
|
6 | 7 | import {initMarkupContent} from '../../markup/content.js';
|
7 | 8 | import {handleGlobalEnterQuickSubmit} from './QuickSubmit.js';
|
8 | 9 | import {attachRefIssueContextPopup} from '../contextpopup.js';
|
| 10 | +import {emojiKeys, emojiString} from '../emoji.js'; |
9 | 11 |
|
10 | 12 | let elementIdCounter = 0;
|
| 13 | +const maxExpanderMatches = 6; |
11 | 14 |
|
12 | 15 | /**
|
13 | 16 | * validate if the given textarea is non-empty.
|
@@ -40,13 +43,10 @@ class ComboMarkdownEditor {
|
40 | 43 |
|
41 | 44 | async init() {
|
42 | 45 | this.prepareEasyMDEToolbarActions();
|
43 |
| - |
44 | 46 | this.setupTab();
|
45 | 47 | this.setupDropzone();
|
46 |
| - |
47 | 48 | this.setupTextarea();
|
48 |
| - |
49 |
| - await attachTribute(this.textarea, {mentions: true, emoji: true}); |
| 49 | + this.setupExpander(); |
50 | 50 |
|
51 | 51 | if (this.userPreferredEditor === 'easymde') {
|
52 | 52 | await this.switchToEasyMDE();
|
@@ -83,6 +83,76 @@ class ComboMarkdownEditor {
|
83 | 83 | }
|
84 | 84 | }
|
85 | 85 |
|
| 86 | + setupExpander() { |
| 87 | + const expander = this.container.querySelector('text-expander'); |
| 88 | + expander?.addEventListener('text-expander-change', ({detail: {key, provide, text}}) => { |
| 89 | + if (key === ':') { |
| 90 | + const matches = []; |
| 91 | + for (const name of emojiKeys) { |
| 92 | + if (name.includes(text)) { |
| 93 | + matches.push(name); |
| 94 | + if (matches.length >= maxExpanderMatches) break; |
| 95 | + } |
| 96 | + } |
| 97 | + if (!matches.length) return provide({matched: false}); |
| 98 | + |
| 99 | + const ul = document.createElement('ul'); |
| 100 | + ul.classList.add('suggestions'); |
| 101 | + for (const name of matches) { |
| 102 | + const emoji = emojiString(name); |
| 103 | + const li = document.createElement('li'); |
| 104 | + li.setAttribute('role', 'option'); |
| 105 | + li.setAttribute('data-value', emoji); |
| 106 | + li.textContent = `${emoji} ${name}`; |
| 107 | + ul.append(li); |
| 108 | + } |
| 109 | + |
| 110 | + provide({matched: true, fragment: ul}); |
| 111 | + } else if (key === '@') { |
| 112 | + const matches = []; |
| 113 | + for (const obj of window.config.tributeValues) { |
| 114 | + if (obj.key.includes(text)) { |
| 115 | + matches.push(obj); |
| 116 | + if (matches.length >= maxExpanderMatches) break; |
| 117 | + } |
| 118 | + } |
| 119 | + if (!matches.length) return provide({matched: false}); |
| 120 | + |
| 121 | + const ul = document.createElement('ul'); |
| 122 | + ul.classList.add('suggestions'); |
| 123 | + for (const {value, name, fullname, avatar} of matches) { |
| 124 | + const li = document.createElement('li'); |
| 125 | + li.setAttribute('role', 'option'); |
| 126 | + li.setAttribute('data-value', `${key}${value}`); |
| 127 | + |
| 128 | + const img = document.createElement('img'); |
| 129 | + img.src = avatar; |
| 130 | + li.append(img); |
| 131 | + |
| 132 | + const nameSpan = document.createElement('span'); |
| 133 | + nameSpan.textContent = name; |
| 134 | + li.append(nameSpan); |
| 135 | + |
| 136 | + if (fullname && fullname.toLowerCase() !== name) { |
| 137 | + const fullnameSpan = document.createElement('span'); |
| 138 | + fullnameSpan.classList.add('fullname'); |
| 139 | + fullnameSpan.textContent = fullname; |
| 140 | + li.append(fullnameSpan); |
| 141 | + } |
| 142 | + |
| 143 | + ul.append(li); |
| 144 | + } |
| 145 | + |
| 146 | + provide({matched: true, fragment: ul}); |
| 147 | + } |
| 148 | + }); |
| 149 | + expander?.addEventListener('text-expander-value', ({detail}) => { |
| 150 | + if (detail?.item) { |
| 151 | + detail.value = detail.item.getAttribute('data-value'); |
| 152 | + } |
| 153 | + }); |
| 154 | + } |
| 155 | + |
86 | 156 | setupDropzone() {
|
87 | 157 | const dropzoneParentContainer = this.container.getAttribute('data-dropzone-parent-container');
|
88 | 158 | if (dropzoneParentContainer) {
|
|
0 commit comments