emoji-picker-react 4.0.0-rc.8 → 4.0.0
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.
- package/README.md +176 -43
- package/dist/DomUtils/classNames.d.ts +9 -1
- package/dist/DomUtils/elementPositionInRow.d.ts +10 -0
- package/dist/DomUtils/focusElement.d.ts +5 -0
- package/dist/DomUtils/getActiveElement.d.ts +2 -0
- package/dist/DomUtils/isDarkTheme.d.ts +1 -0
- package/dist/DomUtils/keyboardNavigation.d.ts +8 -0
- package/dist/DomUtils/scrollTo.d.ts +3 -1
- package/dist/DomUtils/selectors.d.ts +24 -11
- package/dist/EmojiPickerReact.d.ts +6 -0
- package/dist/components/ErrorBoundary.d.ts +15 -0
- package/dist/components/body/{RecentlyUsed.d.ts → Suggested.d.ts} +1 -1
- package/dist/components/context/ElementRefContext.d.ts +10 -13
- package/dist/components/context/PickerContext.d.ts +3 -3
- package/dist/components/emoji/Emoji.d.ts +11 -7
- package/dist/components/emoji/ExportedEmoji.d.ts +8 -0
- package/dist/components/footer/Preview.d.ts +1 -1
- package/dist/config/categoryConfig.d.ts +7 -3
- package/dist/config/config.d.ts +18 -15
- package/dist/config/useConfig.d.ts +11 -4
- package/dist/data/skinToneVariations.d.ts +2 -0
- package/dist/dataUtils/emojiSelectors.d.ts +3 -2
- package/dist/dataUtils/suggested.d.ts +11 -0
- package/dist/emoji-picker-react.cjs.development.css +9 -9
- package/dist/emoji-picker-react.cjs.development.js +3774 -3070
- package/dist/emoji-picker-react.cjs.development.js.map +1 -1
- package/dist/emoji-picker-react.cjs.production.min.js +1 -1
- package/dist/emoji-picker-react.cjs.production.min.js.map +1 -1
- package/dist/emoji-picker-react.esm.js +3784 -3080
- package/dist/emoji-picker-react.esm.js.map +1 -1
- package/dist/hooks/preloadEmoji.d.ts +1 -1
- package/dist/hooks/useActiveCategoryScrollDetection.d.ts +1 -2
- package/dist/hooks/useCloseAllOpenToggles.d.ts +2 -4
- package/dist/hooks/useDebouncedState.d.ts +1 -1
- package/dist/hooks/useDisallowMouseMove.d.ts +4 -0
- package/dist/hooks/useEmojiPreviewEvents.d.ts +3 -0
- package/dist/hooks/useFilter.d.ts +3 -3
- package/dist/hooks/useFocus.d.ts +3 -0
- package/dist/hooks/useInitialLoad.d.ts +1 -1
- package/dist/hooks/useIsEmojiHidden.d.ts +7 -1
- package/dist/hooks/useKeyboardNavigation.d.ts +1 -0
- package/dist/hooks/useMouseDownHandlers.d.ts +3 -2
- package/dist/hooks/useOnFocus.d.ts +1 -0
- package/dist/hooks/useScrollCategoryIntoView.d.ts +1 -0
- package/dist/hooks/useSetVariationPicker.d.ts +2 -0
- package/dist/index.d.ts +2 -2
- package/dist/types/exposedTypes.d.ts +5 -1
- package/package.json +13 -5
- package/src/DomUtils/classNames.ts +12 -1
- package/src/DomUtils/elementPositionInRow.ts +167 -0
- package/src/DomUtils/focusElement.ts +35 -0
- package/src/DomUtils/getActiveElement.ts +5 -0
- package/src/DomUtils/isDarkTheme.ts +7 -0
- package/src/DomUtils/keyboardNavigation.ts +161 -0
- package/src/DomUtils/scrollTo.ts +22 -1
- package/src/DomUtils/selectors.ts +200 -41
- package/src/EmojiPickerReact.css +99 -48
- package/src/EmojiPickerReact.tsx +26 -0
- package/src/components/ErrorBoundary.tsx +28 -0
- package/src/components/body/Body.tsx +3 -2
- package/src/components/body/EmojiCategory.css +14 -28
- package/src/components/body/EmojiCategory.tsx +5 -4
- package/src/components/body/EmojiList.tsx +13 -13
- package/src/components/body/EmojiVariationPicker.css +13 -5
- package/src/components/body/EmojiVariationPicker.tsx +25 -17
- package/src/components/body/Suggested.tsx +54 -0
- package/src/components/context/ElementRefContext.tsx +54 -19
- package/src/components/context/PickerContext.tsx +36 -33
- package/src/components/emoji/Emoji.css +42 -12
- package/src/components/emoji/Emoji.tsx +73 -34
- package/src/components/emoji/ExportedEmoji.tsx +30 -0
- package/src/components/footer/Preview.css +1 -1
- package/src/components/footer/Preview.tsx +16 -23
- package/src/components/header/Search.css +7 -7
- package/src/components/header/Search.tsx +31 -7
- package/src/components/header/SkinTonePicker.css +15 -8
- package/src/components/header/SkinTonePicker.tsx +23 -5
- package/src/components/main/PickerMain.css +0 -2
- package/src/components/main/PickerMain.tsx +14 -1
- package/src/components/navigation/CategoryNavigation.css +41 -10
- package/src/components/navigation/CategoryNavigation.tsx +22 -10
- package/src/components/navigation/svg/CategoryNav.svg +343 -379
- package/src/config/categoryConfig.ts +40 -14
- package/src/config/config.ts +59 -13
- package/src/config/useConfig.ts +35 -7
- package/src/data/emojis.json +1 -1
- package/src/data/skinToneVariations.ts +19 -0
- package/src/dataUtils/emojiSelectors.ts +25 -5
- package/src/dataUtils/{recentlyUsed.ts → suggested.ts} +20 -18
- package/src/hooks/preloadEmoji.ts +13 -2
- package/src/hooks/useActiveCategoryScrollDetection.ts +10 -8
- package/src/hooks/useCloseAllOpenToggles.ts +15 -4
- package/src/hooks/useDebouncedState.ts +10 -7
- package/src/hooks/useDisallowMouseMove.ts +47 -0
- package/src/hooks/useEmojiPreviewEvents.ts +103 -0
- package/src/hooks/useFilter.ts +46 -47
- package/src/hooks/useFocus.ts +40 -0
- package/src/hooks/useInitialLoad.ts +1 -0
- package/src/hooks/useIsEmojiHidden.ts +16 -3
- package/src/hooks/useKeyboardNavigation.ts +438 -0
- package/src/hooks/useMouseDownHandlers.ts +97 -89
- package/src/hooks/useOnFocus.ts +46 -0
- package/src/hooks/useOnScroll.ts +2 -2
- package/src/hooks/useScrollCategoryIntoView.ts +28 -0
- package/src/hooks/useSetVariationPicker.ts +28 -0
- package/src/index.tsx +9 -17
- package/src/types/exposedTypes.ts +6 -1
- package/dist/DomUtils/emojiElementRef.d.ts +0 -10
- package/dist/DomUtils/scrollCategoryIntoView.d.ts +0 -1
- package/dist/dataUtils/recentlyUsed.d.ts +0 -11
- package/dist/hooks/useEmojiElements.d.ts +0 -1
- package/dist/hooks/useEmojiMouseEnter.d.ts +0 -3
- package/src/DomUtils/emojiElementRef.ts +0 -42
- package/src/DomUtils/scrollCategoryIntoView.ts +0 -18
- package/src/components/body/RecentlyUsed.tsx +0 -50
- package/src/hooks/useEmojiElements.ts +0 -7
- package/src/hooks/useEmojiMouseEnter.ts +0 -59
|
@@ -1,69 +1,102 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
|
|
4
|
+
import { ClassNames } from '../../DomUtils/classNames';
|
|
4
5
|
import { DataEmoji } from '../../dataUtils/DataTypes';
|
|
6
|
+
import { asEmoji } from '../../dataUtils/asEmoji';
|
|
5
7
|
import {
|
|
8
|
+
emojiByUnified,
|
|
6
9
|
emojiHasVariations,
|
|
7
10
|
emojiName,
|
|
8
|
-
|
|
11
|
+
emojiNames,
|
|
9
12
|
emojiUrlByUnified
|
|
10
13
|
} from '../../dataUtils/emojiSelectors';
|
|
11
14
|
import { parseNativeEmoji } from '../../dataUtils/parseNativeEmoji';
|
|
12
|
-
import { ClassNames } from '../../DomUtils/classNames';
|
|
13
|
-
import { EmojiRef } from '../../DomUtils/emojiElementRef';
|
|
14
15
|
import { EmojiStyle } from '../../types/exposedTypes';
|
|
15
16
|
import { useEmojisThatFailedToLoadState } from '../context/PickerContext';
|
|
16
17
|
import './Emoji.css';
|
|
17
18
|
|
|
18
|
-
type
|
|
19
|
-
|
|
19
|
+
type ClickableEmojiProps = Readonly<
|
|
20
|
+
BaseProps & {
|
|
21
|
+
hidden?: boolean;
|
|
22
|
+
showVariations?: boolean;
|
|
23
|
+
hiddenOnSearch?: boolean;
|
|
24
|
+
emoji: DataEmoji;
|
|
25
|
+
}
|
|
26
|
+
>;
|
|
27
|
+
|
|
28
|
+
type BaseProps = {
|
|
29
|
+
emoji?: DataEmoji;
|
|
20
30
|
emojiStyle: EmojiStyle;
|
|
21
|
-
emoji: DataEmoji;
|
|
22
31
|
unified: string;
|
|
23
|
-
showVariations?: boolean;
|
|
24
32
|
size?: number;
|
|
25
|
-
|
|
26
|
-
}
|
|
33
|
+
lazyLoad?: boolean;
|
|
34
|
+
};
|
|
27
35
|
|
|
28
|
-
export function
|
|
36
|
+
export function ClickableEmoji({
|
|
29
37
|
emoji,
|
|
30
38
|
unified,
|
|
31
39
|
hidden,
|
|
40
|
+
hiddenOnSearch,
|
|
32
41
|
emojiStyle,
|
|
33
42
|
showVariations = true,
|
|
34
43
|
size,
|
|
35
|
-
|
|
36
|
-
}:
|
|
44
|
+
lazyLoad
|
|
45
|
+
}: ClickableEmojiProps) {
|
|
37
46
|
const hasVariations = emojiHasVariations(emoji);
|
|
38
47
|
|
|
39
|
-
const style = {} as React.CSSProperties;
|
|
40
|
-
if (size) {
|
|
41
|
-
style.width = style.height = style.fontSize = `${size}px`;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const base = {
|
|
45
|
-
style,
|
|
46
|
-
unified
|
|
47
|
-
};
|
|
48
|
-
|
|
49
48
|
return (
|
|
50
49
|
<button
|
|
51
|
-
className={clsx(
|
|
50
|
+
className={clsx(ClassNames.emoji, {
|
|
52
51
|
[ClassNames.hidden]: hidden,
|
|
53
|
-
[ClassNames.
|
|
54
|
-
|
|
52
|
+
[ClassNames.hiddenOnSearch]: hiddenOnSearch,
|
|
53
|
+
[ClassNames.visible]: !hidden && !hiddenOnSearch,
|
|
54
|
+
[ClassNames.emojiHasVariatios]: hasVariations && showVariations
|
|
55
55
|
})}
|
|
56
56
|
data-unified={unified}
|
|
57
|
-
data-original={emojiUnified(emoji)}
|
|
58
57
|
// @ts-ignore - let's ignore the fact this is not a real react ref, ok?
|
|
59
|
-
|
|
58
|
+
aria-label={emojiName(emoji)}
|
|
59
|
+
data-full-name={emojiNames(emoji)}
|
|
60
60
|
>
|
|
61
|
+
<ViewOnlyEmoji
|
|
62
|
+
unified={unified}
|
|
63
|
+
emoji={emoji}
|
|
64
|
+
size={size}
|
|
65
|
+
emojiStyle={emojiStyle}
|
|
66
|
+
lazyLoad={lazyLoad}
|
|
67
|
+
/>
|
|
68
|
+
</button>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function ViewOnlyEmoji({
|
|
73
|
+
emoji,
|
|
74
|
+
unified,
|
|
75
|
+
emojiStyle,
|
|
76
|
+
size,
|
|
77
|
+
lazyLoad
|
|
78
|
+
}: BaseProps) {
|
|
79
|
+
const style = {} as React.CSSProperties;
|
|
80
|
+
if (size) {
|
|
81
|
+
style.width = style.height = style.fontSize = `${size}px`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const emojiToRender = emoji ? emoji : emojiByUnified(unified);
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<>
|
|
61
88
|
{emojiStyle === EmojiStyle.NATIVE ? (
|
|
62
|
-
<NativeEmoji {
|
|
89
|
+
<NativeEmoji unified={unified} style={style} />
|
|
63
90
|
) : (
|
|
64
|
-
<EmojiImg
|
|
91
|
+
<EmojiImg
|
|
92
|
+
unified={unified}
|
|
93
|
+
style={style}
|
|
94
|
+
emoji={asEmoji(emojiToRender)}
|
|
95
|
+
emojiStyle={emojiStyle}
|
|
96
|
+
lazyLoad={lazyLoad}
|
|
97
|
+
/>
|
|
65
98
|
)}
|
|
66
|
-
|
|
99
|
+
</>
|
|
67
100
|
);
|
|
68
101
|
}
|
|
69
102
|
|
|
@@ -75,7 +108,11 @@ function NativeEmoji({
|
|
|
75
108
|
style: React.CSSProperties;
|
|
76
109
|
}) {
|
|
77
110
|
return (
|
|
78
|
-
<span
|
|
111
|
+
<span
|
|
112
|
+
className={clsx(ClassNames.external, 'epr-emoji-native')}
|
|
113
|
+
data-unified={unified}
|
|
114
|
+
style={style}
|
|
115
|
+
>
|
|
79
116
|
{parseNativeEmoji(unified)}
|
|
80
117
|
</span>
|
|
81
118
|
);
|
|
@@ -85,12 +122,14 @@ function EmojiImg({
|
|
|
85
122
|
emoji,
|
|
86
123
|
unified,
|
|
87
124
|
emojiStyle,
|
|
88
|
-
style
|
|
125
|
+
style,
|
|
126
|
+
lazyLoad = false
|
|
89
127
|
}: {
|
|
90
128
|
emoji: DataEmoji;
|
|
91
129
|
unified: string;
|
|
92
130
|
emojiStyle: EmojiStyle;
|
|
93
131
|
style: React.CSSProperties;
|
|
132
|
+
lazyLoad?: boolean;
|
|
94
133
|
}) {
|
|
95
134
|
const [, setEmojisThatFailedToLoad] = useEmojisThatFailedToLoadState();
|
|
96
135
|
|
|
@@ -98,8 +137,8 @@ function EmojiImg({
|
|
|
98
137
|
<img
|
|
99
138
|
src={emojiUrlByUnified(emojiStyle, unified)}
|
|
100
139
|
alt={emojiName(emoji)}
|
|
101
|
-
className=
|
|
102
|
-
loading=
|
|
140
|
+
className={clsx(ClassNames.external, 'epr-emoji-img')}
|
|
141
|
+
loading={lazyLoad ? 'lazy' : 'eager'}
|
|
103
142
|
onError={onError}
|
|
104
143
|
style={style}
|
|
105
144
|
/>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { EmojiStyle } from '../../types/exposedTypes';
|
|
4
|
+
|
|
5
|
+
import { ViewOnlyEmoji } from './Emoji';
|
|
6
|
+
|
|
7
|
+
export function ExportedEmoji({
|
|
8
|
+
unified,
|
|
9
|
+
size = 32,
|
|
10
|
+
emojiStyle = EmojiStyle.APPLE,
|
|
11
|
+
lazyLoad = false
|
|
12
|
+
}: {
|
|
13
|
+
unified: string;
|
|
14
|
+
emojiStyle?: EmojiStyle;
|
|
15
|
+
size?: number;
|
|
16
|
+
lazyLoad?: boolean;
|
|
17
|
+
}) {
|
|
18
|
+
if (!unified) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<ViewOnlyEmoji
|
|
24
|
+
unified={unified}
|
|
25
|
+
size={size}
|
|
26
|
+
emojiStyle={emojiStyle}
|
|
27
|
+
lazyLoad={lazyLoad}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -1,37 +1,34 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
useEmojiStyleConfig,
|
|
6
|
-
useShowPreviewConfig
|
|
7
|
-
} from '../../config/useConfig';
|
|
4
|
+
import { useEmojiStyleConfig, usePreviewConfig } from '../../config/useConfig';
|
|
8
5
|
import { asEmoji } from '../../dataUtils/asEmoji';
|
|
9
6
|
import {
|
|
10
7
|
emojiByUnified,
|
|
11
8
|
emojiName,
|
|
12
9
|
emojiUnified
|
|
13
10
|
} from '../../dataUtils/emojiSelectors';
|
|
14
|
-
import {
|
|
11
|
+
import { useEmojiPreviewEvents } from '../../hooks/useEmojiPreviewEvents';
|
|
15
12
|
import Flex from '../Layout/Flex';
|
|
16
|
-
import { Emoji } from '../emoji/Emoji';
|
|
17
|
-
import './Preview.css';
|
|
18
13
|
import { useEmojiVariationPickerState } from '../context/PickerContext';
|
|
14
|
+
import { ViewOnlyEmoji } from '../emoji/Emoji';
|
|
15
|
+
import './Preview.css';
|
|
19
16
|
|
|
20
17
|
export function Preview() {
|
|
21
|
-
const
|
|
22
|
-
const [
|
|
18
|
+
const previewConfig = usePreviewConfig();
|
|
19
|
+
const [previewEmoji, setPreviewEmoji] = useState<PreviewEmoji>(null);
|
|
23
20
|
const emojiStyle = useEmojiStyleConfig();
|
|
24
21
|
const [variationPickerEmoji] = useEmojiVariationPickerState();
|
|
25
22
|
|
|
26
|
-
|
|
23
|
+
useEmojiPreviewEvents(previewConfig.showPreview, setPreviewEmoji);
|
|
27
24
|
|
|
28
|
-
if (!showPreview) {
|
|
25
|
+
if (!previewConfig.showPreview) {
|
|
29
26
|
return null;
|
|
30
27
|
}
|
|
31
28
|
|
|
32
|
-
const emoji = emojiByUnified(
|
|
29
|
+
const emoji = emojiByUnified(previewEmoji?.originalUnified);
|
|
33
30
|
|
|
34
|
-
const show = emoji != null &&
|
|
31
|
+
const show = emoji != null && previewEmoji != null;
|
|
35
32
|
|
|
36
33
|
return (
|
|
37
34
|
<Flex className="epr-preview">
|
|
@@ -41,31 +38,27 @@ export function Preview() {
|
|
|
41
38
|
|
|
42
39
|
function PreviewContent() {
|
|
43
40
|
const defaultEmoji = asEmoji(
|
|
44
|
-
variationPickerEmoji ?? emojiByUnified(
|
|
41
|
+
variationPickerEmoji ?? emojiByUnified(previewConfig.defaultEmoji)
|
|
45
42
|
);
|
|
46
43
|
const defaultText = variationPickerEmoji
|
|
47
44
|
? emojiName(variationPickerEmoji)
|
|
48
|
-
:
|
|
45
|
+
: previewConfig.defaultCaption;
|
|
49
46
|
|
|
50
47
|
return (
|
|
51
48
|
<>
|
|
52
49
|
<div>
|
|
53
50
|
{show ? (
|
|
54
|
-
<
|
|
55
|
-
unified={
|
|
51
|
+
<ViewOnlyEmoji
|
|
52
|
+
unified={previewEmoji?.unified as string}
|
|
56
53
|
emoji={asEmoji(emoji)}
|
|
57
|
-
showVariations={false}
|
|
58
54
|
emojiStyle={emojiStyle}
|
|
59
|
-
hidden={false}
|
|
60
55
|
size={45}
|
|
61
56
|
/>
|
|
62
57
|
) : defaultEmoji ? (
|
|
63
|
-
<
|
|
58
|
+
<ViewOnlyEmoji
|
|
64
59
|
unified={emojiUnified(defaultEmoji)}
|
|
65
60
|
emoji={defaultEmoji}
|
|
66
|
-
showVariations={false}
|
|
67
61
|
emojiStyle={emojiStyle}
|
|
68
|
-
hidden={false}
|
|
69
62
|
size={45}
|
|
70
63
|
/>
|
|
71
64
|
) : null}
|
|
@@ -82,7 +75,7 @@ export function Preview() {
|
|
|
82
75
|
}
|
|
83
76
|
}
|
|
84
77
|
|
|
85
|
-
export type
|
|
78
|
+
export type PreviewEmoji = null | {
|
|
86
79
|
unified: string;
|
|
87
80
|
originalUnified: string;
|
|
88
81
|
};
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
.EmojiPickerReact .epr-search-container input.epr-search {
|
|
8
8
|
outline: none;
|
|
9
9
|
transition: all 0.2s ease-in-out;
|
|
10
|
-
color: var(--epr-search-
|
|
10
|
+
color: var(--epr-search-input-text-color);
|
|
11
11
|
border-radius: var(--epr-search-input-border-radius);
|
|
12
12
|
padding: var(--epr-search-input-padding);
|
|
13
13
|
height: var(--epr-search-input-height);
|
|
14
|
-
background-color: var(--epr-search-
|
|
15
|
-
border: 1px solid var(--epr-search-
|
|
14
|
+
background-color: var(--epr-search-input-bg-color);
|
|
15
|
+
border: 1px solid var(--epr-search-input-bg-color);
|
|
16
16
|
width: 100%;
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
background-repeat: no-repeat;
|
|
25
25
|
position: absolute;
|
|
26
26
|
top: 50%;
|
|
27
|
-
right:
|
|
27
|
+
right: var(--epr-search-bar-inner-padding);
|
|
28
28
|
transform: translateY(-50%);
|
|
29
29
|
background-size: 20px;
|
|
30
30
|
}
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
content: '';
|
|
49
49
|
position: absolute;
|
|
50
50
|
top: 50%;
|
|
51
|
-
left:
|
|
51
|
+
left: var(--epr-search-bar-inner-padding);
|
|
52
52
|
transform: translateY(-50%);
|
|
53
53
|
width: 20px;
|
|
54
54
|
height: 20px;
|
|
@@ -63,10 +63,10 @@
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
.EmojiPickerReact .epr-search-container input.epr-search::placeholder {
|
|
66
|
-
color: var(--epr-search-
|
|
66
|
+
color: var(--epr-search-input-placeholder-color);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
.EmojiPickerReact .epr-search-container input.epr-search:focus {
|
|
70
|
-
background-color: var(--epr-search-
|
|
70
|
+
background-color: var(--epr-search-input-bg-color-active);
|
|
71
71
|
border: 1px solid var(--epr-search-border-color);
|
|
72
72
|
}
|
|
@@ -1,30 +1,43 @@
|
|
|
1
|
+
import clsx from 'clsx';
|
|
1
2
|
import * as React from 'react';
|
|
3
|
+
import { useState } from 'react';
|
|
2
4
|
|
|
3
5
|
import {
|
|
4
6
|
useAutoFocusSearchConfig,
|
|
5
7
|
useSearchPlaceHolderConfig
|
|
6
8
|
} from '../../config/useConfig';
|
|
7
9
|
import { useCloseAllOpenToggles } from '../../hooks/useCloseAllOpenToggles';
|
|
8
|
-
import { useFilter } from '../../hooks/useFilter';
|
|
10
|
+
import { useClearSearch, useFilter } from '../../hooks/useFilter';
|
|
9
11
|
import Relative from '../Layout/Relative';
|
|
12
|
+
import { useSearchInputRef } from '../context/ElementRefContext';
|
|
13
|
+
|
|
10
14
|
import './Search.css';
|
|
11
15
|
|
|
12
16
|
export function Search() {
|
|
13
|
-
const
|
|
14
|
-
|
|
17
|
+
const [inc, setInc] = useState(0);
|
|
18
|
+
const closeAllOpenToggles = useCloseAllOpenToggles();
|
|
19
|
+
const SearchInputRef = useSearchInputRef();
|
|
20
|
+
const clearSearch = useClearSearch();
|
|
15
21
|
const placeholder = useSearchPlaceHolderConfig();
|
|
16
22
|
const autoFocus = useAutoFocusSearchConfig();
|
|
17
|
-
const { onChange
|
|
23
|
+
const { onChange } = useFilter();
|
|
24
|
+
|
|
25
|
+
const input = SearchInputRef?.current;
|
|
26
|
+
const value = input?.value;
|
|
18
27
|
|
|
19
28
|
return (
|
|
20
29
|
<Relative className="epr-search-container">
|
|
30
|
+
<CssSearch value={value} />
|
|
21
31
|
<input
|
|
32
|
+
// eslint-disable-next-line jsx-a11y/no-autofocus
|
|
22
33
|
autoFocus={autoFocus}
|
|
34
|
+
aria-label={'Type to search for an emoji'}
|
|
23
35
|
onFocus={closeAllOpenToggles}
|
|
24
36
|
className="epr-search"
|
|
25
37
|
type="text"
|
|
26
38
|
placeholder={placeholder}
|
|
27
39
|
onChange={event => {
|
|
40
|
+
setInc(inc + 1);
|
|
28
41
|
setTimeout(() => {
|
|
29
42
|
onChange(event.target.value);
|
|
30
43
|
});
|
|
@@ -32,9 +45,20 @@ export function Search() {
|
|
|
32
45
|
ref={SearchInputRef}
|
|
33
46
|
/>
|
|
34
47
|
<div className="epr-icn-search" />
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
48
|
+
<button
|
|
49
|
+
className={clsx('epr-btn-clear-search', 'epr-visible-on-search-only')}
|
|
50
|
+
onClick={clearSearch}
|
|
51
|
+
/>
|
|
38
52
|
</Relative>
|
|
39
53
|
);
|
|
40
54
|
}
|
|
55
|
+
|
|
56
|
+
function CssSearch({ value }: { value: undefined | string }) {
|
|
57
|
+
return value ? (
|
|
58
|
+
<style>{`
|
|
59
|
+
.EmojiPickerReact button.epr-emoji:not([data-full-name*="${value}"]) {
|
|
60
|
+
display: none;
|
|
61
|
+
}
|
|
62
|
+
`}</style>
|
|
63
|
+
) : null;
|
|
64
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
.EmojiPickerReact .epr-skin-tones {
|
|
2
|
-
|
|
2
|
+
--epr-skin-tone-size: 16px;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.EmojiPickerReact .epr-skin-tones {
|
|
3
6
|
display: flex;
|
|
4
7
|
align-items: center;
|
|
5
8
|
justify-content: flex-end;
|
|
@@ -13,8 +16,8 @@
|
|
|
13
16
|
|
|
14
17
|
.EmojiPickerReact .epr-skin-tone-select {
|
|
15
18
|
position: relative;
|
|
16
|
-
width:
|
|
17
|
-
height:
|
|
19
|
+
width: var(--epr-skin-tone-size);
|
|
20
|
+
height: var(--epr-skin-tone-size);
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
.EmojiPickerReact .epr-skin-tones.open .epr-tone {
|
|
@@ -26,7 +29,7 @@
|
|
|
26
29
|
opacity: 0;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
|
-
.EmojiPickerReact .epr-skin-tones .epr-tone.active {
|
|
32
|
+
.EmojiPickerReact .epr-skin-tones .epr-tone.epr-active {
|
|
30
33
|
z-index: 1;
|
|
31
34
|
}
|
|
32
35
|
|
|
@@ -34,8 +37,8 @@
|
|
|
34
37
|
display: block;
|
|
35
38
|
cursor: pointer;
|
|
36
39
|
border-radius: 4px;
|
|
37
|
-
height:
|
|
38
|
-
width:
|
|
40
|
+
height: var(--epr-skin-tone-size);
|
|
41
|
+
width: var(--epr-skin-tone-size);
|
|
39
42
|
position: absolute;
|
|
40
43
|
right: 0;
|
|
41
44
|
transition: transform 0.3s ease-in-out, opacity 0.5s ease-in-out;
|
|
@@ -47,10 +50,14 @@
|
|
|
47
50
|
box-shadow: 0 0 0 5px var(--epr-active-skin-hover-color);
|
|
48
51
|
}
|
|
49
52
|
|
|
53
|
+
.EmojiPickerReact .epr-skin-tones .epr-tone:focus {
|
|
54
|
+
box-shadow: 0 0 0 5px var(--epr-focus-bg-color);
|
|
55
|
+
}
|
|
56
|
+
|
|
50
57
|
.EmojiPickerReact
|
|
51
58
|
.epr-skin-tones.open
|
|
52
59
|
.epr-skin-tone-select
|
|
53
|
-
.epr-tone.active:after {
|
|
60
|
+
.epr-tone.epr-active:after {
|
|
54
61
|
content: '';
|
|
55
62
|
position: absolute;
|
|
56
63
|
top: -2px;
|
|
@@ -61,7 +68,7 @@
|
|
|
61
68
|
border: 1px solid var(--epr-active-skin-tone-indicator-border-color);
|
|
62
69
|
}
|
|
63
70
|
|
|
64
|
-
.EmojiPickerReact .epr-skin-tones .epr-tone.active {
|
|
71
|
+
.EmojiPickerReact .epr-skin-tones .epr-tone.epr-active {
|
|
65
72
|
opacity: 1;
|
|
66
73
|
z-index: 1;
|
|
67
74
|
}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
|
|
4
|
+
import { ClassNames } from '../../DomUtils/classNames';
|
|
4
5
|
import { useSkinTonesDisabledConfig } from '../../config/useConfig';
|
|
5
|
-
import skinToneVariations
|
|
6
|
+
import skinToneVariations, {
|
|
7
|
+
skinTonesNamed
|
|
8
|
+
} from '../../data/skinToneVariations';
|
|
6
9
|
import { useCloseAllOpenToggles } from '../../hooks/useCloseAllOpenToggles';
|
|
10
|
+
import { useFocusSearchInput } from '../../hooks/useFocus';
|
|
11
|
+
import { SkinTones } from '../../types/exposedTypes';
|
|
7
12
|
import Relative from '../Layout/Relative';
|
|
13
|
+
import { useSkinTonePickerRef } from '../context/ElementRefContext';
|
|
8
14
|
import {
|
|
9
15
|
useActiveSkinToneState,
|
|
10
16
|
useSkinToneFanOpenState
|
|
@@ -12,10 +18,12 @@ import {
|
|
|
12
18
|
import './SkinTonePicker.css';
|
|
13
19
|
|
|
14
20
|
export function SkinTonePicker() {
|
|
21
|
+
const SkinTonePickerRef = useSkinTonePickerRef();
|
|
15
22
|
const isDisabled = useSkinTonesDisabledConfig();
|
|
16
23
|
const [isOpen, setIsOpen] = useSkinToneFanOpenState();
|
|
17
24
|
const [activeSkinTone, setActiveSkinTone] = useActiveSkinToneState();
|
|
18
|
-
const
|
|
25
|
+
const closeAllOpenToggles = useCloseAllOpenToggles();
|
|
26
|
+
const focusSearchInput = useFocusSearchInput();
|
|
19
27
|
|
|
20
28
|
if (isDisabled) {
|
|
21
29
|
return null;
|
|
@@ -27,7 +35,7 @@ export function SkinTonePicker() {
|
|
|
27
35
|
open: isOpen
|
|
28
36
|
})}
|
|
29
37
|
>
|
|
30
|
-
<div className="epr-skin-tone-select"
|
|
38
|
+
<div className="epr-skin-tone-select" ref={SkinTonePickerRef}>
|
|
31
39
|
{skinToneVariations.map((skinToneVariation, i) => {
|
|
32
40
|
const active = skinToneVariation === activeSkinTone;
|
|
33
41
|
return (
|
|
@@ -39,13 +47,23 @@ export function SkinTonePicker() {
|
|
|
39
47
|
)
|
|
40
48
|
}}
|
|
41
49
|
onClick={() => {
|
|
42
|
-
|
|
50
|
+
if (isOpen) {
|
|
51
|
+
setActiveSkinTone(skinToneVariation);
|
|
52
|
+
focusSearchInput();
|
|
53
|
+
} else {
|
|
54
|
+
setIsOpen(true);
|
|
55
|
+
}
|
|
43
56
|
closeAllOpenToggles();
|
|
44
57
|
}}
|
|
45
58
|
key={skinToneVariation}
|
|
46
59
|
className={clsx(`epr-tone-${skinToneVariation}`, 'epr-tone', {
|
|
47
|
-
active
|
|
60
|
+
[ClassNames.active]: active
|
|
48
61
|
})}
|
|
62
|
+
tabIndex={isOpen ? 0 : -1}
|
|
63
|
+
aria-pressed={active}
|
|
64
|
+
aria-label={`Skin tone ${
|
|
65
|
+
skinTonesNamed[skinToneVariation as SkinTones]
|
|
66
|
+
}`}
|
|
49
67
|
></button>
|
|
50
68
|
);
|
|
51
69
|
})}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
|
|
4
|
-
import { useThemeConfig } from '../../config/useConfig';
|
|
5
4
|
import { ClassNames } from '../../DomUtils/classNames';
|
|
5
|
+
import { usePickerSizeConfig, useThemeConfig } from '../../config/useConfig';
|
|
6
6
|
import useIsSearchMode from '../../hooks/useIsSearchMode';
|
|
7
|
+
import { useKeyboardNavigation } from '../../hooks/useKeyboardNavigation';
|
|
8
|
+
import { useOnFocus } from '../../hooks/useOnFocus';
|
|
7
9
|
import { Theme } from '../../types/exposedTypes';
|
|
8
10
|
import { usePickerMainRef } from '../context/ElementRefContext';
|
|
9
11
|
import { PickerContextProvider } from '../context/PickerContext';
|
|
@@ -29,6 +31,16 @@ function PickerRootElement({ children }: RootProps) {
|
|
|
29
31
|
const theme = useThemeConfig();
|
|
30
32
|
const searchModeActive = useIsSearchMode();
|
|
31
33
|
const PickerMainRef = usePickerMainRef();
|
|
34
|
+
const { height, width } = usePickerSizeConfig();
|
|
35
|
+
|
|
36
|
+
useKeyboardNavigation();
|
|
37
|
+
useOnFocus();
|
|
38
|
+
|
|
39
|
+
const style = {
|
|
40
|
+
height: `${height}px`,
|
|
41
|
+
width: `${width}px`
|
|
42
|
+
};
|
|
43
|
+
|
|
32
44
|
return (
|
|
33
45
|
<aside
|
|
34
46
|
className={clsx('EmojiPickerReact', 'epr-main', {
|
|
@@ -36,6 +48,7 @@ function PickerRootElement({ children }: RootProps) {
|
|
|
36
48
|
'epr-dark-theme': theme === Theme.DARK
|
|
37
49
|
})}
|
|
38
50
|
ref={PickerMainRef}
|
|
51
|
+
style={style}
|
|
39
52
|
>
|
|
40
53
|
{children}
|
|
41
54
|
</aside>
|
|
@@ -15,24 +15,52 @@
|
|
|
15
15
|
outline: none;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
.EmojiPickerReact
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
.EmojiPickerReact .epr-category-nav > button.epr-cat-btn:focus:before {
|
|
19
|
+
content: '';
|
|
20
|
+
position: absolute;
|
|
21
|
+
top: -2px;
|
|
22
|
+
left: -2px;
|
|
23
|
+
right: -2px;
|
|
24
|
+
bottom: -2px;
|
|
25
|
+
border: 2px solid var(--epr-category-icon-active-color);
|
|
26
|
+
border-radius: 50%;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
aside.EmojiPickerReact.epr-main:has(input:not(:placeholder-shown))
|
|
30
|
+
.epr-category-nav {
|
|
25
31
|
opacity: 0.3;
|
|
26
32
|
cursor: default;
|
|
27
33
|
pointer-events: none;
|
|
28
34
|
}
|
|
29
35
|
|
|
36
|
+
.EmojiPickerReact.epr-search-active .epr-category-nav {
|
|
37
|
+
opacity: 0.3;
|
|
38
|
+
cursor: default;
|
|
39
|
+
pointer-events: none;
|
|
40
|
+
}
|
|
41
|
+
aside.EmojiPickerReact.epr-main:has(input:not(:placeholder-shown))
|
|
42
|
+
.epr-category-nav {
|
|
43
|
+
opacity: 0.3;
|
|
44
|
+
cursor: default;
|
|
45
|
+
pointer-events: none;
|
|
46
|
+
}
|
|
47
|
+
.EmojiPickerReact.epr-search-active .epr-category-nav {
|
|
48
|
+
opacity: 0.3;
|
|
49
|
+
cursor: default;
|
|
50
|
+
pointer-events: none;
|
|
51
|
+
}
|
|
52
|
+
aside.EmojiPickerReact.epr-main:has(input:not(:placeholder-shown))
|
|
53
|
+
.epr-category-nav
|
|
54
|
+
> button.epr-cat-btn:hover {
|
|
55
|
+
opacity: 1;
|
|
56
|
+
background-position-y: var(--epr-category-navigation-button-size);
|
|
57
|
+
}
|
|
30
58
|
.EmojiPickerReact:not(.epr-search-active)
|
|
31
59
|
.epr-category-nav
|
|
32
60
|
> button.epr-cat-btn:hover,
|
|
33
61
|
.EmojiPickerReact:not(.epr-search-active)
|
|
34
62
|
.epr-category-nav
|
|
35
|
-
> button.epr-cat-btn.active {
|
|
63
|
+
> button.epr-cat-btn.epr-active {
|
|
36
64
|
opacity: 1;
|
|
37
65
|
background-position-y: var(--epr-category-navigation-button-size);
|
|
38
66
|
}
|
|
@@ -45,15 +73,18 @@
|
|
|
45
73
|
background-position-y: calc(var(--epr-category-navigation-button-size) * 2);
|
|
46
74
|
}
|
|
47
75
|
|
|
76
|
+
aside.EmojiPickerReact.epr-main:has(input:placeholder-shown) .epr-category-nav {
|
|
77
|
+
background-position-y: calc(var(--epr-category-navigation-button-size) * 3);
|
|
78
|
+
}
|
|
48
79
|
.EmojiPickerReact.epr-dark-theme:not(.epr-search-active)
|
|
49
80
|
.epr-category-nav
|
|
50
81
|
> button.epr-cat-btn:hover,
|
|
51
82
|
.EmojiPickerReact.epr-dark-theme:not(.epr-search-active)
|
|
52
83
|
.epr-category-nav
|
|
53
|
-
> button.epr-cat-btn.active {
|
|
84
|
+
> button.epr-cat-btn.epr-active {
|
|
54
85
|
background-position-y: calc(var(--epr-category-navigation-button-size) * 3);
|
|
55
86
|
}
|
|
56
|
-
.EmojiPickerReact button.epr-cat-btn.epr-icn-
|
|
87
|
+
.EmojiPickerReact button.epr-cat-btn.epr-icn-suggested {
|
|
57
88
|
background-position-x: calc(var(--epr-category-navigation-button-size) * -8);
|
|
58
89
|
}
|
|
59
90
|
.EmojiPickerReact button.epr-cat-btn.epr-icn-activities {
|