emoji-picker-react 4.6.15 → 4.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +60 -30
  2. package/dist/components/Reactions/BtnPlus.d.ts +2 -0
  3. package/dist/components/Reactions/DEFAULT_REACTIONS.d.ts +1 -0
  4. package/dist/components/Reactions/Reactions.d.ts +2 -0
  5. package/dist/components/context/ElementRefContext.d.ts +1 -0
  6. package/dist/components/context/PickerContext.d.ts +1 -0
  7. package/dist/components/emoji/ClickableEmojiButton.d.ts +2 -1
  8. package/dist/components/emoji/Emoji.d.ts +2 -1
  9. package/dist/config/config.d.ts +5 -1
  10. package/dist/config/mutableConfig.d.ts +3 -2
  11. package/dist/config/useConfig.d.ts +7 -1
  12. package/dist/emoji-picker-react.cjs.development.js +956 -741
  13. package/dist/emoji-picker-react.cjs.development.js.map +1 -1
  14. package/dist/emoji-picker-react.cjs.production.min.js +1 -1
  15. package/dist/emoji-picker-react.cjs.production.min.js.map +1 -1
  16. package/dist/emoji-picker-react.esm.js +957 -742
  17. package/dist/emoji-picker-react.esm.js.map +1 -1
  18. package/dist/hooks/useMouseDownHandlers.d.ts +2 -1
  19. package/package.json +5 -2
  20. package/src/EmojiPickerReact.tsx +28 -3
  21. package/src/Stylesheet/stylesheet.tsx +14 -2
  22. package/src/components/Reactions/BtnPlus.tsx +78 -0
  23. package/src/components/Reactions/DEFAULT_REACTIONS.ts +9 -0
  24. package/src/components/Reactions/Reactions.tsx +63 -0
  25. package/src/components/Reactions/svg/plus.svg +17 -0
  26. package/src/components/body/Body.tsx +10 -3
  27. package/src/components/context/ElementRefContext.tsx +10 -2
  28. package/src/components/context/PickerContext.tsx +16 -3
  29. package/src/components/emoji/ClickableEmojiButton.tsx +8 -2
  30. package/src/components/emoji/Emoji.tsx +4 -1
  31. package/src/components/footer/Preview.tsx +7 -2
  32. package/src/components/header/Header.tsx +5 -1
  33. package/src/components/main/PickerMain.tsx +27 -2
  34. package/src/config/config.ts +11 -3
  35. package/src/config/mutableConfig.ts +5 -3
  36. package/src/config/useConfig.ts +31 -8
  37. package/src/hooks/useMouseDownHandlers.ts +13 -11
  38. package/src/index.tsx +2 -1
@@ -1,3 +1,4 @@
1
1
  import * as React from 'react';
2
2
  import { NullableElement } from '../DomUtils/selectors';
3
- export declare function useMouseDownHandlers(BodyRef: React.MutableRefObject<NullableElement>): void;
3
+ import { MOUSE_EVENT_SOURCE } from '../config/useConfig';
4
+ export declare function useMouseDownHandlers(ContainerRef: React.MutableRefObject<NullableElement>, mouseEventSource: MOUSE_EVENT_SOURCE): void;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.6.15",
2
+ "version": "4.7.1",
3
3
  "license": "MIT",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
@@ -12,7 +12,10 @@
12
12
  "emoji-picker",
13
13
  "react-emoji-picker",
14
14
  "emoji",
15
- "emojis"
15
+ "emojis",
16
+ "reactions",
17
+ "reactions-picker",
18
+ "emoji-reactions"
16
19
  ],
17
20
  "engines": {
18
21
  "node": ">=10"
@@ -1,8 +1,10 @@
1
1
  import * as React from 'react';
2
2
 
3
+ import { Reactions } from './components/Reactions/Reactions';
3
4
  import { Body } from './components/body/Body';
4
5
  import { ElementRefContextProvider } from './components/context/ElementRefContext';
5
6
  import { PickerConfigProvider } from './components/context/PickerConfigContext';
7
+ import { useReactionsModeState } from './components/context/PickerContext';
6
8
  import { Preview } from './components/footer/Preview';
7
9
  import { Header } from './components/header/Header';
8
10
  import PickerMain from './components/main/PickerMain';
@@ -14,21 +16,44 @@ function EmojiPicker(props: PickerProps) {
14
16
  <ElementRefContextProvider>
15
17
  <PickerConfigProvider {...props}>
16
18
  <PickerMain>
17
- <Header />
18
- <Body />
19
- <Preview />
19
+ <ContentControl />
20
20
  </PickerMain>
21
21
  </PickerConfigProvider>
22
22
  </ElementRefContextProvider>
23
23
  );
24
24
  }
25
25
 
26
+ function ContentControl() {
27
+ const [reactionsDefaultOpen] = useReactionsModeState();
28
+ const [renderAll, setRenderAll] = React.useState(!reactionsDefaultOpen);
29
+
30
+ React.useEffect(() => {
31
+ if (!renderAll) {
32
+ setRenderAll(true);
33
+ }
34
+ }, []);
35
+
36
+ return (
37
+ <>
38
+ <Reactions />
39
+ {renderAll ? (
40
+ <>
41
+ <Header />
42
+ <Body />
43
+ <Preview />
44
+ </>
45
+ ) : null}
46
+ </>
47
+ );
48
+ }
49
+
26
50
  // eslint-disable-next-line complexity
27
51
  export default React.memo(EmojiPicker, (prev, next) => {
28
52
  const prevCustomEmojis = prev.customEmojis ?? [];
29
53
  const nextCustomEmojis = next.customEmojis ?? [];
30
54
  return (
31
55
  prev.emojiVersion === next.emojiVersion &&
56
+ prev.reactionsDefaultOpen === next.reactionsDefaultOpen &&
32
57
  prev.searchPlaceHolder === next.searchPlaceHolder &&
33
58
  prev.searchPlaceholder === next.searchPlaceholder &&
34
59
  prev.defaultSkinTone === next.defaultSkinTone &&
@@ -10,7 +10,8 @@ const hidden = {
10
10
  display: 'none',
11
11
  opacity: '0',
12
12
  pointerEvents: 'none',
13
- visibility: 'hidden'
13
+ visibility: 'hidden',
14
+ overflow: 'hidden'
14
15
  };
15
16
 
16
17
  export const commonStyles = stylesheet.create({
@@ -53,7 +54,18 @@ export const commonInteractionStyles = stylesheet.create({
53
54
  visibleOnSearchOnly: hidden
54
55
  }
55
56
  },
56
-
57
+ hiddenOnReactions: {
58
+ transition: 'all 0.5s ease-in-out'
59
+ },
60
+ '.epr-reactions': {
61
+ hiddenOnReactions: {
62
+ height: '0px',
63
+ width: '0px',
64
+ opacity: '0',
65
+ pointerEvents: 'none',
66
+ overflow: 'hidden'
67
+ }
68
+ },
57
69
  '.EmojiPickerReact:not(.epr-search-active)': {
58
70
  categoryBtn: {
59
71
  ':hover': {
@@ -0,0 +1,78 @@
1
+ import { cx } from 'flairup';
2
+ import * as React from 'react';
3
+
4
+ import { stylesheet } from '../../Stylesheet/stylesheet';
5
+ import { Button } from '../atoms/Button';
6
+ import { useReactionsModeState } from '../context/PickerContext';
7
+
8
+ import Plus from './svg/plus.svg';
9
+
10
+ export function BtnPlus() {
11
+ const [, setReactionsMode] = useReactionsModeState();
12
+ return (
13
+ <Button
14
+ aria-label="Show all Emojis"
15
+ title="Show all Emojis"
16
+ tabIndex={0}
17
+ className={cx(styles.plusSign)}
18
+ onClick={() => setReactionsMode(false)}
19
+ />
20
+ );
21
+ }
22
+
23
+ const styles = stylesheet.create({
24
+ plusSign: {
25
+ fontSize: '20px',
26
+ padding: '18px',
27
+ color: 'var(--epr-text-color)',
28
+ borderRadius: '50%',
29
+ textAlign: 'center',
30
+ lineHeight: '100%',
31
+ width: '20px',
32
+ height: '20px',
33
+ display: 'flex',
34
+ justifyContent: 'center',
35
+ alignItems: 'center',
36
+ transition: 'background-color 0.1s ease-in-out',
37
+ ':after': {
38
+ content: '',
39
+ minWidth: '20px',
40
+ minHeight: '20px',
41
+ backgroundImage: `url(${Plus})`,
42
+ backgroundColor: 'transparent',
43
+ backgroundRepeat: 'no-repeat',
44
+ backgroundSize: '20px',
45
+ backgroundPositionY: '0'
46
+ },
47
+ ':hover': {
48
+ color: 'var(--epr-highlight-color)',
49
+ backgroundColor: 'var(--epr-hover-bg-color)',
50
+ // @ts-ignore
51
+ ':after': {
52
+ backgroundPositionY: '-20px'
53
+ }
54
+ },
55
+ ':focus': {
56
+ color: 'var(--epr-highlight-color)',
57
+ backgroundColor: 'var(--epr-hover-bg-color)',
58
+ // @ts-ignore
59
+ ':after': {
60
+ backgroundPositionY: '-40px'
61
+ }
62
+ }
63
+ },
64
+ '.epr-dark-theme': {
65
+ plusSign: {
66
+ ':after': { backgroundPositionY: '-40px' },
67
+ ':hover:after': { backgroundPositionY: '-60px' }
68
+ }
69
+ },
70
+ '.epr-auto-theme': {
71
+ plusSign: {
72
+ '@media (prefers-color-scheme: dark)': {
73
+ ':after': { backgroundPositionY: '-40px' },
74
+ ':hover:after': { backgroundPositionY: '-60px' }
75
+ }
76
+ }
77
+ }
78
+ });
@@ -0,0 +1,9 @@
1
+ export const DEFAULT_REACTIONS = [
2
+ '1f44d', // 👍
3
+ '2764-fe0f', // ❤️
4
+ '1f603', // 😃
5
+ '1f622', // 😢
6
+ '1f64f', // 🙏
7
+ '1f44e', // 👎
8
+ '1f621' // 😡
9
+ ];
@@ -0,0 +1,63 @@
1
+ import { cx } from 'flairup';
2
+ import * as React from 'react';
3
+
4
+ import { commonStyles, stylesheet } from '../../Stylesheet/stylesheet';
5
+ import {
6
+ MOUSE_EVENT_SOURCE,
7
+ useEmojiStyleConfig,
8
+ useReactionsConfig
9
+ } from '../../config/useConfig';
10
+ import { DataEmoji } from '../../dataUtils/DataTypes';
11
+ import { emojiByUnified } from '../../dataUtils/emojiSelectors';
12
+ import { useMouseDownHandlers } from '../../hooks/useMouseDownHandlers';
13
+ import { useReactionsRef } from '../context/ElementRefContext';
14
+ import { useReactionsModeState } from '../context/PickerContext';
15
+ import { ClickableEmoji } from '../emoji/Emoji';
16
+
17
+ import { BtnPlus } from './BtnPlus';
18
+
19
+ export function Reactions() {
20
+ const [reactionsOpen] = useReactionsModeState();
21
+ const ReactionsRef = useReactionsRef();
22
+ const reactions = useReactionsConfig();
23
+ useMouseDownHandlers(ReactionsRef, MOUSE_EVENT_SOURCE.REACTIONS);
24
+ const emojiStyle = useEmojiStyleConfig();
25
+
26
+ if (!reactionsOpen) {
27
+ return null;
28
+ }
29
+
30
+ return (
31
+ <ul
32
+ className={cx(styles.list, !reactionsOpen && commonStyles.hidden)}
33
+ ref={ReactionsRef}
34
+ >
35
+ {reactions.map(reaction => (
36
+ <li key={reaction}>
37
+ <ClickableEmoji
38
+ emoji={emojiByUnified(reaction) as DataEmoji}
39
+ emojiStyle={emojiStyle}
40
+ unified={reaction}
41
+ showVariations={false}
42
+ round
43
+ />
44
+ </li>
45
+ ))}
46
+ <li>
47
+ <BtnPlus />
48
+ </li>
49
+ </ul>
50
+ );
51
+ }
52
+
53
+ const styles = stylesheet.create({
54
+ list: {
55
+ listStyle: 'none',
56
+ margin: '0',
57
+ padding: '0 5px',
58
+ display: 'flex',
59
+ justifyContent: 'space-between',
60
+ alignItems: 'center',
61
+ height: '100%'
62
+ }
63
+ });
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 28.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4
+ width="20px" height="80px" viewBox="0 0 20 80" enable-background="new 0 0 20 80" xml:space="preserve">
5
+ <path fill="#868686" d="M5.7,10.4c0,0.1,0.1,0.3,0.2,0.4C6,10.9,6.1,11,6.3,11h3.4v3.4c0,0.1,0.1,0.3,0.2,0.4
6
+ c0.1,0.1,0.2,0.2,0.4,0.2c0.3,0,0.5-0.2,0.5-0.5v-3.4h3.4c0.3,0,0.5-0.2,0.5-0.5s-0.2-0.5-0.5-0.5h-3.4V6.7c0-0.3-0.2-0.5-0.5-0.5
7
+ C9.8,6,9.6,6.2,9.6,6.5v3.4H6.2C5.9,9.9,5.7,10.1,5.7,10.4L5.7,10.4z"/>
8
+ <path fill="#3371B7" d="M5.7,30.4c0,0.1,0.1,0.3,0.2,0.4S6.1,31,6.3,31h3.4v3.4c0,0.1,0.1,0.3,0.2,0.4c0.1,0.1,0.2,0.2,0.4,0.2
9
+ c0.3,0,0.5-0.2,0.5-0.5v-3.4h3.4c0.3,0,0.5-0.2,0.5-0.5s-0.2-0.5-0.5-0.5h-3.4v-3.4c0-0.3-0.2-0.5-0.5-0.5s-0.5,0.2-0.5,0.5v3.4H6.2
10
+ C5.9,29.9,5.7,30.1,5.7,30.4L5.7,30.4z"/>
11
+ <path fill="#C0C0BF" d="M5.7,50.4c0,0.1,0.1,0.3,0.2,0.4C6,50.9,6.1,51,6.3,51h3.4v3.4c0,0.1,0.1,0.3,0.2,0.4
12
+ c0.1,0.1,0.2,0.2,0.4,0.2c0.3,0,0.5-0.2,0.5-0.5v-3.4h3.4c0.3,0,0.5-0.2,0.5-0.5s-0.2-0.5-0.5-0.5h-3.4v-3.4c0-0.3-0.2-0.5-0.5-0.5
13
+ s-0.5,0.2-0.5,0.5v3.4H6.2C5.9,49.9,5.7,50.1,5.7,50.4L5.7,50.4z"/>
14
+ <path fill="#6AA9DD" d="M5.7,70.4c0,0.1,0.1,0.3,0.2,0.4S6.1,71,6.3,71h3.4v3.4c0,0.1,0.1,0.3,0.2,0.4c0.1,0.1,0.2,0.2,0.4,0.2
15
+ c0.3,0,0.5-0.2,0.5-0.5v-3.4h3.4c0.3,0,0.5-0.2,0.5-0.5s-0.2-0.5-0.5-0.5h-3.4v-3.4c0-0.3-0.2-0.5-0.5-0.5s-0.5,0.2-0.5,0.5v3.4H6.4
16
+ C5.9,69.9,5.7,70.1,5.7,70.4L5.7,70.4z"/>
17
+ </svg>
@@ -2,7 +2,11 @@ import { cx } from 'flairup';
2
2
  import * as React from 'react';
3
3
 
4
4
  import { ClassNames } from '../../DomUtils/classNames';
5
- import { stylesheet } from '../../Stylesheet/stylesheet';
5
+ import {
6
+ commonInteractionStyles,
7
+ stylesheet
8
+ } from '../../Stylesheet/stylesheet';
9
+ import { MOUSE_EVENT_SOURCE } from '../../config/useConfig';
6
10
  import { useOnMouseMove } from '../../hooks/useDisallowMouseMove';
7
11
  import { useMouseDownHandlers } from '../../hooks/useMouseDownHandlers';
8
12
  import { useOnScroll } from '../../hooks/useOnScroll';
@@ -14,11 +18,14 @@ import { EmojiVariationPicker } from './EmojiVariationPicker';
14
18
  export function Body() {
15
19
  const BodyRef = useBodyRef();
16
20
  useOnScroll(BodyRef);
17
- useMouseDownHandlers(BodyRef);
21
+ useMouseDownHandlers(BodyRef, MOUSE_EVENT_SOURCE.PICKER);
18
22
  useOnMouseMove();
19
23
 
20
24
  return (
21
- <div className={cx(styles.body)} ref={BodyRef}>
25
+ <div
26
+ className={cx(styles.body, commonInteractionStyles.hiddenOnReactions)}
27
+ ref={BodyRef}
28
+ >
22
29
  <EmojiVariationPicker />
23
30
  <EmojiList />
24
31
  </div>
@@ -15,6 +15,7 @@ export function ElementRefContextProvider({
15
15
  const SkinTonePickerRef = React.useRef<HTMLDivElement>(null);
16
16
  const CategoryNavigationRef = React.useRef<HTMLDivElement>(null);
17
17
  const VariationPickerRef = React.useRef<HTMLDivElement>(null);
18
+ const ReactionsRef = React.useRef<HTMLUListElement>(null);
18
19
 
19
20
  return (
20
21
  <ElementRefContext.Provider
@@ -25,7 +26,8 @@ export function ElementRefContextProvider({
25
26
  PickerMainRef,
26
27
  SearchInputRef,
27
28
  SkinTonePickerRef,
28
- VariationPickerRef
29
+ VariationPickerRef,
30
+ ReactionsRef
29
31
  }}
30
32
  >
31
33
  {children}
@@ -45,6 +47,7 @@ type ElementRefs = {
45
47
  BodyRef: ElementRef<HTMLDivElement>;
46
48
  CategoryNavigationRef: ElementRef<HTMLDivElement>;
47
49
  VariationPickerRef: ElementRef<HTMLDivElement>;
50
+ ReactionsRef: ElementRef<HTMLUListElement>;
48
51
  };
49
52
 
50
53
  const ElementRefContext = React.createContext<ElementRefs>({
@@ -54,7 +57,8 @@ const ElementRefContext = React.createContext<ElementRefs>({
54
57
  PickerMainRef: React.createRef(),
55
58
  SearchInputRef: React.createRef(),
56
59
  SkinTonePickerRef: React.createRef(),
57
- VariationPickerRef: React.createRef()
60
+ VariationPickerRef: React.createRef(),
61
+ ReactionsRef: React.createRef()
58
62
  });
59
63
 
60
64
  function useElementRef() {
@@ -84,6 +88,10 @@ export function useBodyRef() {
84
88
  return useElementRef()['BodyRef'];
85
89
  }
86
90
 
91
+ export function useReactionsRef() {
92
+ return useElementRef()['ReactionsRef'];
93
+ }
94
+
87
95
  export function useSearchInputRef() {
88
96
  return useElementRef()['SearchInputRef'];
89
97
  }
@@ -1,7 +1,10 @@
1
1
  import * as React from 'react';
2
2
  import { useState } from 'react';
3
3
 
4
- import { useDefaultSkinToneConfig } from '../../config/useConfig';
4
+ import {
5
+ useDefaultSkinToneConfig,
6
+ useReactionsOpenConfig
7
+ } from '../../config/useConfig';
5
8
  import { DataEmoji } from '../../dataUtils/DataTypes';
6
9
  import { alphaNumericEmojiIndex } from '../../dataUtils/alphaNumericEmojiIndex';
7
10
  import { useDebouncedState } from '../../hooks/useDebouncedState';
@@ -13,6 +16,7 @@ import { SkinTones } from '../../types/exposedTypes';
13
16
  export function PickerContextProvider({ children }: Props) {
14
17
  const disallowedEmojis = useDisallowedEmojis();
15
18
  const defaultSkinTone = useDefaultSkinToneConfig();
19
+ const reactionsDefaultOpen = useReactionsOpenConfig();
16
20
 
17
21
  // Initialize the filter with the inititial dictionary
18
22
  const filterRef = React.useRef<FilterState>(alphaNumericEmojiIndex);
@@ -29,6 +33,7 @@ export function PickerContextProvider({ children }: Props) {
29
33
  const activeCategoryState = useState<ActiveCategoryState>(null);
30
34
  const emojisThatFailedToLoadState = useState<Set<string>>(new Set());
31
35
  const emojiVariationPickerState = useState<DataEmoji | null>(null);
36
+ const reactionsModeState = useState(reactionsDefaultOpen);
32
37
  const [isPastInitialLoad, setIsPastInitialLoad] = useState(false);
33
38
 
34
39
  useMarkInitialLoad(setIsPastInitialLoad);
@@ -47,7 +52,8 @@ export function PickerContextProvider({ children }: Props) {
47
52
  isPastInitialLoad,
48
53
  searchTerm,
49
54
  skinToneFanOpenState,
50
- suggestedUpdateState
55
+ suggestedUpdateState,
56
+ reactionsModeState
51
57
  }}
52
58
  >
53
59
  {children}
@@ -70,6 +76,7 @@ const PickerContext = React.createContext<{
70
76
  disallowClickRef: React.MutableRefObject<boolean>;
71
77
  disallowMouseRef: React.MutableRefObject<boolean>;
72
78
  disallowedEmojisRef: React.MutableRefObject<Record<string, boolean>>;
79
+ reactionsModeState: ReactState<boolean>;
73
80
  }>({
74
81
  activeCategoryState: [null, () => {}],
75
82
  activeSkinTone: [SkinTones.NEUTRAL, () => {}],
@@ -82,7 +89,8 @@ const PickerContext = React.createContext<{
82
89
  isPastInitialLoad: true,
83
90
  searchTerm: ['', () => new Promise<string>(() => undefined)],
84
91
  skinToneFanOpenState: [false, () => {}],
85
- suggestedUpdateState: [Date.now(), () => {}]
92
+ suggestedUpdateState: [Date.now(), () => {}],
93
+ reactionsModeState: [false, () => {}]
86
94
  });
87
95
 
88
96
  type Props = Readonly<{
@@ -104,6 +112,11 @@ export function useDisallowMouseRef() {
104
112
  return disallowMouseRef;
105
113
  }
106
114
 
115
+ export function useReactionsModeState() {
116
+ const { reactionsModeState } = React.useContext(PickerContext);
117
+ return reactionsModeState;
118
+ }
119
+
107
120
  export function useSearchTermState() {
108
121
  const { searchTerm } = React.useContext(PickerContext);
109
122
  return searchTerm;
@@ -17,6 +17,7 @@ type ClickableEmojiButtonProps = Readonly<{
17
17
  children: React.ReactNode;
18
18
  hasVariations: boolean;
19
19
  unified?: string;
20
+ round?: boolean;
20
21
  }>;
21
22
 
22
23
  export function ClickableEmojiButton({
@@ -26,7 +27,8 @@ export function ClickableEmojiButton({
26
27
  hiddenOnSearch,
27
28
  showVariations = true,
28
29
  hasVariations,
29
- children
30
+ children,
31
+ round = false
30
32
  }: ClickableEmojiButtonProps) {
31
33
  return (
32
34
  <Button
@@ -37,7 +39,8 @@ export function ClickableEmojiButton({
37
39
  {
38
40
  [ClassNames.visible]: !hidden && !hiddenOnSearch
39
41
  },
40
- !!(hasVariations && showVariations) && styles.hasVariations
42
+ !!(hasVariations && showVariations) && styles.hasVariations,
43
+ round && styles.round
41
44
  )}
42
45
  data-unified={unified}
43
46
  aria-label={emojiNames[0]}
@@ -70,6 +73,9 @@ const styles = stylesheet.create({
70
73
  backgroundColor: 'var(--epr-focus-bg-color)'
71
74
  }
72
75
  },
76
+ round: {
77
+ borderRadius: '50%'
78
+ },
73
79
  hasVariations: {
74
80
  '.': ClassNames.emojiHasVariations,
75
81
  ':after': {
@@ -14,6 +14,7 @@ type ClickableEmojiProps = Readonly<
14
14
  hiddenOnSearch?: boolean;
15
15
  emoji: DataEmoji;
16
16
  className?: string;
17
+ round?: boolean;
17
18
  }
18
19
  >;
19
20
 
@@ -26,7 +27,8 @@ export function ClickableEmoji({
26
27
  showVariations = true,
27
28
  size,
28
29
  lazyLoad,
29
- getEmojiUrl
30
+ getEmojiUrl,
31
+ round = false
30
32
  }: ClickableEmojiProps) {
31
33
  const hasVariations = emojiHasVariations(emoji);
32
34
 
@@ -38,6 +40,7 @@ export function ClickableEmoji({
38
40
  hiddenOnSearch={hiddenOnSearch}
39
41
  emojiNames={emojiNames(emoji)}
40
42
  unified={unified}
43
+ round={round}
41
44
  >
42
45
  <ViewOnlyEmoji
43
46
  unified={unified}
@@ -2,7 +2,10 @@ import { cx } from 'flairup';
2
2
  import * as React from 'react';
3
3
  import { useState } from 'react';
4
4
 
5
- import { stylesheet } from '../../Stylesheet/stylesheet';
5
+ import {
6
+ commonInteractionStyles,
7
+ stylesheet
8
+ } from '../../Stylesheet/stylesheet';
6
9
  import {
7
10
  useEmojiStyleConfig,
8
11
  useGetEmojiUrlConfig,
@@ -30,7 +33,9 @@ export function Preview() {
30
33
  }
31
34
 
32
35
  return (
33
- <Flex className={cx(styles.preview)}>
36
+ <Flex
37
+ className={cx(styles.preview, commonInteractionStyles.hiddenOnReactions)}
38
+ >
34
39
  <PreviewBody />
35
40
  <Space />
36
41
  {isSkinToneInPreview ? <SkinTonePickerMenu /> : null}
@@ -1,5 +1,7 @@
1
+ import { cx } from 'flairup';
1
2
  import * as React from 'react';
2
3
 
4
+ import { commonInteractionStyles } from '../../Stylesheet/stylesheet';
3
5
  import Relative from '../Layout/Relative';
4
6
  import { CategoryNavigation } from '../navigation/CategoryNavigation';
5
7
 
@@ -7,7 +9,9 @@ import { SearchContainer } from './Search/Search';
7
9
 
8
10
  export function Header() {
9
11
  return (
10
- <Relative className="epr-header">
12
+ <Relative
13
+ className={cx('epr-header', commonInteractionStyles.hiddenOnReactions)}
14
+ >
11
15
  <SearchContainer />
12
16
  <CategoryNavigation />
13
17
  </Relative>
@@ -13,7 +13,10 @@ import { useKeyboardNavigation } from '../../hooks/useKeyboardNavigation';
13
13
  import { useOnFocus } from '../../hooks/useOnFocus';
14
14
  import { Theme } from '../../types/exposedTypes';
15
15
  import { usePickerMainRef } from '../context/ElementRefContext';
16
- import { PickerContextProvider } from '../context/PickerContext';
16
+ import {
17
+ PickerContextProvider,
18
+ useReactionsModeState
19
+ } from '../context/PickerContext';
17
20
 
18
21
  type Props = Readonly<{
19
22
  children: React.ReactNode;
@@ -35,6 +38,7 @@ type RootProps = Readonly<{
35
38
  }>;
36
39
 
37
40
  function PickerRootElement({ children }: RootProps) {
41
+ const [reactionsMode] = useReactionsModeState();
38
42
  const theme = useThemeConfig();
39
43
  const searchModeActive = useIsSearchMode();
40
44
  const PickerMainRef = usePickerMainRef();
@@ -44,6 +48,8 @@ function PickerRootElement({ children }: RootProps) {
44
48
  useKeyboardNavigation();
45
49
  useOnFocus();
46
50
 
51
+ const { width, height, ...styleProps } = style || {};
52
+
47
53
  return (
48
54
  <aside
49
55
  className={cx(
@@ -54,10 +60,14 @@ function PickerRootElement({ children }: RootProps) {
54
60
  {
55
61
  [ClassNames.searchActive]: searchModeActive
56
62
  },
63
+ reactionsMode && styles.reactionsMenu,
57
64
  className
58
65
  )}
59
66
  ref={PickerMainRef}
60
- style={style}
67
+ style={{
68
+ ...styleProps,
69
+ ...(!reactionsMode && { height, width })
70
+ }}
61
71
  >
62
72
  {children}
63
73
  </aside>
@@ -75,6 +85,7 @@ const DarkTheme = {
75
85
  '--epr-category-label-bg-color': 'var(--epr-dark-category-label-bg-color)',
76
86
  '--epr-picker-border-color': 'var(--epr-dark-picker-border-color)',
77
87
  '--epr-bg-color': 'var(--epr-dark-bg-color)',
88
+ '--epr-reactions-bg-color': 'var(--epr-dark-reactions-bg-color)',
78
89
  '--epr-search-input-bg-color-active':
79
90
  'var(--epr-dark-search-input-bg-color-active)',
80
91
  '--epr-emoji-variation-indicator-color':
@@ -97,6 +108,7 @@ const styles = stylesheet.create({
97
108
  borderColor: 'var(--epr-picker-border-color)',
98
109
  backgroundColor: 'var(--epr-bg-color)',
99
110
  overflow: 'hidden',
111
+ transition: 'all 0.3s ease-in-out, background-color 0.1s ease-in-out',
100
112
  // @ts-expect-error - need to work on this
101
113
  '*': {
102
114
  boxSizing: 'border-box',
@@ -112,6 +124,7 @@ const styles = stylesheet.create({
112
124
  '--epr-search-input-bg-color': '#f6f6f6',
113
125
  '--epr-picker-border-color': '#e7e7e7',
114
126
  '--epr-bg-color': '#fff',
127
+ '--epr-reactions-bg-color': '#ffffff90',
115
128
  '--epr-category-icon-active-color': '#6aa8de',
116
129
  '--epr-skin-tone-picker-menu-color': '#ffffff95',
117
130
 
@@ -187,6 +200,7 @@ const styles = stylesheet.create({
187
200
  '--epr-dark-category-label-bg-color': '#222222e6',
188
201
  '--epr-dark-picker-border-color': '#151617',
189
202
  '--epr-dark-bg-color': '#222222',
203
+ '--epr-dark-reactions-bg-color': '#22222290',
190
204
  '--epr-dark-search-input-bg-color-active': 'var(--epr-dark)',
191
205
  '--epr-dark-emoji-variation-indicator-color': '#444',
192
206
  '--epr-dark-category-icon-active-color': '#3271b7',
@@ -202,5 +216,16 @@ const styles = stylesheet.create({
202
216
  darkTheme: {
203
217
  '.': ClassNames.darkTheme,
204
218
  '--': DarkTheme
219
+ },
220
+ reactionsMenu: {
221
+ '.': 'epr-reactions',
222
+ height: '50px',
223
+ display: 'inline-flex',
224
+ backgroundColor: 'var(--epr-reactions-bg-color)',
225
+ // @ts-expect-error - need to work on this
226
+ backdropFilter: 'blur(8px)',
227
+ '--': {
228
+ '--epr-picker-border-radius': '50px'
229
+ }
205
230
  }
206
231
  });
@@ -1,5 +1,6 @@
1
- import * as React from 'react'
1
+ import * as React from 'react';
2
2
 
3
+ import { DEFAULT_REACTIONS } from '../components/Reactions/DEFAULT_REACTIONS';
3
4
  import { GetEmojiUrl } from '../components/emoji/BaseEmojiProps';
4
5
  import {
5
6
  setCustomEmojis,
@@ -85,7 +86,9 @@ export function basePickerConfig(): PickerConfigInternal {
85
86
  suggestedEmojisMode: SuggestionMode.FREQUENT,
86
87
  theme: Theme.LIGHT,
87
88
  unicodeToHide: new Set<string>(KNOWN_FAILING_EMOJIS),
88
- width: 350
89
+ width: 350,
90
+ reactionsDefaultOpen: false,
91
+ reactions: DEFAULT_REACTIONS
89
92
  };
90
93
  }
91
94
 
@@ -111,6 +114,8 @@ export type PickerConfigInternal = {
111
114
  skinTonePickerLocation: SkinTonePickerLocation;
112
115
  unicodeToHide: Set<string>;
113
116
  customEmojis: CustomEmoji[];
117
+ reactionsDefaultOpen: boolean;
118
+ reactions: string[];
114
119
  };
115
120
 
116
121
  export type PreviewConfig = {
@@ -127,9 +132,12 @@ const basePreviewConfig: PreviewConfig = {
127
132
 
128
133
  type ConfigExternal = {
129
134
  previewConfig: Partial<PreviewConfig>;
130
- onEmojiClick: (emoji: EmojiClickData, event: MouseEvent) => void;
135
+ onEmojiClick: MouseDownEvent;
136
+ onReactionClick: MouseDownEvent;
131
137
  } & Omit<PickerConfigInternal, 'previewConfig' | 'unicodeToHide'>;
132
138
 
133
139
  export type PickerConfig = Partial<ConfigExternal>;
134
140
 
135
141
  export type PickerDimensions = string | number;
142
+
143
+ export type MouseDownEvent = (emoji: EmojiClickData, event: MouseEvent) => void;