Explorar el Código

feat: Insert emoji to editor on icon click

https://youtrack.weseek.co.jp/issue/GW-7648
- Insert emoji to editor on icon click
mudana hace 4 años
padre
commit
0501725698

+ 45 - 17
packages/app/src/components/PageEditor/CodeMirrorEditor.jsx

@@ -117,6 +117,8 @@ export default class CodeMirrorEditor extends AbstractEditor {
       additionalClassSet: new Set(),
       isEmojiPickerShown: false,
       emojiSearchText: null,
+      isInputtingEmoji: false,
+      searchEmojiTimeout: 0,
     };
 
     this.gridEditModal = React.createRef();
@@ -155,8 +157,10 @@ export default class CodeMirrorEditor extends AbstractEditor {
 
     this.foldDrawioSection = this.foldDrawioSection.bind(this);
     this.onSaveForDrawio = this.onSaveForDrawio.bind(this);
-    this.emojiPickerOpened = this.emojiPickerOpened.bind(this);
     this.toggleEmojiPicker = this.toggleEmojiPicker.bind(this);
+    this.getSearchCursor = this.getSearchCursor.bind(this);
+    this.addEmoji = this.addEmoji.bind(this);
+
   }
 
   init() {
@@ -664,16 +668,40 @@ export default class CodeMirrorEditor extends AbstractEditor {
     );
   }
 
+  getSearchCursor() {
+    const cm = this.getCodeMirror();
+    const pattern = /:[^:\s]+/;
+    const currentPos = cm.getCursor();
+    const sc = cm.getSearchCursor(pattern, currentPos, { multiline: false });
+    return sc;
+  }
+
+  addEmoji(emoji) {
+    const cm = this.getCodeMirror();
+    const currentPos = cm.getCursor();
+    const doc = cm.getDoc();
+    const sc = this.getSearchCursor();
+    if (sc.findPrevious() && this.state.isInputtingEmoji) {
+      sc.replace(emoji.colons, cm.getTokenAt(currentPos).string);
+      this.setState({ emojiSearchText: null });
+    }
+    else {
+      doc.replaceRange(emoji.colons, currentPos);
+    }
+
+  }
+
   toggleEmojiPicker() {
     this.setState({ isEmojiPickerShown: !this.state.isEmojiPickerShown });
   }
 
   renderEmojiPicker() {
+    const { emojiSearchText } = this.state;
     return this.state.isEmojiPickerShown
       ? (
         <div className="text-left">
           <div className="mb-2 d-none d-md-block">
-            <EmojiPicker close={this.toggleEmojiPicker} />
+            <EmojiPicker close={this.toggleEmojiPicker} selectEmoji={this.addEmoji} emojiSearchText={emojiSearchText} />
           </div>
         </div>
       )
@@ -779,15 +807,6 @@ export default class CodeMirrorEditor extends AbstractEditor {
     return range;
   }
 
-  emojiPickerOpened() {
-    // Get input element of emoji picker search
-    const input = document.querySelector('[id^="emoji-mart-search"]');
-    const valueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
-    // Set value to input of emoji picker search and trigger the search
-    valueSetter.call(input, this.state.emojiSearchText);
-    const event = new Event('input', { bubbles: true });
-    input.dispatchEvent(event);
-  }
 
   getNavbarItems() {
     return [
@@ -939,20 +958,29 @@ export default class CodeMirrorEditor extends AbstractEditor {
   }
 
   searchEmojiPattern() {
+    const sc = this.getSearchCursor();
     const cm = this.getCodeMirror();
-    const pattern = /:[^:\s]+/;
     const currentPos = cm.getCursor();
-    const sc = cm.getSearchCursor(pattern, currentPos, { multiline: false });
 
     if (sc.findPrevious()) {
       const isInputtingEmoji = (currentPos.line === sc.to().line && currentPos.ch === sc.to().ch);
+      this.setState({ isInputtingEmoji });
+      // current search cursor position
+      const pos = {
+        line: sc.to().line,
+        ch: sc.to().ch,
+      };
       // Add delay between search emoji and display emoji picker
-      const searchText = cm.getTokenAt(currentPos).string;
-      const searchValue = searchText.replace(':', '');
-      setTimeout(() => {
+      if (this.state.searchEmojiTimeout) {
+        clearTimeout(this.state.searchEmojiTimeout);
+      }
+      const timeout = setTimeout(() => {
+        const currentSearchText = sc.matches(true, pos).match[0];
+        const searchValue = currentSearchText.replace(':', '');
         this.setState({ isEmojiPickerShown: isInputtingEmoji });
         this.setState({ emojiSearchText: searchValue });
-      }, 1000);
+      }, 700);
+      this.setState({ searchEmojiTimeout: timeout });
       // return if it isn't inputting emoji
       if (!isInputtingEmoji) {
         return;

+ 8 - 1
packages/app/src/components/PageEditor/EmojiPicker.jsx

@@ -9,6 +9,7 @@ class EmojiPicker extends React.Component {
     super(props);
     this.emojiPicker = React.createRef();
     this.handleClickOutside = this.handleClickOutside.bind(this);
+    this.selectEmoji = this.selectEmoji.bind(this);
   }
 
   componentDidMount() {
@@ -25,13 +26,17 @@ class EmojiPicker extends React.Component {
     }
   }
 
+  selectEmoji(emoji) {
+    this.props.selectEmoji(emoji);
+  }
+
   render() {
     const { t } = this.props;
 
     return (
       <div className="overlay">
         <div ref={this.emojiPicker}>
-          <Picker set="apple" autoFocus />
+          <Picker set="apple" autoFocus onSelect={this.selectEmoji} />
         </div>
       </div>
     );
@@ -42,6 +47,8 @@ class EmojiPicker extends React.Component {
 EmojiPicker.propTypes = {
   t: PropTypes.func.isRequired,
   close: PropTypes.func,
+  selectEmoji: PropTypes.func,
+  emojiSearchText: PropTypes.string,
 };
 
 export default withTranslation()(EmojiPicker);