diff --git a/src/DomUtils/classNames.ts b/src/DomUtils/classNames.ts
index f5461ba8..9542027d 100644
--- a/src/DomUtils/classNames.ts
+++ b/src/DomUtils/classNames.ts
@@ -4,6 +4,7 @@ export enum ClassNames {
hidden = 'epr-hidden',
visible = 'epr-visible',
active = 'epr-active',
+ selected = 'epr-selected',
emoji = 'epr-emoji',
category = 'epr-emoji-category',
label = 'epr-emoji-category-label',
diff --git a/src/Stylesheet/stylesheet.tsx b/src/Stylesheet/stylesheet.tsx
index 02fd2a28..2f368623 100644
--- a/src/Stylesheet/stylesheet.tsx
+++ b/src/Stylesheet/stylesheet.tsx
@@ -13,11 +13,19 @@ const hidden = {
overflow: 'hidden'
};
+const selected = {
+ backgroundColor: 'var(--epr-emoji-select-color)'
+};
+
export const commonStyles = stylesheet.create({
hidden: {
'.': ClassNames.hidden,
...hidden
- }
+ },
+ selected: {
+ '.': ClassNames.selected,
+ ...selected
+ },
});
export const PickerStyleTag = React.memo(function PickerStyleTag() {
diff --git a/src/components/Reactions/Reactions.tsx b/src/components/Reactions/Reactions.tsx
index a78493ad..3c91d158 100644
--- a/src/components/Reactions/Reactions.tsx
+++ b/src/components/Reactions/Reactions.tsx
@@ -7,7 +7,8 @@ import {
useEmojiStyleConfig,
useReactionsConfig,
useAllowExpandReactions,
- useGetEmojiUrlConfig
+ useGetEmojiUrlConfig,
+ useGetIsSelectedEmojis,
} from '../../config/useConfig';
import { DataEmoji } from '../../dataUtils/DataTypes';
import { emojiByUnified } from '../../dataUtils/emojiSelectors';
@@ -26,6 +27,7 @@ export function Reactions() {
const emojiStyle = useEmojiStyleConfig();
const allowExpandReactions = useAllowExpandReactions();
const getEmojiUrl = useGetEmojiUrlConfig();
+ const getIsSelectedEmojis = useGetIsSelectedEmojis();
if (!reactionsOpen) {
return null;
@@ -46,6 +48,7 @@ export function Reactions() {
className={cx(styles.emojiButton)}
noBackground
getEmojiUrl={getEmojiUrl}
+ selected={getIsSelectedEmojis(reaction)}
/>
))}
diff --git a/src/components/body/EmojiList.tsx b/src/components/body/EmojiList.tsx
index 0fc492cc..4d562014 100644
--- a/src/components/body/EmojiList.tsx
+++ b/src/components/body/EmojiList.tsx
@@ -12,6 +12,7 @@ import {
useCategoriesConfig,
useEmojiStyleConfig,
useGetEmojiUrlConfig,
+ useGetIsSelectedEmojis,
useLazyLoadEmojisConfig,
useSkinTonesDisabledConfig
} from '../../config/useConfig';
@@ -71,6 +72,7 @@ function RenderCategory({
const isEmojiDisallowed = useIsEmojiDisallowed();
const getEmojiUrl = useGetEmojiUrlConfig();
const showVariations = !useSkinTonesDisabledConfig();
+ const getIsSelectedEmojis = useGetIsSelectedEmojis();
// Small trick to defer the rendering of all emoji categories until the first category is visible
// This way the user gets to actually see something and not wait for the whole picker to render.
@@ -110,6 +112,7 @@ function RenderCategory({
emojiStyle={emojiStyle}
lazyLoad={lazyLoadEmojis}
getEmojiUrl={getEmojiUrl}
+ selected={getIsSelectedEmojis(unified)}
/>
);
});
diff --git a/src/components/body/EmojiVariationPicker.tsx b/src/components/body/EmojiVariationPicker.tsx
index f2c80f22..98fd32fd 100644
--- a/src/components/body/EmojiVariationPicker.tsx
+++ b/src/components/body/EmojiVariationPicker.tsx
@@ -13,7 +13,8 @@ import {
import { darkMode, stylesheet } from '../../Stylesheet/stylesheet';
import {
useEmojiStyleConfig,
- useGetEmojiUrlConfig
+ useGetEmojiUrlConfig,
+ useGetIsSelectedEmojis
} from '../../config/useConfig';
import {
emojiHasVariations,
@@ -49,6 +50,7 @@ export function EmojiVariationPicker() {
const setAnchoredEmojiRef = useSetAnchoredEmojiRef();
const getPointerStyle = usePointerStyle(VariationPickerRef);
const getEmojiUrl = useGetEmojiUrlConfig();
+ const getIsSelectedEmojis = useGetIsSelectedEmojis();
const button = buttonFromTarget(AnchoredEmojiRef.current);
@@ -98,6 +100,7 @@ export function EmojiVariationPicker() {
emojiStyle={emojiStyle}
showVariations={false}
getEmojiUrl={getEmojiUrl}
+ selected={getIsSelectedEmojis(unified)}
/>
))
: null}
diff --git a/src/components/body/Suggested.tsx b/src/components/body/Suggested.tsx
index 06045366..9c1a0029 100644
--- a/src/components/body/Suggested.tsx
+++ b/src/components/body/Suggested.tsx
@@ -4,6 +4,7 @@ import { CategoryConfig } from '../../config/categoryConfig';
import {
useEmojiStyleConfig,
useGetEmojiUrlConfig,
+ useGetIsSelectedEmojis,
useSuggestedEmojisModeConfig
} from '../../config/useConfig';
import { emojiByUnified } from '../../dataUtils/emojiSelectors';
@@ -29,6 +30,7 @@ export function Suggested({ categoryConfig }: Props) {
[suggestedUpdated, suggestedEmojisModeConfig]
);
const emojiStyle = useEmojiStyleConfig();
+ const getIsSelectedEmojis = useGetIsSelectedEmojis();
if (!isMounted) {
return null;
@@ -55,6 +57,7 @@ export function Suggested({ categoryConfig }: Props) {
emoji={emoji}
key={suggestedItem.unified}
getEmojiUrl={getEmojiUrl}
+ selected={getIsSelectedEmojis(suggestedItem.unified)}
/>
);
})}
diff --git a/src/components/context/PickerConfigContext.tsx b/src/components/context/PickerConfigContext.tsx
index 32a5faf9..3bed5c45 100644
--- a/src/components/context/PickerConfigContext.tsx
+++ b/src/components/context/PickerConfigContext.tsx
@@ -58,7 +58,8 @@ export function useSetConfig(config: PickerConfig) {
config.width,
config.searchDisabled,
config.skinTonePickerLocation,
- config.allowExpandReactions
+ config.allowExpandReactions,
+ config.selectedEmojis
]);
return mergedConfig;
diff --git a/src/components/emoji/ClickableEmojiButton.tsx b/src/components/emoji/ClickableEmojiButton.tsx
index 50dc9888..63b4275a 100644
--- a/src/components/emoji/ClickableEmojiButton.tsx
+++ b/src/components/emoji/ClickableEmojiButton.tsx
@@ -11,6 +11,7 @@ import { Button } from '../atoms/Button';
type ClickableEmojiButtonProps = Readonly<{
hidden?: boolean;
+ selected?: boolean;
showVariations?: boolean;
hiddenOnSearch?: boolean;
emojiNames: string[];
@@ -25,6 +26,7 @@ export function ClickableEmojiButton({
emojiNames,
unified,
hidden,
+ selected,
hiddenOnSearch,
showVariations = true,
hasVariations,
@@ -37,6 +39,7 @@ export function ClickableEmojiButton({
className={cx(
styles.emoji,
hidden && commonStyles.hidden,
+ selected && commonStyles.selected,
hiddenOnSearch && commonInteractionStyles.hiddenOnSearch,
{
[ClassNames.visible]: !hidden && !hiddenOnSearch
@@ -80,7 +83,13 @@ const styles = stylesheet.create({
},
':focus': {
backgroundColor: 'var(--epr-focus-bg-color)'
- }
+ },
+ [`&.${ClassNames.selected}`]: {
+ backgroundColor: 'var(--epr-selected-bg-color)',
+ ':focus': {
+ backgroundColor: 'var(--epr-selected-bg-color)'
+ },
+ },
},
noBackground: {
background: 'none',
@@ -91,7 +100,7 @@ const styles = stylesheet.create({
':focus': {
backgroundColor: 'transparent',
background: 'none'
- }
+ },
},
hasVariations: {
'.': ClassNames.emojiHasVariations,
diff --git a/src/components/emoji/Emoji.tsx b/src/components/emoji/Emoji.tsx
index 9cffe281..c245ee84 100644
--- a/src/components/emoji/Emoji.tsx
+++ b/src/components/emoji/Emoji.tsx
@@ -10,6 +10,7 @@ import { ViewOnlyEmoji } from './ViewOnlyEmoji';
type ClickableEmojiProps = Readonly<
BaseEmojiProps & {
hidden?: boolean;
+ selected?: boolean;
showVariations?: boolean;
hiddenOnSearch?: boolean;
emoji: DataEmoji;
@@ -22,6 +23,7 @@ export function ClickableEmoji({
emoji,
unified,
hidden,
+ selected,
hiddenOnSearch,
emojiStyle,
showVariations = true,
@@ -38,6 +40,7 @@ export function ClickableEmoji({
hasVariations={hasVariations}
showVariations={showVariations}
hidden={hidden}
+ selected={selected}
hiddenOnSearch={hiddenOnSearch}
emojiNames={emojiNames(emoji)}
unified={unified}
diff --git a/src/components/main/PickerMain.tsx b/src/components/main/PickerMain.tsx
index ca957d77..79537c63 100644
--- a/src/components/main/PickerMain.tsx
+++ b/src/components/main/PickerMain.tsx
@@ -123,6 +123,7 @@ const styles = stylesheet.create({
'--epr-hover-bg-color': '#f1f8ff',
'--epr-hover-bg-color-reduced-opacity': '#f1f8ff80',
'--epr-focus-bg-color': '#e0f0ff',
+ '--epr-selected-bg-color': '#6aa8de',
'--epr-text-color': '#858585',
'--epr-search-input-bg-color': '#f6f6f6',
'--epr-picker-border-color': '#e7e7e7',
diff --git a/src/config/compareConfig.ts b/src/config/compareConfig.ts
index 62d60d32..b364042a 100644
--- a/src/config/compareConfig.ts
+++ b/src/config/compareConfig.ts
@@ -23,6 +23,7 @@ export function compareConfig(prev: PickerConfig, next: PickerConfig) {
prev.style === next.style &&
prev.searchDisabled === next.searchDisabled &&
prev.skinTonePickerLocation === next.skinTonePickerLocation &&
+ prev.selectedEmojis === next.selectedEmojis &&
prevCustomEmojis.length === nextCustomEmojis.length
);
}
\ No newline at end of file
diff --git a/src/config/config.ts b/src/config/config.ts
index 5102bf90..76ff89fc 100644
--- a/src/config/config.ts
+++ b/src/config/config.ts
@@ -95,7 +95,8 @@ export function basePickerConfig(): PickerConfigInternal {
reactions: DEFAULT_REACTIONS,
open: true,
allowExpandReactions: true,
- hiddenEmojis: []
+ hiddenEmojis: [],
+ selectedEmojis: []
};
}
@@ -126,6 +127,7 @@ export type PickerConfigInternal = {
open: boolean;
allowExpandReactions: boolean;
hiddenEmojis: string[];
+ selectedEmojis: string[];
};
export type PreviewConfig = {
diff --git a/src/config/useConfig.ts b/src/config/useConfig.ts
index dbc60814..1d3c9a10 100644
--- a/src/config/useConfig.ts
+++ b/src/config/useConfig.ts
@@ -186,3 +186,8 @@ export function useSearchResultsConfig(searchResultsCount: number): string {
return SEARCH_RESULTS_NO_RESULTS_FOUND;
}
+
+export function useGetIsSelectedEmojis(): (emoji: string) => boolean {
+ const { selectedEmojis } = usePickerConfig();
+ return (emoji) => selectedEmojis.includes(emoji);
+}
diff --git a/stories/picker.stories.tsx b/stories/picker.stories.tsx
index 44694ee3..6609b0dd 100644
--- a/stories/picker.stories.tsx
+++ b/stories/picker.stories.tsx
@@ -289,6 +289,24 @@ export const HideEmojisByUnicode = (args: Props) => (
);
+export const SelectedEmojis = () => {
+ const [selectedEmojis, setSelectedEmojis] = useState([]);
+
+ return (
+
+ setSelectedEmojis(
+ selectedEmojis.includes(unified)
+ ? selectedEmojis.filter((emoji) => emoji !== unified)
+ : [...selectedEmojis, unified],
+ )
+ }
+ />
+ );
+};
+
function TemplateDark(args) {
const [open, setOpen] = useState(true);
const [hasBg, setHasBg] = useState(false);