Просмотр исходного кода

feat: Insert emoji to editor on icon click

https://youtrack.weseek.co.jp/issue/GW-7648
- Set value to emoji picker search input
- Set focus on codemirror editor after selecting emoji
- Adjust styling for emoji icon cursor type
- Add translation (i18n) function to emoji picker
- Adjust emoji icon color on editor top bar
- Update translation for each locale
mudana 4 лет назад
Родитель
Сommit
8485390deb

+ 30 - 0
packages/app/resource/locales/en_US/translation.json

@@ -943,5 +943,35 @@
     "success_to_send_email": "Success to send email",
     "incorrect_token_or_expired_url": "The token is incorrect or the URL has expired. Please resend a password reset request via the link below.",
     "password_and_confirm_password_does_not_match": "Password and confirm password does not match"
+  },
+  "emoji" :{
+    "title": "Emoji Mart",
+    "search": "Search",
+    "clear": "Clear",
+    "notfound": "No Emoji Found",
+    "skintext": "Choose your default skin tone",
+    "categories": {
+      "search": "Search Results",
+      "recent": "Frequently Used",
+      "smileys": "Smileys & Emotion",
+      "people": "People & Body",
+      "nature": "Animals & Nature",
+      "foods": "Food & Drink",
+      "activity": "Activity",
+      "places": "Travel & Places",
+      "objects": "Objects",
+      "symbols": "Symbols",
+      "flags": "Flags",
+      "custom": "Custom"
+    },
+    "categorieslabel": "Emoji categories",
+    "skintones": {
+      "1": "Default Skin Tone",
+      "2": "Light Skin Tone",
+      "3": "Medium-Light Skin Tone",
+      "4": "Medium Skin Tone",
+      "5": "Medium-Dark Skin Tone",
+      "6": "Dark Skin Tone"
+    }
   }
 }

+ 30 - 0
packages/app/resource/locales/ja_JP/translation.json

@@ -936,5 +936,35 @@
     "success_to_send_email": "メールを送信しました",
     "incorrect_token_or_expired_url":"トークンが正しくないか、URLの有効期限が切れています。 以下のリンクからパスワードリセットリクエストを再送信してください。",
     "password_and_confirm_password_does_not_match": "パスワードと確認パスワードが一致しません"
+  },
+  "emoji" :{
+    "title": "Emoji Mart",
+    "search": "Search",
+    "clear": "Clear",
+    "notfound": "No Emoji Found",
+    "skintext": "Choose your default skin tone",
+    "categories": {
+      "search": "Search Results",
+      "recent": "Frequently Used",
+      "smileys": "Smileys & Emotion",
+      "people": "People & Body",
+      "nature": "Animals & Nature",
+      "foods": "Food & Drink",
+      "activity": "Activity",
+      "places": "Travel & Places",
+      "objects": "Objects",
+      "symbols": "Symbols",
+      "flags": "Flags",
+      "custom": "Custom"
+    },
+    "categorieslabel": "Emoji categories",
+    "skintones": {
+      "1": "Default Skin Tone",
+      "2": "Light Skin Tone",
+      "3": "Medium-Light Skin Tone",
+      "4": "Medium Skin Tone",
+      "5": "Medium-Dark Skin Tone",
+      "6": "Dark Skin Tone"
+    }
   }
 }

+ 30 - 0
packages/app/resource/locales/zh_CN/translation.json

@@ -946,5 +946,35 @@
     "success_to_send_email": "我发了一封电子邮件",
     "incorrect_token_or_expired_url":"令牌不正确或 URL 已过期。 请通过以下链接重新发送密码重置请求",
     "password_and_confirm_password_does_not_match": "密码和确认密码不匹配"
+  },
+  "emoji" :{
+    "title": "Emoji Mart",
+    "search": "Search",
+    "clear": "Clear",
+    "notfound": "No Emoji Found",
+    "skintext": "Choose your default skin tone",
+    "categories": {
+      "search": "Search Results",
+      "recent": "Frequently Used",
+      "smileys": "Smileys & Emotion",
+      "people": "People & Body",
+      "nature": "Animals & Nature",
+      "foods": "Food & Drink",
+      "activity": "Activity",
+      "places": "Travel & Places",
+      "objects": "Objects",
+      "symbols": "Symbols",
+      "flags": "Flags",
+      "custom": "Custom"
+    },
+    "categorieslabel": "Emoji categories",
+    "skintones": {
+      "1": "Default Skin Tone",
+      "2": "Light Skin Tone",
+      "3": "Medium-Light Skin Tone",
+      "4": "Medium Skin Tone",
+      "5": "Medium-Dark Skin Tone",
+      "6": "Dark Skin Tone"
+    }
   }
 }

+ 2 - 0
packages/app/src/components/PageEditor/CodeMirrorEditor.jsx

@@ -684,9 +684,11 @@ export default class CodeMirrorEditor extends AbstractEditor {
     if (sc.findPrevious() && this.state.isInputtingEmoji) {
       sc.replace(emoji.colons, cm.getTokenAt(currentPos).string);
       this.setState({ emojiSearchText: null });
+      cm.focus();
     }
     else {
       doc.replaceRange(emoji.colons, currentPos);
+      cm.focus();
     }
 
   }

+ 5 - 4
packages/app/src/components/PageEditor/EditorIcon.jsx

@@ -129,11 +129,12 @@ const EditorIcon = (props) => {
       return (
         <svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 30 30">
           <g transform="translate(-435 -392)">
-            <rect width="30" height="30" transform="translate(435 392)" fill="none" />
-            <path d="M8,1a7,7,0,1,0,7,7A7.008,7.008,0,0,0,8,1M8,0A8,8,0,1,1,0,8,8,8,0,0,1,8,0Z" transform="translate(442 399)" fill="#2a2d33" /><circle cx="1" cy="1" r="1" transform="translate(446 403)" fill="#2a2d33" />
-            <circle cx="1" cy="1" r="1" transform="translate(452 403)" fill="#2a2d33" />
+            <rect width="30" height="30" transform="translate(435 392)" fillOpacity="0" />
+            <path d="M8,1a7,7,0,1,0,7,7A7.008,7.008,0,0,0,8,1M8,0A8,8,0,1,1,0,8,8,8,0,0,1,8,0Z" transform="translate(442 399)" />
+            <circle cx="1" cy="1" r="1" transform="translate(446 403)" />
+            <circle cx="1" cy="1" r="1" transform="translate(452 403)" />
             <g transform="translate(445 406.5)">
-              <path d="M5,5.5a5.006,5.006,0,0,1-5-5,.5.5,0,1,1,1,0,4,4,0,0,0,8,0,.5.5,0,0,1,1,0A5.006,5.006,0,0,1,5,5.5Z" fill="#2a2d33" />
+              <path d="M5,5.5a5.006,5.006,0,0,1-5-5,.5.5,0,1,1,1,0,4,4,0,0,0,8,0,.5.5,0,0,1,1,0A5.006,5.006,0,0,1,5,5.5Z" />
             </g>
           </g>
         </svg>

+ 53 - 2
packages/app/src/components/PageEditor/EmojiPicker.jsx

@@ -12,6 +12,57 @@ class EmojiPicker extends React.Component {
     this.selectEmoji = this.selectEmoji.bind(this);
   }
 
+  componentDidUpdate(prevProps) {
+    if (this.props.emojiSearchText !== prevProps.emojiSearchText) {
+      if (this.props.emojiSearchText != null) {
+        // 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.props.emojiSearchText);
+        const event = new Event('input', { bubbles: true });
+        input.dispatchEvent(event);
+      }
+    }
+  }
+
+  getTranslation() {
+    const { t } = this.props;
+    const categories = {};
+    [
+      'search',
+      'recent',
+      'smileys',
+      'people',
+      'nature',
+      'foods',
+      'activity',
+      'places',
+      'objects',
+      'symbols',
+      'flags',
+      'custom',
+    ].forEach((category) => {
+      categories[category] = t(`emoji.categories.${category}`);
+    });
+
+    const skintones = {};
+    (Array.from(Array(6).keys())).forEach((tone) => {
+      skintones[tone + 1] = t(`emoji.skintones.${tone + 1}`);
+    });
+
+    const translation = {
+      search: t('emoji.search'),
+      clear: t('emoji.clear'),
+      notfound: t('emoji.notfound'),
+      skintext: t('emoji.skintext'),
+      categories,
+      categorieslabel: t('emoji.categorieslabel'),
+      skintones,
+    };
+    return translation;
+  }
+
   componentDidMount() {
     document.addEventListener('mousedown', this.handleClickOutside);
   }
@@ -32,11 +83,11 @@ class EmojiPicker extends React.Component {
 
   render() {
     const { t } = this.props;
-
+    const i18n = this.getTranslation();
     return (
       <div className="overlay">
         <div ref={this.emojiPicker}>
-          <Picker set="apple" autoFocus onSelect={this.selectEmoji} />
+          <Picker set="apple" autoFocus onSelect={this.selectEmoji} i18n={i18n} title={t('emoji.title')} />
         </div>
       </div>
     );

+ 6 - 0
packages/app/src/styles/_emoji-mart.scss

@@ -11,3 +11,9 @@ button.emoji-mart-anchors {
 :root button {
   overflow: hidden;
 }
+
+.emoji-mart-category {
+  .emoji-mart-emoji span {
+    cursor: pointer !important;
+  }
+}