Преглед изворни кода

feat: Unify Emoji with EmojiPicker and convert to typescript

https://youtrack.weseek.co.jp/issue/GW-7652
- Unify Emoji to EmojiPicker
- Remove Emoji
- Convert EmojiPicker to typescript
- Change to EmojiPicker to functional component
- Adjust implementation of EmojiPicker
- Add additional props of editor in EmojiPicker
- Add translation function in EmojiPickerHelper
I Komang Mudana пре 4 година
родитељ
комит
21b48a4b42

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

@@ -696,15 +696,15 @@ export default class CodeMirrorEditor extends AbstractEditor {
   }
 
   renderEmojiPicker() {
-    const { emojiSearchText, isInputtingEmoji } = this.state;
+    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}
-              selectEmoji={isInputtingEmoji ? this.emojiPickerHelper.addEmojiOnSearch : this.emojiPickerHelper.addEmoji}
               emojiSearchText={emojiSearchText}
+              editor={this.getCodeMirror()}
             />
           </div>
         </div>

+ 0 - 101
packages/app/src/components/PageEditor/Emoji.jsx

@@ -1,101 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { Picker } from 'emoji-mart';
-import { withTranslation } from 'react-i18next';
-
-class Emoji extends React.Component {
-
-  constructor(props) {
-    super(props);
-    this.emojiPicker = React.createRef();
-    this.handleClickOutside = this.handleClickOutside.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);
-  }
-
-  componentWillUnmount() {
-    document.removeEventListener('mousedown', this.handleClickOutside);
-  }
-
-  handleClickOutside(event) {
-    if (this.emojiPicker && !this.emojiPicker.current.contains(event.target)) {
-      this.props.close();
-    }
-  }
-
-
-  render() {
-    const { t } = this.props;
-    const i18n = this.getTranslation();
-    return (
-      <div className="overlay">
-        <div ref={this.emojiPicker}>
-          <Picker set="apple" autoFocus onSelect={this.props.selectEmoji} i18n={i18n} title={t('emoji.title')} />
-        </div>
-      </div>
-    );
-  }
-
-}
-
-Emoji.propTypes = {
-  t: PropTypes.func.isRequired,
-  close: PropTypes.func,
-  selectEmoji: PropTypes.func,
-  emojiSearchText: PropTypes.string,
-};
-
-export default withTranslation()(Emoji);

+ 0 - 21
packages/app/src/components/PageEditor/EmojiPicker.jsx

@@ -1,21 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Emoji from './Emoji';
-
-function EmojiPicker(props) {
-  return (
-    <div className="overlay">
-      <div>
-        <Emoji close={props.close} selectEmoji={props.selectEmoji} emojiSearchText={props.emojiSearchText} />
-      </div>
-    </div>
-  );
-}
-
-EmojiPicker.propTypes = {
-  close: PropTypes.func,
-  selectEmoji: PropTypes.func,
-  emojiSearchText: PropTypes.string,
-};
-
-export default EmojiPicker;

+ 65 - 0
packages/app/src/components/PageEditor/EmojiPicker.tsx

@@ -0,0 +1,65 @@
+import React, { FC, useRef, useEffect } from 'react';
+import { Picker } from 'emoji-mart';
+import EmojiPickerHelper, { getEmojiTranslation } from './EmojiPickerHelper';
+
+type Props = {
+  close: () => void,
+  emojiSearchText: string,
+  editor: any
+}
+
+const EmojiPicker: FC<Props> = (props: Props) => {
+
+  const { close, emojiSearchText, editor } = props;
+
+  const emojiPickerContainer = useRef<HTMLDivElement>(null);
+
+  useEffect(() => {
+
+    if (emojiSearchText != null) {
+      // Get input element of emoji picker search
+      const input = document.querySelector('[id^="emoji-mart-search"]') as HTMLInputElement;
+      const valueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;
+      // Set value to input of emoji picker search and trigger the search
+      valueSetter?.call(input, emojiSearchText);
+      const event = new Event('input', { bubbles: true });
+      input.dispatchEvent(event);
+    }
+
+    function handleClickOutside(event) {
+      if (emojiPickerContainer.current && !emojiPickerContainer.current.contains(event.target)) {
+        close();
+      }
+    }
+    document.addEventListener('mousedown', handleClickOutside);
+    return () => {
+      // Unbind the event listener on clean up
+      document.removeEventListener('mousedown', handleClickOutside);
+    };
+
+
+  }, [emojiPickerContainer, close, emojiSearchText]);
+
+  const emojiPickerHelper = new EmojiPickerHelper(editor);
+
+  const selectEmoji = (emoji) => {
+    if (emojiSearchText !== null) {
+      emojiPickerHelper.addEmojiOnSearch(emoji);
+    }
+    else {
+      emojiPickerHelper.addEmoji(emoji);
+    }
+  };
+
+  const translation = getEmojiTranslation();
+
+  return (
+    <div className="overlay">
+      <div ref={emojiPickerContainer}>
+        <Picker set="apple" autoFocus onSelect={selectEmoji} i18n={translation} title={translation.title} />
+      </div>
+    </div>
+  );
+};
+
+export default EmojiPicker;

+ 42 - 1
packages/app/src/components/PageEditor/EmojiPickerHelper.ts

@@ -1,10 +1,11 @@
+import i18n from 'i18next';
+
 export default class EmojiPickerHelper {
 
 editor;
 
 pattern: RegExp;
 
-
 constructor(editor) {
   this.editor = editor;
   this.pattern = /:[^:\s]+/;
@@ -62,3 +63,43 @@ getEmoji = () => {
 }
 
 }
+
+
+export const getEmojiTranslation = () => {
+
+  const categories = {};
+  [
+    'search',
+    'recent',
+    'smileys',
+    'people',
+    'nature',
+    'foods',
+    'activity',
+    'places',
+    'objects',
+    'symbols',
+    'flags',
+    'custom',
+  ].forEach((category) => {
+    categories[category] = i18n.t(`emoji.categories.${category}`);
+  });
+
+  const skintones = {};
+  (Array.from(Array(6).keys())).forEach((tone) => {
+    skintones[tone + 1] = i18n.t(`emoji.skintones.${tone + 1}`);
+  });
+
+  const translation = {
+    search: i18n.t('emoji.search'),
+    clear: i18n.t('emoji.clear'),
+    notfound: i18n.t('emoji.notfound'),
+    skintext: i18n.t('emoji.skintext'),
+    categories,
+    categorieslabel: i18n.t('emoji.categorieslabel'),
+    skintones,
+    title: i18n.t('emoji.title'),
+  };
+
+  return translation;
+};