From d3688af567308e06d00138788ee3ad9cd349c7d9 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Thu, 17 Aug 2023 15:58:12 +0300 Subject: [PATCH 1/7] Submit page 2.0: Added emoji picker --- package.json | 2 + .../decks/deck-settings/decks-settings.tsx | 4 +- .../deck-threads-form-emoji-picker.tsx | 8 +- .../components/editor-toolbar/index.tsx | 18 +- .../components/emoji-picker/index-old.tsx | 201 +++++++++++++ src/common/components/emoji-picker/index.tsx | 265 +++++------------- src/common/core/react-query.ts | 4 +- yarn.lock | 15 + 8 files changed, 309 insertions(+), 208 deletions(-) create mode 100644 src/common/components/emoji-picker/index-old.tsx diff --git a/package.json b/package.json index f8bdfbd280d..d99938bcb58 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "dependencies": { "@ecency/render-helper": "^2.2.25", "@ecency/render-helper-amp": "^1.1.0", + "@emoji-mart/data": "^1.1.2", "@firebase/analytics": "^0.8.0", "@firebase/app": "^0.7.28", "@firebase/messaging": "^0.9.16", @@ -34,6 +35,7 @@ "currency-symbol-map": "^4.0.4", "debounce": "^1.2.1", "diff-match-patch": "^1.0.5", + "emoji-mart": "^5.5.2", "express": "^4.17.3", "history": "^4.7.2", "hive-uri": "^0.2.3", diff --git a/src/common/components/decks/deck-settings/decks-settings.tsx b/src/common/components/decks/deck-settings/decks-settings.tsx index 407ea36d42c..2875b1b7118 100644 --- a/src/common/components/decks/deck-settings/decks-settings.tsx +++ b/src/common/components/decks/deck-settings/decks-settings.tsx @@ -2,7 +2,7 @@ import { Alert, Button, Form, InputGroup, Modal } from "react-bootstrap"; import React, { useContext, useEffect, useState } from "react"; import "./_decks-settings.scss"; import { DeckGrid } from "../types"; -import EmojiPicker from "../../emoji-picker"; +import { EmojiPicker } from "../../emoji-picker"; import { deleteForeverSvg, emoticonHappyOutlineSvg } from "../../../img/svg"; import ClickAwayListener from "../../clickaway-listener"; import * as uuid from "uuid"; @@ -120,7 +120,7 @@ export const DecksSettings = ({ show, setShow, deck }: Props) => { {showEmoji ? ( setShowEmoji(false)}> { + onSelect={(value) => { setIcon(value); setShowEmoji(false); }} diff --git a/src/common/components/decks/deck-threads-form/deck-threads-form-emoji-picker.tsx b/src/common/components/decks/deck-threads-form/deck-threads-form-emoji-picker.tsx index e3711ead1c3..db60fe09d31 100644 --- a/src/common/components/decks/deck-threads-form/deck-threads-form-emoji-picker.tsx +++ b/src/common/components/decks/deck-threads-form/deck-threads-form-emoji-picker.tsx @@ -3,7 +3,7 @@ import { emojiIconSvg } from "../icons"; import { _t } from "../../../i18n"; import { PopperDropdown } from "../../popper-dropdown"; import Tooltip from "../../tooltip"; -import EmojiPicker from "../../emoji-picker"; +import { EmojiPicker } from "../../emoji-picker"; interface Props { onPick: (v: string) => void; @@ -15,11 +15,7 @@ export const DeckThreadsFormEmojiPicker = ({ onPick }: Props) => {
- { - onPick(value); - }} - /> + onPick(value)} />
diff --git a/src/common/components/editor-toolbar/index.tsx b/src/common/components/editor-toolbar/index.tsx index c144bbf0b64..26a7e83099b 100644 --- a/src/common/components/editor-toolbar/index.tsx +++ b/src/common/components/editor-toolbar/index.tsx @@ -7,7 +7,7 @@ import { User } from "../../store/users/types"; import { Global } from "../../store/global/types"; import Tooltip from "../tooltip"; -import EmojiPicker from "../emoji-picker"; +import { EmojiPicker } from "../emoji-picker"; import GifPicker from "../gif-picker"; import Gallery from "../gallery"; import Fragments from "../fragments"; @@ -65,6 +65,7 @@ interface State { shGif: boolean; showVideoUpload: boolean; showVideoGallery: boolean; + isMounted: boolean; } export const detectEvent = (eventType: string) => { @@ -86,7 +87,8 @@ export class EditorToolbar extends Component { mobileImage: false, shGif: false, showVideoUpload: false, - showVideoGallery: false + showVideoGallery: false, + isMounted: false }; holder = React.createRef(); @@ -155,6 +157,9 @@ export class EditorToolbar extends Component { window.addEventListener("blockquote", this.quote); window.addEventListener("image", this.toggleImage); window.addEventListener("customToolbarEvent", this.handleCustomToolbarEvent); + this.setState({ + isMounted: true + }); } componentWillUnmount() { @@ -576,13 +581,12 @@ export class EditorToolbar extends Component { )} -
+
{emoticonHappyOutlineSvg} - {showEmoji && ( + {showEmoji && this.state.isMounted && ( { - this.insertText(e, ""); - }} + anchor={document.querySelector("#editor-tool-emoji-picker")!!} + onSelect={(e) => this.insertText(e, "")} /> )}
diff --git a/src/common/components/emoji-picker/index-old.tsx b/src/common/components/emoji-picker/index-old.tsx new file mode 100644 index 00000000000..adb98000a2f --- /dev/null +++ b/src/common/components/emoji-picker/index-old.tsx @@ -0,0 +1,201 @@ +import React from "react"; +import { FormControl } from "react-bootstrap"; +import BaseComponent from "../base"; +import SearchBox from "../search-box"; +import { _t } from "../../i18n"; +import { getEmojiData } from "../../api/misc"; +import * as ls from "../../util/local-storage"; +import { insertOrReplace } from "../../util/input-util"; +import "./_index.scss"; + +interface Emoji { + a: string; + b: string; + j: string[]; +} + +interface EmojiCategory { + id: string; + name: string; + emojis: string[]; +} + +interface EmojiData { + categories: EmojiCategory[]; + emojis: Record; +} + +interface EmojiCacheItem { + id: string; + name: string; + keywords: string[]; +} + +interface Props { + fallback?: (e: string) => void; +} + +interface State { + data: EmojiData | null; + cache: EmojiCacheItem[] | null; + filter: string; +} + +export default class EmojiPicker extends BaseComponent { + state: State = { + data: null, + cache: null, + filter: "" + }; + + _target: HTMLInputElement | null = null; + + componentDidMount() { + getEmojiData().then((data) => this.setData(data)); + + this.watchTarget(); // initial + + if (typeof window !== "undefined") { + window.addEventListener("focus", this.watchTarget, true); + } + } + + componentWillUnmount() { + super.componentWillUnmount(); + if (typeof window !== "undefined") { + window.removeEventListener("focus", this.watchTarget, true); + } + } + + watchTarget = () => { + if (document.activeElement?.classList.contains("accepts-emoji")) { + this._target = document.activeElement as HTMLInputElement; + } + }; + + setData = (data: EmojiData) => { + const cache: EmojiCacheItem[] = Object.keys(data.emojis).map((e) => { + const em = data.emojis[e]; + return { + id: e, + name: em.a.toLowerCase(), + keywords: em.j ? em.j : [] + }; + }); + + this.stateSet({ data, cache }); + }; + + filterChanged = (e: React.ChangeEvent) => { + this.setState({ filter: e.target.value }); + }; + + clicked = (id: string, native: string) => { + const recent = ls.get("recent-emoji", []); + if (!recent.includes(id)) { + const newRecent = [...new Set([id, ...recent])].slice(0, 18); + ls.set("recent-emoji", newRecent); + this.forceUpdate(); // Re-render recent list + } + + if (this._target) { + insertOrReplace(this._target, native); + } else { + const { fallback } = this.props; + if (fallback) fallback(native); + } + }; + + renderEmoji = (emoji: string) => { + const { data } = this.state; + const em = data!.emojis[emoji]; + if (!em) { + return null; + } + const unicodes = em.b.split("-"); + const codePoints = unicodes.map((u) => Number(`0x${u}`)); + const native = String.fromCodePoint(...codePoints); + + return ( +
{ + this.clicked(emoji, native); + }} + key={emoji} + className="emoji" + title={em.a} + > + {native} +
+ ); + }; + + render() { + const { data, cache, filter } = this.state; + if (!data || !cache) { + return null; + } + + const recent: string[] = ls.get("recent-emoji", []); + + return ( +
+ + + {(() => { + if (filter) { + const results = cache + .filter( + (i) => + i.id.indexOf(filter) !== -1 || + i.name.indexOf(filter) !== -1 || + i.keywords.includes(filter) + ) + .map((i) => i.id); + + return ( +
+
+
+ {results.length === 0 && _t("emoji-picker.filter-no-match")} + {results.length > 0 && results.map((emoji) => this.renderEmoji(emoji))} +
+
+
+ ); + } else { + return ( +
+ {recent.length > 0 && ( +
+
{_t("emoji-picker.recently-used")}
+
+ {recent.map((emoji) => this.renderEmoji(emoji))} +
+
+ )} + + {data.categories.map((cat) => ( +
+
{cat.name}
+
+ {cat.emojis.map((emoji) => this.renderEmoji(emoji))} +
+
+ ))} +
+ ); + } + })()} +
+ ); + } +} diff --git a/src/common/components/emoji-picker/index.tsx b/src/common/components/emoji-picker/index.tsx index 581795c50d8..d6546db20b5 100644 --- a/src/common/components/emoji-picker/index.tsx +++ b/src/common/components/emoji-picker/index.tsx @@ -1,207 +1,88 @@ -import React from "react"; - -import { FormControl } from "react-bootstrap"; - -import BaseComponent from "../base"; -import SearchBox from "../search-box"; - -import { _t } from "../../i18n"; - -import { getEmojiData } from "../../api/misc"; - -import * as ls from "../../util/local-storage"; - -import { insertOrReplace } from "../../util/input-util"; -import "./_index.scss"; - -interface Emoji { - a: string; - b: string; - j: string[]; -} - -interface EmojiCategory { - id: string; - name: string; - emojis: string[]; -} - -interface EmojiData { - categories: EmojiCategory[]; - emojis: Record; -} - -interface EmojiCacheItem { - id: string; - name: string; - keywords: string[]; -} +import React, { useEffect, useRef, useState } from "react"; +import { useQuery } from "@tanstack/react-query"; +import { QueryIdentifiers } from "../../core"; +import { Picker } from "emoji-mart"; +import { createPortal } from "react-dom"; +import useClickAway from "react-use/lib/useClickAway"; +import useMountedState from "react-use/lib/useMountedState"; + +export const DEFAULT_EMOJI_DATA = { + categories: [], + emojis: {}, + aliases: {}, + sheet: { + cols: 0, + rows: 0 + } +}; interface Props { - fallback?: (e: string) => void; + anchor: Element; + onSelect: (e: string) => void; } -interface State { - data: EmojiData | null; - cache: EmojiCacheItem[] | null; - filter: string; -} - -export default class EmojiPicker extends BaseComponent { - state: State = { - data: null, - cache: null, - filter: "" - }; - - _target: HTMLInputElement | null = null; - - componentDidMount() { - getEmojiData().then((data) => this.setData(data)); - - this.watchTarget(); // initial - - if (typeof window !== "undefined") { - window.addEventListener("focus", this.watchTarget, true); +export function EmojiPicker({ anchor, onSelect }: Props) { + const ref = useRef(null); + const instance = useRef(null); + + const [show, setShow] = useState(false); + const [position, setPosition] = useState({ x: 0, y: 0 }); + + useClickAway(ref, () => { + setShow(false); + }); + + const { data } = useQuery( + [QueryIdentifiers.EMOJI_PICKER], + async () => { + try { + const data = await import(/* webpackChunkName: "emojis" */ "@emoji-mart/data"); + return data.default as typeof DEFAULT_EMOJI_DATA; + } catch (e) { + console.error("Failed to load emoji data"); + } + + return DEFAULT_EMOJI_DATA; + }, + { + initialData: DEFAULT_EMOJI_DATA } - } + ); - componentWillUnmount() { - super.componentWillUnmount(); - if (typeof window !== "undefined") { - window.removeEventListener("focus", this.watchTarget, true); - } - } + const isMounted = useMountedState(); - watchTarget = () => { - if (document.activeElement?.classList.contains("accepts-emoji")) { - this._target = document.activeElement as HTMLInputElement; + useEffect(() => { + if (data.categories.length > 0) { + instance.current = new Picker({ + onEmojiSelect: (e: { native: string }) => onSelect(e.native), + ref + }); } - }; + }, [data]); - setData = (data: EmojiData) => { - const cache: EmojiCacheItem[] = Object.keys(data.emojis).map((e) => { - const em = data.emojis[e]; - return { - id: e, - name: em.a.toLowerCase(), - keywords: em.j ? em.j : [] - }; - }); + useEffect(() => { + if (anchor) { + anchor.addEventListener("click", () => setShow(true)); - this.stateSet({ data, cache }); - }; - - filterChanged = (e: React.ChangeEvent) => { - this.setState({ filter: e.target.value }); - }; - - clicked = (id: string, native: string) => { - const recent = ls.get("recent-emoji", []); - if (!recent.includes(id)) { - const newRecent = [...new Set([id, ...recent])].slice(0, 18); - ls.set("recent-emoji", newRecent); - this.forceUpdate(); // Re-render recent list + const { x, y } = anchor.getBoundingClientRect(); + setPosition({ x, y }); } + }, [anchor]); - if (this._target) { - insertOrReplace(this._target, native); - } else { - const { fallback } = this.props; - if (fallback) fallback(native); - } - }; - - renderEmoji = (emoji: string) => { - const { data } = this.state; - const em = data!.emojis[emoji]; - if (!em) { - return null; - } - const unicodes = em.b.split("-"); - const codePoints = unicodes.map((u) => Number(`0x${u}`)); - const native = String.fromCodePoint(...codePoints); - - return ( + return isMounted() ? ( + createPortal(
{ - this.clicked(emoji, native); + ref={ref} + style={{ + position: "absolute", + top: position.y + 40, + left: position.x, + display: show ? "block" : "none" }} - key={emoji} - className="emoji" - title={em.a} - > - {native} -
- ); - }; - - render() { - const { data, cache, filter } = this.state; - if (!data || !cache) { - return null; - } - - const recent: string[] = ls.get("recent-emoji", []); - - return ( -
- - - {(() => { - if (filter) { - const results = cache - .filter( - (i) => - i.id.indexOf(filter) !== -1 || - i.name.indexOf(filter) !== -1 || - i.keywords.includes(filter) - ) - .map((i) => i.id); - - return ( -
-
-
- {results.length === 0 && _t("emoji-picker.filter-no-match")} - {results.length > 0 && results.map((emoji) => this.renderEmoji(emoji))} -
-
-
- ); - } else { - return ( -
- {recent.length > 0 && ( -
-
{_t("emoji-picker.recently-used")}
-
- {recent.map((emoji) => this.renderEmoji(emoji))} -
-
- )} - - {data.categories.map((cat) => ( -
-
{cat.name}
-
- {cat.emojis.map((emoji) => this.renderEmoji(emoji))} -
-
- ))} -
- ); - } - })()} -
- ); - } + />, + document.body!! + ) + ) : ( + <> + ); } diff --git a/src/common/core/react-query.ts b/src/common/core/react-query.ts index f3db0fc44c1..de5ac6e4422 100644 --- a/src/common/core/react-query.ts +++ b/src/common/core/react-query.ts @@ -25,5 +25,7 @@ export enum QueryIdentifiers { THREE_SPEAK_VIDEO_LIST = "three-speak-video-list", THREE_SPEAK_VIDEO_LIST_FILTERED = "three-speak-video-list-filtered", DRAFTS = "drafts", - BY_DRAFT_ID = "by-draft-id" + BY_DRAFT_ID = "by-draft-id", + + EMOJI_PICKER = "emoji-picker" } diff --git a/yarn.lock b/yarn.lock index 058c132c026..330f6f2309d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1344,6 +1344,16 @@ xmldom "^0.5.0" xss "^1.0.9" +"@emoji-mart/data@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@emoji-mart/data/-/data-1.1.2.tgz#777c976f8f143df47cbb23a7077c9ca9fe5fc513" + integrity sha512-1HP8BxD2azjqWJvxIaWAMyTySeZY0Osr83ukYjltPVkNXeJvTz7yDrPLBtnrD5uqJ3tg4CcLuuBW09wahqL/fg== + +"@emoji-mart/react@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@emoji-mart/react/-/react-1.1.1.tgz#ddad52f93a25baf31c5383c3e7e4c6e05554312a" + integrity sha512-NMlFNeWgv1//uPsvLxvGQoIerPuVdXwK/EUek8OOkJ6wVOWPUizRBJU0hDqWZCOROVpfBgCemaC3m6jDOXi03g== + "@firebase/analytics@^0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@firebase/analytics/-/analytics-0.8.0.tgz#b5d595082f57d33842b1fd9025d88f83065e87fe" @@ -5077,6 +5087,11 @@ emittery@^0.7.1: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== +emoji-mart@^5.5.2: + version "5.5.2" + resolved "https://registry.yarnpkg.com/emoji-mart/-/emoji-mart-5.5.2.tgz#3ddbaf053139cf4aa217650078bc1c50ca8381af" + integrity sha512-Sqc/nso4cjxhOwWJsp9xkVm8OF5c+mJLZJFoFfzRuKO+yWiN7K8c96xmtughYb0d/fZ8UC6cLIQ/p4BR6Pv3/A== + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" From 041441a02eb1569b710cdf6d8cd223eb300de21c Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Thu, 17 Aug 2023 16:10:56 +0300 Subject: [PATCH 2/7] Submit page 2.0: Added emoji picker same to decks --- .../deck-threads-form-emoji-picker.tsx | 19 +++++++++---------- src/common/components/emoji-picker/index.tsx | 12 +++++++----- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/common/components/decks/deck-threads-form/deck-threads-form-emoji-picker.tsx b/src/common/components/decks/deck-threads-form/deck-threads-form-emoji-picker.tsx index db60fe09d31..72587416795 100644 --- a/src/common/components/decks/deck-threads-form/deck-threads-form-emoji-picker.tsx +++ b/src/common/components/decks/deck-threads-form/deck-threads-form-emoji-picker.tsx @@ -1,7 +1,6 @@ -import React from "react"; +import React, { useRef } from "react"; import { emojiIconSvg } from "../icons"; import { _t } from "../../../i18n"; -import { PopperDropdown } from "../../popper-dropdown"; import Tooltip from "../../tooltip"; import { EmojiPicker } from "../../emoji-picker"; @@ -10,15 +9,15 @@ interface Props { } export const DeckThreadsFormEmojiPicker = ({ onPick }: Props) => { + const anchorRef = useRef(null); + return ( -
- - -
- onPick(value)} /> -
-
-
+
+ {emojiIconSvg} + onPick(value)} />
); }; diff --git a/src/common/components/emoji-picker/index.tsx b/src/common/components/emoji-picker/index.tsx index d6546db20b5..378b44255da 100644 --- a/src/common/components/emoji-picker/index.tsx +++ b/src/common/components/emoji-picker/index.tsx @@ -17,7 +17,7 @@ export const DEFAULT_EMOJI_DATA = { }; interface Props { - anchor: Element; + anchor: Element | null; onSelect: (e: string) => void; } @@ -62,10 +62,11 @@ export function EmojiPicker({ anchor, onSelect }: Props) { useEffect(() => { if (anchor) { - anchor.addEventListener("click", () => setShow(true)); - - const { x, y } = anchor.getBoundingClientRect(); - setPosition({ x, y }); + anchor.addEventListener("click", () => { + const { x, y } = anchor.getBoundingClientRect(); + setPosition({ x, y }); + setShow(true); + }); } }, [anchor]); @@ -75,6 +76,7 @@ export function EmojiPicker({ anchor, onSelect }: Props) { ref={ref} style={{ position: "absolute", + zIndex: 201, top: position.y + 40, left: position.x, display: show ? "block" : "none" From 6752ba3ca63ab2a61b8f82767fe540219d1b1553 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Thu, 17 Aug 2023 21:12:54 +0300 Subject: [PATCH 3/7] Submit page 2.0: Added emoji picker same to decks --- .../decks/deck-settings/decks-settings.tsx | 4 +- .../components/emoji-picker/_index-old.scss | 74 ++++++++++++++++ .../components/emoji-picker/_index.scss | 87 +++++-------------- .../components/emoji-picker/index-old.tsx | 2 +- src/common/components/emoji-picker/index.tsx | 19 +++- 5 files changed, 113 insertions(+), 73 deletions(-) create mode 100644 src/common/components/emoji-picker/_index-old.scss diff --git a/src/common/components/decks/deck-settings/decks-settings.tsx b/src/common/components/decks/deck-settings/decks-settings.tsx index 2875b1b7118..9b9b8058f08 100644 --- a/src/common/components/decks/deck-settings/decks-settings.tsx +++ b/src/common/components/decks/deck-settings/decks-settings.tsx @@ -2,7 +2,7 @@ import { Alert, Button, Form, InputGroup, Modal } from "react-bootstrap"; import React, { useContext, useEffect, useState } from "react"; import "./_decks-settings.scss"; import { DeckGrid } from "../types"; -import { EmojiPicker } from "../../emoji-picker"; +import EmojiPicker from "../../emoji-picker/index-old"; import { deleteForeverSvg, emoticonHappyOutlineSvg } from "../../../img/svg"; import ClickAwayListener from "../../clickaway-listener"; import * as uuid from "uuid"; @@ -120,7 +120,7 @@ export const DecksSettings = ({ show, setShow, deck }: Props) => { {showEmoji ? ( setShowEmoji(false)}> { + fallback={(value) => { setIcon(value); setShowEmoji(false); }} diff --git a/src/common/components/emoji-picker/_index-old.scss b/src/common/components/emoji-picker/_index-old.scss new file mode 100644 index 00000000000..c478b0df909 --- /dev/null +++ b/src/common/components/emoji-picker/_index-old.scss @@ -0,0 +1,74 @@ +@import "src/style/colors"; +@import "src/style/variables"; +@import "src/style/bootstrap_vars"; +@import "src/style/mixins"; + +.emoji-picker { + width: 280px; + position: absolute; + right: 0; + z-index: 100; + border-bottom-right-radius: 8px; + border-bottom-left-radius: 8px; + padding: 14px 6px; + + @media (max-width: $sm-break) { + width: 160px !important; + } + + @include themify(day) { + background: darken($white-three, 5); + } + + @include themify(night) { + background: $dark-two; + } + + .emoji-cat-list { + height: 160px; + overflow: auto; + + .emoji-cat { + .cat-title { + font-weight: 500; + font-size: 14px; + margin: 4px 0 6px 0; + + @include themify(day) { + color: $charcoal-grey; + } + + @include themify(night) { + color: $pinkish-grey; + } + } + + .emoji-list { + @include clearfix(); + display: flex; + flex-wrap: wrap; + + .emoji { + cursor: pointer; + font-size: 18px; + margin: 3px 3px 1px 3px; + align-items: center; + justify-content: center; + display: flex; + font-family: "Segoe UI Emoji", "Segoe UI Symbol", "Segoe UI", "Apple Color Emoji"; + border-bottom: 2px solid transparent; + + &:hover { + @include themify(day) { + border-bottom: 2px solid $white-three; + } + + @include themify(night) { + border-bottom: 2px solid lighten($dark-six, 14); + } + } + } + } + } + } +} diff --git a/src/common/components/emoji-picker/_index.scss b/src/common/components/emoji-picker/_index.scss index c478b0df909..4674b2365ec 100644 --- a/src/common/components/emoji-picker/_index.scss +++ b/src/common/components/emoji-picker/_index.scss @@ -1,74 +1,29 @@ -@import "src/style/colors"; -@import "src/style/variables"; -@import "src/style/bootstrap_vars"; -@import "src/style/mixins"; +@import "src/style/vars_mixins"; -.emoji-picker { - width: 280px; +.emoji-picker-dialog { position: absolute; - right: 0; - z-index: 100; - border-bottom-right-radius: 8px; - border-bottom-left-radius: 8px; - padding: 14px 6px; + z-index: 201; - @media (max-width: $sm-break) { - width: 160px !important; + em-emoji-picker { + width: 300px; } - @include themify(day) { - background: darken($white-three, 5); - } - - @include themify(night) { - background: $dark-two; - } - - .emoji-cat-list { - height: 160px; - overflow: auto; - - .emoji-cat { - .cat-title { - font-weight: 500; - font-size: 14px; - margin: 4px 0 6px 0; - - @include themify(day) { - color: $charcoal-grey; - } - - @include themify(night) { - color: $pinkish-grey; - } - } - - .emoji-list { - @include clearfix(); - display: flex; - flex-wrap: wrap; - - .emoji { - cursor: pointer; - font-size: 18px; - margin: 3px 3px 1px 3px; - align-items: center; - justify-content: center; - display: flex; - font-family: "Segoe UI Emoji", "Segoe UI Symbol", "Segoe UI", "Apple Color Emoji"; - border-bottom: 2px solid transparent; - - &:hover { - @include themify(day) { - border-bottom: 2px solid $white-three; - } + @include media-breakpoint-down(sm) { + bottom: 0; + top: unset !important; + left: 0 !important; + width: 100%; + background: #fff; + align-items: center; + justify-content: center; + border-top: 1px solid var(--border-color); + + @include themify(night) { + background-color: rgba(21, 22, 23); + } - @include themify(night) { - border-bottom: 2px solid lighten($dark-six, 14); - } - } - } - } + em-emoji-picker { + width: 100%; } } -} +} \ No newline at end of file diff --git a/src/common/components/emoji-picker/index-old.tsx b/src/common/components/emoji-picker/index-old.tsx index adb98000a2f..57420a94fa8 100644 --- a/src/common/components/emoji-picker/index-old.tsx +++ b/src/common/components/emoji-picker/index-old.tsx @@ -6,7 +6,7 @@ import { _t } from "../../i18n"; import { getEmojiData } from "../../api/misc"; import * as ls from "../../util/local-storage"; import { insertOrReplace } from "../../util/input-util"; -import "./_index.scss"; +import "./_index-old.scss"; interface Emoji { a: string; diff --git a/src/common/components/emoji-picker/index.tsx b/src/common/components/emoji-picker/index.tsx index 378b44255da..db5bd6c679e 100644 --- a/src/common/components/emoji-picker/index.tsx +++ b/src/common/components/emoji-picker/index.tsx @@ -5,6 +5,8 @@ import { Picker } from "emoji-mart"; import { createPortal } from "react-dom"; import useClickAway from "react-use/lib/useClickAway"; import useMountedState from "react-use/lib/useMountedState"; +import "./_index.scss"; +import { useMappedStore } from "../../store/use-mapped-store"; export const DEFAULT_EMOJI_DATA = { categories: [], @@ -25,6 +27,8 @@ export function EmojiPicker({ anchor, onSelect }: Props) { const ref = useRef(null); const instance = useRef(null); + const { global } = useMappedStore(); + const [show, setShow] = useState(false); const [position, setPosition] = useState({ x: 0, y: 0 }); @@ -54,11 +58,19 @@ export function EmojiPicker({ anchor, onSelect }: Props) { useEffect(() => { if (data.categories.length > 0) { instance.current = new Picker({ + set: "apple", + theme: global.theme === "day" ? "light" : "dark", + dynamicWidth: true, + previewPosition: "none", onEmojiSelect: (e: { native: string }) => onSelect(e.native), ref }); } - }, [data]); + + return () => { + instance.current = null; + }; + }, [data, global.theme]); useEffect(() => { if (anchor) { @@ -73,13 +85,12 @@ export function EmojiPicker({ anchor, onSelect }: Props) { return isMounted() ? ( createPortal(
, document.body!! From ebdde4b71b15071cbe11882c8eb37a65bc2297f1 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Thu, 17 Aug 2023 21:14:07 +0300 Subject: [PATCH 4/7] Fixed tests --- src/common/components/emoji-picker/index.spec.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/components/emoji-picker/index.spec.tsx b/src/common/components/emoji-picker/index.spec.tsx index 3d5ade5679b..09ccc449af5 100644 --- a/src/common/components/emoji-picker/index.spec.tsx +++ b/src/common/components/emoji-picker/index.spec.tsx @@ -2,7 +2,7 @@ import React from "react"; import renderer from "react-test-renderer"; -import EmojiPicker from "./index"; +import EmojiPicker from "./index-old"; import emojiData from "../../../../public/emoji.json"; From c9c3369696c016b0da586364d99ce04a58e6b217 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Fri, 18 Aug 2023 23:48:49 +0300 Subject: [PATCH 5/7] Fixed emoji picker in comment block --- src/common/components/emoji-picker/index.tsx | 2 +- yarn.lock | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/common/components/emoji-picker/index.tsx b/src/common/components/emoji-picker/index.tsx index db5bd6c679e..8a057b4f5c8 100644 --- a/src/common/components/emoji-picker/index.tsx +++ b/src/common/components/emoji-picker/index.tsx @@ -76,7 +76,7 @@ export function EmojiPicker({ anchor, onSelect }: Props) { if (anchor) { anchor.addEventListener("click", () => { const { x, y } = anchor.getBoundingClientRect(); - setPosition({ x, y }); + setPosition({ x: x + window.scrollX, y: y + window.scrollY }); setShow(true); }); } diff --git a/yarn.lock b/yarn.lock index 330f6f2309d..e15a1a6e9dc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1349,11 +1349,6 @@ resolved "https://registry.yarnpkg.com/@emoji-mart/data/-/data-1.1.2.tgz#777c976f8f143df47cbb23a7077c9ca9fe5fc513" integrity sha512-1HP8BxD2azjqWJvxIaWAMyTySeZY0Osr83ukYjltPVkNXeJvTz7yDrPLBtnrD5uqJ3tg4CcLuuBW09wahqL/fg== -"@emoji-mart/react@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@emoji-mart/react/-/react-1.1.1.tgz#ddad52f93a25baf31c5383c3e7e4c6e05554312a" - integrity sha512-NMlFNeWgv1//uPsvLxvGQoIerPuVdXwK/EUek8OOkJ6wVOWPUizRBJU0hDqWZCOROVpfBgCemaC3m6jDOXi03g== - "@firebase/analytics@^0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@firebase/analytics/-/analytics-0.8.0.tgz#b5d595082f57d33842b1fd9025d88f83065e87fe" From d88005056c2a1e04894fbb019da6588c773024a7 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Sun, 20 Aug 2023 22:13:44 +0300 Subject: [PATCH 6/7] Fixed emoji picker in Brave --- src/common/components/emoji-picker/_index.scss | 1 + src/common/components/emoji-picker/index.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/components/emoji-picker/_index.scss b/src/common/components/emoji-picker/_index.scss index 4674b2365ec..583adb88c9c 100644 --- a/src/common/components/emoji-picker/_index.scss +++ b/src/common/components/emoji-picker/_index.scss @@ -17,6 +17,7 @@ align-items: center; justify-content: center; border-top: 1px solid var(--border-color); + position: fixed; @include themify(night) { background-color: rgba(21, 22, 23); diff --git a/src/common/components/emoji-picker/index.tsx b/src/common/components/emoji-picker/index.tsx index 8a057b4f5c8..1ac07378695 100644 --- a/src/common/components/emoji-picker/index.tsx +++ b/src/common/components/emoji-picker/index.tsx @@ -93,7 +93,7 @@ export function EmojiPicker({ anchor, onSelect }: Props) { display: show ? "flex" : "none" }} />, - document.body!! + document.querySelector("#root")!! ) ) : ( <> From 08735df9afdd22c71663cf1c84c7e87f3a3efdf8 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Tue, 22 Aug 2023 13:11:08 +0300 Subject: [PATCH 7/7] Fixed emoji picker appearing --- src/common/components/emoji-picker/index.tsx | 57 ++++++++++---------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/common/components/emoji-picker/index.tsx b/src/common/components/emoji-picker/index.tsx index 1ac07378695..11f6f21f787 100644 --- a/src/common/components/emoji-picker/index.tsx +++ b/src/common/components/emoji-picker/index.tsx @@ -23,14 +23,21 @@ interface Props { onSelect: (e: string) => void; } +/** + * Renders an emoji picker dialog. + * + * @param {Props} anchor - The anchor element to position the picker relative to. + * @param {function} onSelect - The callback function to be called when an emoji is selected. + * @return The rendered emoji picker dialog. + */ export function EmojiPicker({ anchor, onSelect }: Props) { const ref = useRef(null); - const instance = useRef(null); const { global } = useMappedStore(); const [show, setShow] = useState(false); const [position, setPosition] = useState({ x: 0, y: 0 }); + const [pickerInstance, setPickerInstance] = useState(); useClickAway(ref, () => { setShow(false); @@ -57,19 +64,17 @@ export function EmojiPicker({ anchor, onSelect }: Props) { useEffect(() => { if (data.categories.length > 0) { - instance.current = new Picker({ - set: "apple", - theme: global.theme === "day" ? "light" : "dark", - dynamicWidth: true, - previewPosition: "none", - onEmojiSelect: (e: { native: string }) => onSelect(e.native), - ref - }); + setPickerInstance( + new Picker({ + dynamicWidth: true, + onEmojiSelect: (e: { native: string }) => onSelect(e.native), + previewPosition: "none", + ref, + set: "apple", + theme: global.theme === "day" ? "light" : "dark" + }) + ); } - - return () => { - instance.current = null; - }; }, [data, global.theme]); useEffect(() => { @@ -82,20 +87,16 @@ export function EmojiPicker({ anchor, onSelect }: Props) { } }, [anchor]); - return isMounted() ? ( - createPortal( -
, - document.querySelector("#root")!! - ) - ) : ( - <> + return createPortal( +
, + document.querySelector("#root")!! ); }