Ver Fonte

feat: implement emojiIndex

- Use emojiIndex from emoji-mart instead of create emoji data manually
- Implement and modify emoji autocomplete helper
- Implement emojiIndex to search emoji
- Create emoji list from search (by typing ":" / colons)  (on progress)
- Generate list of emoji to show in codemirror hint (on progress)
mudana há 4 anos atrás
pai
commit
1080631e5e

+ 1 - 63
packages/app/src/components/PageEditor/CodeMirrorEditor.jsx

@@ -171,7 +171,7 @@ export default class CodeMirrorEditor extends AbstractEditor {
 
   componentWillMount() {
     // if (this.props.emojiStrategy != null) {
-    // this.emojiAutoCompleteHelper = new EmojiAutoCompleteHelper(this.props.emojiStrategy);
+    this.emojiAutoCompleteHelper = new EmojiAutoCompleteHelper();
     this.setState({ isEnabledEmojiAutoComplete: true });
     // }
 
@@ -767,68 +767,6 @@ export default class CodeMirrorEditor extends AbstractEditor {
     return range;
   }
 
-  getEmojiList() {
-    const emojiList = [];
-    emojiData.forEach((key) => {
-      emojiList.push({
-        text: `${key}`,
-        render: (element) => {
-          element.innerHTML = `<img width="15" height="15" src="${emojiData[key]}" alt="icon" async></img> ${key}`;
-        },
-      });
-    });
-    return emojiList;
-  }
-
-  emojiComplete(cm) {
-    codemirror.showHint(cm, () => {
-
-      const cur = cm.getCursor(); const
-        token = cm.getTokenAt(cur);
-
-      const start = token.start; const end = cur.ch; const
-        word = token.string.slice(0, end - start);
-
-      let ch = cur.ch; const
-        line = cur.line;
-
-      let currentWord = token.string;
-
-      while (ch-- > -1) {
-
-        const t = cm.getTokenAt({ ch, line }).string;
-
-        if (t === ':') {
-          const emojiList = this.getEmojiList();
-          // eslint-disable-next-line no-loop-func
-          const filteredList = emojiList.filter((item) => {
-
-            return item.text.indexOf(currentWord) === 0;
-
-          });
-
-          if (filteredList.length >= 1) {
-
-            return {
-
-              list: filteredList,
-
-              from: codemirror.Pos(line, ch),
-
-              to: codemirror.Pos(line, end),
-
-            };
-
-          }
-
-        }
-
-        currentWord = t + currentWord;
-
-      }
-
-    }, { completeSingle: false });
-  }
 
   getNavbarItems() {
     return [

+ 133 - 142
packages/app/src/components/PageEditor/EmojiAutoCompleteHelper.js

@@ -1,149 +1,140 @@
-// import UpdateDisplayUtil from '~/client/util/codemirror/update-display-util.ext';
 
+import { emojiIndex } from 'emoji-mart';
+import UpdateDisplayUtil from '~/client/util/codemirror/update-display-util.ext';
 // This class will be deleted by GW-7652
 class EmojiAutoCompleteHelper {
 
-  // constructor(emojiStrategy) {
-  //   this.emojiStrategy = emojiStrategy;
-
-  //   this.emojiShortnameImageMap = {};
-
-  //   this.initEmojiImageMap = this.initEmojiImageMap.bind(this);
-  //   this.showHint = this.showHint.bind(this);
-
-  //   this.initEmojiImageMap();
-  // }
-
-  // initEmojiImageMap() {
-  //   for (const data of Object.values(this.emojiStrategy)) {
-  //     const shortname = data.shortname;
-  //     // add image tag
-  //     this.emojiShortnameImageMap[shortname] = emojione.shortnameToImage(shortname);
-  //   }
-  // }
-
-  // /**
-  //  * try to find emoji terms and show hint
-  //  * @param {any} editor An editor instance of CodeMirror
-  //  */
-  // showHint(editor) {
-  //   // see https://regex101.com/r/gy3i03/1
-  //   const pattern = /:[^:\s]+/;
-
-  //   const currentPos = editor.getCursor();
-  //   // find previous ':shortname'
-  //   const sc = editor.getSearchCursor(pattern, currentPos, { multiline: false });
-  //   if (sc.findPrevious()) {
-  //     const isInputtingEmoji = (currentPos.line === sc.to().line && currentPos.ch === sc.to().ch);
-  //     // return if it isn't inputting emoji
-  //     if (!isInputtingEmoji) {
-  //       return;
-  //     }
-  //   }
-  //   else {
-  //     return;
-  //   }
-
-  //   /*
-  //    * https://github.com/weseek/growi/issues/703 is caused
-  //    * because 'editor.display.viewOffset' is zero
-  //    *
-  //    * call stack:
-  //    *   1. https://github.com/codemirror/CodeMirror/blob/5.42.0/addon/hint/show-hint.js#L220
-  //    *   2. https://github.com/codemirror/CodeMirror/blob/5.42.0/src/edit/methods.js#L189
-  //    *   3. https://github.com/codemirror/CodeMirror/blob/5.42.0/src/measurement/position_measurement.js#L372
-  //    *   4. https://github.com/codemirror/CodeMirror/blob/5.42.0/src/measurement/position_measurement.js#L315
-  //    */
-  //   UpdateDisplayUtil.forceUpdateViewOffset(editor);
-
-  //   // see https://codemirror.net/doc/manual.html#addon_show-hint
-  //   editor.showHint({
-  //     completeSingle: false,
-  //     // closeOnUnfocus: false,  // for debug
-  //     hint: () => {
-  //       const matched = editor.getDoc().getRange(sc.from(), sc.to());
-  //       const term = matched.replace(':', ''); // remove ':' in the head
-
-  //       // get a list of shortnames
-  //       const shortnames = this.searchEmojiShortnames(term);
-  //       if (shortnames.length >= 1) {
-  //         return {
-  //           list: this.generateEmojiRenderer(shortnames),
-  //           from: sc.from(),
-  //           to: sc.to(),
-  //         };
-  //       }
-  //     },
-  //   });
-  // }
-
-  // /**
-  //  * see https://codemirror.net/doc/manual.html#addon_show-hint
-  //  * @param {string[]} emojiShortnames a list of shortname
-  //  */
-  // generateEmojiRenderer(emojiShortnames) {
-  //   return emojiShortnames.map((shortname) => {
-  //     return {
-  //       text: shortname,
-  //       className: 'crowi-emoji-autocomplete',
-  //       render: (element) => {
-  //         element.innerHTML = `<div class="img-container">${this.emojiShortnameImageMap[shortname]}</div>`
-  //           + `<span class="shortname-container">${shortname}</span>`;
-  //       },
-  //     };
-  //   });
-  // }
-
-  // /**
-  //  * transplanted from https://github.com/emojione/emojione/blob/master/examples/OTHER.md
-  //  * @param {string} term
-  //  * @returns {string[]} a list of shortname
-  //  */
-  // searchEmojiShortnames(term) {
-  //   const maxLength = 12;
-
-  //   const results1 = [];
-  //   const results2 = [];
-  //   const results3 = [];
-  //   const results4 = [];
-  //   const countLen1 = () => { return results1.length };
-  //   const countLen2 = () => { return countLen1() + results2.length };
-  //   const countLen3 = () => { return countLen2() + results3.length };
-  //   const countLen4 = () => { return countLen3() + results4.length };
-
-  //   // TODO performance tune
-  //   // when total length of all results is less than `maxLength`
-  //   for (const data of Object.values(this.emojiStrategy)) {
-  //     if (maxLength <= countLen1()) { break }
-  //     // prefix match to shortname
-  //     else if (data.shortname.indexOf(`:${term}`) > -1) {
-  //       results1.push(data.shortname);
-  //       continue;
-  //     }
-  //     else if (maxLength <= countLen2()) { continue }
-  //     // partial match to shortname
-  //     else if (data.shortname.indexOf(term) > -1) {
-  //       results2.push(data.shortname);
-  //       continue;
-  //     }
-  //     else if (maxLength <= countLen3()) { continue }
-  //     // partial match to elements of aliases
-  //     else if ((data.aliases != null) && data.aliases.find((elem) => { return elem.indexOf(term) > -1 })) {
-  //       results3.push(data.shortname);
-  //       continue;
-  //     }
-  //     else if (maxLength <= countLen4()) { continue }
-  //     // partial match to elements of keywords
-  //     else if ((data.keywords != null) && data.keywords.find((elem) => { return elem.indexOf(term) > -1 })) {
-  //       results4.push(data.shortname);
-  //     }
-  //   }
-
-  //   let results = results1.concat(results2).concat(results3).concat(results4);
-  //   results = results.slice(0, maxLength);
-
-  //   return results;
-  // }
+  constructor() {
+
+    this.showHint = this.showHint.bind(this);
+
+  }
+
+
+  /**
+   * try to find emoji terms and show hint
+   * @param {any} editor An editor instance of CodeMirror
+   */
+  showHint(editor) {
+    // see https://regex101.com/r/gy3i03/1
+    const pattern = /:[^:\s]+/;
+
+    const currentPos = editor.getCursor();
+    // find previous ':shortname'
+    const sc = editor.getSearchCursor(pattern, currentPos, { multiline: false });
+    if (sc.findPrevious()) {
+      const isInputtingEmoji = (currentPos.line === sc.to().line && currentPos.ch === sc.to().ch);
+      // return if it isn't inputting emoji
+      if (!isInputtingEmoji) {
+        return;
+      }
+    }
+    else {
+      return;
+    }
+
+    /*
+     * https://github.com/weseek/growi/issues/703 is caused
+     * because 'editor.display.viewOffset' is zero
+     *
+     * call stack:
+     *   1. https://github.com/codemirror/CodeMirror/blob/5.42.0/addon/hint/show-hint.js#L220
+     *   2. https://github.com/codemirror/CodeMirror/blob/5.42.0/src/edit/methods.js#L189
+     *   3. https://github.com/codemirror/CodeMirror/blob/5.42.0/src/measurement/position_measurement.js#L372
+     *   4. https://github.com/codemirror/CodeMirror/blob/5.42.0/src/measurement/position_measurement.js#L315
+     */
+    UpdateDisplayUtil.forceUpdateViewOffset(editor);
+
+    // see https://codemirror.net/doc/manual.html#addon_show-hint
+    editor.showHint({
+      completeSingle: false,
+      // closeOnUnfocus: false,  // for debug
+      hint: () => {
+        const matched = editor.getDoc().getRange(sc.from(), sc.to());
+        const term = matched.replace(':', ''); // remove ':' in the head
+
+        // get a list of shortnames
+        // TODO 13 Jan 2022
+        const shortnames = emojiIndex.search(term).map(emoji => emoji.colons);
+        if (shortnames.length >= 1) {
+          return {
+            list: this.generateEmojiRenderer(shortnames),
+            from: sc.from(),
+            to: sc.to(),
+          };
+        }
+      },
+    });
+  }
+
+  /**
+   * see https://codemirror.net/doc/manual.html#addon_show-hint
+   * @param {string[]} emojiShortnames a list of shortname
+   */
+  // TODO 13 Jan 2022
+  generateEmojiRenderer(emojiShortnames) {
+    return emojiIndex.search(emojiShortnames).map((emoji) => {
+      return {
+        text: emoji.colons,
+        className: 'crowi-emoji-autocomplete',
+        render: (element) => {
+          element.innerHTML = `<div class="img-container">${emoji.native}</div>`
+            + `<span class="shortname-container">${emoji.colons}</span>`;
+        },
+      };
+    });
+  }
+
+  /**
+   * transplanted from https://github.com/emojione/emojione/blob/master/examples/OTHER.md
+   * @param {string} term
+   * @returns {string[]} a list of shortname
+   */
+  searchEmojiShortnames(term) {
+    const maxLength = 12;
+
+    const results1 = [];
+    const results2 = [];
+    const results3 = [];
+    const results4 = [];
+    const countLen1 = () => { return results1.length };
+    const countLen2 = () => { return countLen1() + results2.length };
+    const countLen3 = () => { return countLen2() + results3.length };
+    const countLen4 = () => { return countLen3() + results4.length };
+
+    // TODO performance tune
+    // when total length of all results is less than `maxLength`
+    for (const data of Object.values(this.emojiStrategy)) {
+      if (maxLength <= countLen1()) { break }
+      // prefix match to shortname
+      else if (data.shortname.indexOf(`:${term}`) > -1) {
+        results1.push(data.shortname);
+        continue;
+      }
+      else if (maxLength <= countLen2()) { continue }
+      // partial match to shortname
+      else if (data.shortname.indexOf(term) > -1) {
+        results2.push(data.shortname);
+        continue;
+      }
+      else if (maxLength <= countLen3()) { continue }
+      // partial match to elements of aliases
+      else if ((data.aliases != null) && data.aliases.find((elem) => { return elem.indexOf(term) > -1 })) {
+        results3.push(data.shortname);
+        continue;
+      }
+      else if (maxLength <= countLen4()) { continue }
+      // partial match to elements of keywords
+      else if ((data.keywords != null) && data.keywords.find((elem) => { return elem.indexOf(term) > -1 })) {
+        results4.push(data.shortname);
+      }
+    }
+
+    let results = results1.concat(results2).concat(results3).concat(results4);
+    results = results.slice(0, maxLength);
+
+    return results;
+  }
 
 }