From 4a5a8a9d0a90b02ec9eb7981ed7046aa6b5ac328 Mon Sep 17 00:00:00 2001 From: Thijs Date: Wed, 4 Sep 2024 14:39:10 +0200 Subject: [PATCH] Add support for selected Emojis --- src/DomUtils/classNames.ts | 1 + src/Stylesheet/stylesheet.tsx | 10 +++++++++- src/components/Reactions/Reactions.tsx | 5 ++++- src/components/body/EmojiList.tsx | 3 +++ src/components/body/EmojiVariationPicker.tsx | 5 ++++- src/components/body/Suggested.tsx | 3 +++ src/components/context/PickerConfigContext.tsx | 3 ++- src/components/emoji/ClickableEmojiButton.tsx | 13 +++++++++++-- src/components/emoji/Emoji.tsx | 3 +++ src/components/main/PickerMain.tsx | 1 + src/config/compareConfig.ts | 1 + src/config/config.ts | 4 +++- src/config/useConfig.ts | 5 +++++ stories/picker.stories.tsx | 18 ++++++++++++++++++ 14 files changed, 68 insertions(+), 7 deletions(-) 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) => (