@fibery/ui-kit 1.12.2 → 1.14.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/package.json +5 -7
- package/src/designSystem.ts +2 -0
- package/src/emoji-picker/app-icon-picker.tsx +54 -0
- package/src/emoji-picker/emoji-index.ts +8 -0
- package/src/emoji-picker/emoji-picker.tsx +75 -0
- package/src/emoji-picker/emoji.tsx +11 -0
- package/src/{emoji-mart → emoji-picker}/icon-emoji-picker.tsx +13 -11
- package/src/emoji-picker/index.tsx +5 -0
- package/src/emoji-picker/lazy-emoji-data-store.tsx +29 -0
- package/src/emoji-picker/lazy-icon-data-store.tsx +55 -0
- package/src/emoji-picker/primitives/category.tsx +107 -0
- package/src/emoji-picker/primitives/content.tsx +48 -0
- package/src/emoji-picker/primitives/emoji.tsx +130 -0
- package/src/emoji-picker/primitives/footer.tsx +12 -0
- package/src/emoji-picker/primitives/grid.tsx +67 -0
- package/src/emoji-picker/primitives/header.tsx +11 -0
- package/src/emoji-picker/primitives/layout.ts +5 -0
- package/src/emoji-picker/primitives/root.tsx +79 -0
- package/src/emoji-picker/primitives/search-provider.tsx +22 -0
- package/src/emoji-picker/primitives/search.tsx +133 -0
- package/src/emoji-picker/primitives/skin-provider.tsx +27 -0
- package/src/emoji-picker/primitives/skin-tone.tsx +99 -0
- package/src/emoji-picker/stores/emoji-data-store.tsx +50 -0
- package/src/emoji-picker/utils/emoji-set.ts +5 -0
- package/src/{emoji-mart → emoji-picker/utils}/emoji-support.ts +6 -10
- package/src/emoji-picker/utils/frequently.ts +82 -0
- package/src/emoji-picker/utils/load-emoji-data.ts +4 -0
- package/src/emoji-picker/utils/store.ts +40 -0
- package/src/icons/ast/AddAfter.ts +8 -0
- package/src/icons/ast/AddBefore.ts +8 -0
- package/src/icons/ast/ArrowBarLeft.ts +8 -0
- package/src/icons/ast/ArrowBarRight.ts +8 -0
- package/src/icons/ast/Columns2.ts +8 -0
- package/src/icons/ast/Columns4.ts +8 -0
- package/src/icons/ast/MessagePlusSquare.ts +8 -0
- package/src/icons/ast/UsbFlashDrive.ts +8 -0
- package/src/icons/ast/index.tsx +8 -0
- package/src/icons/react/AddAfter.tsx +12 -0
- package/src/icons/react/AddBefore.tsx +12 -0
- package/src/icons/react/ArrowBarLeft.tsx +12 -0
- package/src/icons/react/ArrowBarRight.tsx +12 -0
- package/src/icons/react/Columns2.tsx +12 -0
- package/src/icons/react/Columns4.tsx +12 -0
- package/src/icons/react/MessagePlusSquare.tsx +12 -0
- package/src/icons/react/UsbFlashDrive.tsx +12 -0
- package/src/icons/react/index.tsx +8 -0
- package/src/emoji-mart/app-icon-picker.tsx +0 -68
- package/src/emoji-mart/data/get-data.tsx +0 -58
- package/src/emoji-mart/emoji/emoji.tsx +0 -26
- package/src/emoji-mart/emoji/index.tsx +0 -24
- package/src/emoji-mart/emoji-index.ts +0 -5
- package/src/emoji-mart/emoji-mart.tsx +0 -216
- package/src/emoji-mart/emoji-picker.tsx +0 -78
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fibery/ui-kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
4
|
"main": "index.ts",
|
|
5
5
|
"private": false,
|
|
6
6
|
"files": [
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"src/tooltip.tsx",
|
|
24
24
|
"src/BackButton.tsx",
|
|
25
25
|
"src/Button",
|
|
26
|
-
"src/emoji-
|
|
26
|
+
"src/emoji-picker/**/*.ts*",
|
|
27
27
|
"src/a11y-color.ts"
|
|
28
28
|
],
|
|
29
29
|
"license": "UNLICENSED",
|
|
@@ -43,7 +43,6 @@
|
|
|
43
43
|
"color-hash": "1.0.3",
|
|
44
44
|
"d3-shape": "1.3.7",
|
|
45
45
|
"date-fns": "^2.29.2",
|
|
46
|
-
"emoji-mart": "3.0.1",
|
|
47
46
|
"invariant": "2.2.4",
|
|
48
47
|
"lodash": "4.17.21",
|
|
49
48
|
"md5": "2.2.1",
|
|
@@ -60,9 +59,9 @@
|
|
|
60
59
|
"react-windowed-select": "5.0.0",
|
|
61
60
|
"screenfull": "6.0.1",
|
|
62
61
|
"ua-parser-js": "0.7.24",
|
|
63
|
-
"@fibery/helpers": "1.0
|
|
64
|
-
"@fibery/emoji-data": "
|
|
65
|
-
"@fibery/react": "1.0.
|
|
62
|
+
"@fibery/helpers": "1.1.0",
|
|
63
|
+
"@fibery/emoji-data": "2.0.0",
|
|
64
|
+
"@fibery/react": "1.0.3"
|
|
66
65
|
},
|
|
67
66
|
"peerDependencies": {
|
|
68
67
|
"react": "^18.2.0",
|
|
@@ -78,7 +77,6 @@
|
|
|
78
77
|
"@testing-library/user-event": "13.5.0",
|
|
79
78
|
"@types/chroma-js": "2.1.3",
|
|
80
79
|
"@types/color-hash": "1.0.2",
|
|
81
|
-
"@types/emoji-mart": "3.0.8",
|
|
82
80
|
"@types/invariant": "2.2.34",
|
|
83
81
|
"@types/lodash": "4.14.172",
|
|
84
82
|
"@types/prop-types": "15.7.5",
|
package/src/designSystem.ts
CHANGED
|
@@ -273,6 +273,7 @@ const lightColors = {
|
|
|
273
273
|
gridPinnedCellBorderColor: slate.slate8,
|
|
274
274
|
gridDisabledCellBgColor: slate.slate2,
|
|
275
275
|
gridSelectedCellBgColor: indigo.indigo3,
|
|
276
|
+
gridHighlightedCellBgColor: indigo.indigo4,
|
|
276
277
|
gridSelectedCellBorderColor: indigo.indigo5,
|
|
277
278
|
gridSelectedPinnedCellBorderColor: indigo.indigo7,
|
|
278
279
|
gridDisabledSelectedCellBgColor: indigo.indigo4,
|
|
@@ -453,6 +454,7 @@ const darkColors = {
|
|
|
453
454
|
gridPinnedCellBorderColor: slateDark.slate8,
|
|
454
455
|
gridDisabledCellBgColor: slateDark.slate3,
|
|
455
456
|
gridSelectedCellBgColor: indigoDark.indigo3,
|
|
457
|
+
gridHighlightedCellBgColor: indigoDark.indigo5,
|
|
456
458
|
gridSelectedCellBorderColor: indigoDark.indigo6,
|
|
457
459
|
gridSelectedPinnedCellBorderColor: indigoDark.indigo7,
|
|
458
460
|
gridDisabledSelectedCellBgColor: indigoDark.indigo5,
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {EmojiItem} from "@fibery/emoji-data/lib/src";
|
|
2
|
+
import {ColorPicker} from "../ColorPicker";
|
|
3
|
+
import {$TSFixMe} from "../tsfixme";
|
|
4
|
+
import {LazyIconDataStore} from "./lazy-icon-data-store";
|
|
5
|
+
import {EmojiPickerContent} from "./primitives/content";
|
|
6
|
+
import {EmojiPickerFooter} from "./primitives/footer";
|
|
7
|
+
import {EmojiPickerGrid} from "./primitives/grid";
|
|
8
|
+
import {EmojiPickerHeader} from "./primitives/header";
|
|
9
|
+
import {EmojiPickerRoot} from "./primitives/root";
|
|
10
|
+
import {EmojiPickerSearch} from "./primitives/search";
|
|
11
|
+
import {cardTypeColors} from "../designSystem";
|
|
12
|
+
|
|
13
|
+
const i18n = {search: "Search icon...", notfound: "No Icons Found"};
|
|
14
|
+
|
|
15
|
+
export type AppIconPickerProps = {
|
|
16
|
+
color?: string;
|
|
17
|
+
getHoverColor?: (color: string) => string | undefined;
|
|
18
|
+
colors?: string[];
|
|
19
|
+
onColorSelect?: (c: $TSFixMe) => void;
|
|
20
|
+
|
|
21
|
+
onSelect: (emoji: EmojiItem) => void;
|
|
22
|
+
};
|
|
23
|
+
export const AppIconPicker: React.FC<AppIconPickerProps> = ({
|
|
24
|
+
onSelect,
|
|
25
|
+
color,
|
|
26
|
+
colors = cardTypeColors,
|
|
27
|
+
onColorSelect,
|
|
28
|
+
getHoverColor,
|
|
29
|
+
}) => {
|
|
30
|
+
return (
|
|
31
|
+
<EmojiPickerRoot i18n={i18n} onEmojiSelect={onSelect}>
|
|
32
|
+
<EmojiPickerContent emojiHoverColor={color ? getHoverColor?.(color) : undefined} emojiSize={20}>
|
|
33
|
+
<LazyIconDataStore fallback={<div style={{height: "282px"}}></div>}>
|
|
34
|
+
<EmojiPickerHeader>
|
|
35
|
+
<EmojiPickerSearch autoFocus />
|
|
36
|
+
</EmojiPickerHeader>
|
|
37
|
+
|
|
38
|
+
<EmojiPickerGrid />
|
|
39
|
+
</LazyIconDataStore>
|
|
40
|
+
|
|
41
|
+
{onColorSelect ? (
|
|
42
|
+
<EmojiPickerFooter>
|
|
43
|
+
<ColorPicker
|
|
44
|
+
colors={colors}
|
|
45
|
+
getSwatchHoverColor={getHoverColor}
|
|
46
|
+
color={color}
|
|
47
|
+
onChangeComplete={onColorSelect}
|
|
48
|
+
/>
|
|
49
|
+
</EmojiPickerFooter>
|
|
50
|
+
) : null}
|
|
51
|
+
</EmojiPickerContent>
|
|
52
|
+
</EmojiPickerRoot>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import {makeEmojiData} from "@fibery/emoji-data";
|
|
2
|
+
|
|
3
|
+
import data from "@fibery/emoji-data/data/all.json";
|
|
4
|
+
|
|
5
|
+
import {getEmojiSet} from "./utils/emoji-set";
|
|
6
|
+
|
|
7
|
+
// TODO: remove it. Emoji Data should not be accesed like this, prefer consuming it from context
|
|
8
|
+
export const emojiIndex = makeEmojiData(data, {set: getEmojiSet()});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import {EmojiItem} from "@fibery/emoji-data/lib/src";
|
|
2
|
+
import {$TSFixMe} from "../tsfixme";
|
|
3
|
+
import {EmojiPickerRoot} from "./primitives/root";
|
|
4
|
+
import {EmojiPickerContent} from "./primitives/content";
|
|
5
|
+
import {EmojiPickerHeader} from "./primitives/header";
|
|
6
|
+
import {EmojiPickerSearch} from "./primitives/search";
|
|
7
|
+
import {cx, css} from "@linaria/core";
|
|
8
|
+
import {EmojiPickerSkinTone} from "./primitives/skin-tone";
|
|
9
|
+
import {EmojiPickerGrid} from "./primitives/grid";
|
|
10
|
+
import {LazyEmojiDataStore} from "./lazy-emoji-data-store";
|
|
11
|
+
import {ColorPicker} from "../ColorPicker";
|
|
12
|
+
import {EmojiPickerFooter} from "./primitives/footer";
|
|
13
|
+
import {cardTypeColors} from "../designSystem";
|
|
14
|
+
|
|
15
|
+
const i18n = {search: "Search emoji..."};
|
|
16
|
+
|
|
17
|
+
export type EmojiPickerProps = {
|
|
18
|
+
color?: string;
|
|
19
|
+
getHoverColor?: (color: string) => string | undefined;
|
|
20
|
+
colors?: string[];
|
|
21
|
+
onColorSelect?: (c: $TSFixMe) => void;
|
|
22
|
+
showSkinTones?: boolean;
|
|
23
|
+
|
|
24
|
+
onSelect: (emoji: EmojiItem) => void;
|
|
25
|
+
};
|
|
26
|
+
export const EmojiPicker: React.FC<EmojiPickerProps> = ({
|
|
27
|
+
onSelect,
|
|
28
|
+
color,
|
|
29
|
+
colors = cardTypeColors,
|
|
30
|
+
onColorSelect,
|
|
31
|
+
getHoverColor,
|
|
32
|
+
showSkinTones,
|
|
33
|
+
}) => {
|
|
34
|
+
return (
|
|
35
|
+
<EmojiPickerRoot i18n={i18n} onEmojiSelect={onSelect}>
|
|
36
|
+
<EmojiPickerContent emojiHoverColor={color ? getHoverColor?.(color) : undefined} emojiSize={20}>
|
|
37
|
+
<LazyEmojiDataStore fallback={<div style={{height: "282px"}}></div>}>
|
|
38
|
+
<EmojiPickerHeader>
|
|
39
|
+
<EmojiPickerSearch
|
|
40
|
+
autoFocus
|
|
41
|
+
className={cx(
|
|
42
|
+
showSkinTones &&
|
|
43
|
+
css`
|
|
44
|
+
width: 250px !important;
|
|
45
|
+
`
|
|
46
|
+
)}
|
|
47
|
+
/>
|
|
48
|
+
{/* Temporary hacks. SkinTone should move to footer */}
|
|
49
|
+
{showSkinTones ? (
|
|
50
|
+
<EmojiPickerSkinTone
|
|
51
|
+
className={css`
|
|
52
|
+
position: absolute;
|
|
53
|
+
top: 4px;
|
|
54
|
+
right: 12px;
|
|
55
|
+
`}
|
|
56
|
+
/>
|
|
57
|
+
) : null}
|
|
58
|
+
</EmojiPickerHeader>
|
|
59
|
+
<EmojiPickerGrid />
|
|
60
|
+
</LazyEmojiDataStore>
|
|
61
|
+
|
|
62
|
+
{onColorSelect ? (
|
|
63
|
+
<EmojiPickerFooter>
|
|
64
|
+
<ColorPicker
|
|
65
|
+
colors={colors}
|
|
66
|
+
getSwatchHoverColor={getHoverColor}
|
|
67
|
+
color={color}
|
|
68
|
+
onChangeComplete={onColorSelect}
|
|
69
|
+
/>
|
|
70
|
+
</EmojiPickerFooter>
|
|
71
|
+
) : null}
|
|
72
|
+
</EmojiPickerContent>
|
|
73
|
+
</EmojiPickerRoot>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {LazyEmojiDataStore} from "./lazy-emoji-data-store";
|
|
2
|
+
import {Emoji as EmojiPrimitive, EmojiProps as EmojiPrimitiveProps} from "./primitives/emoji";
|
|
3
|
+
|
|
4
|
+
export type EmojiProps = EmojiPrimitiveProps;
|
|
5
|
+
export const Emoji: React.FC<EmojiPrimitiveProps> = function EmojiOrLoader(props) {
|
|
6
|
+
return (
|
|
7
|
+
<LazyEmojiDataStore fallback={<div style={{width: props.size, height: props.size}} />}>
|
|
8
|
+
<EmojiPrimitive {...props} />
|
|
9
|
+
</LazyEmojiDataStore>
|
|
10
|
+
);
|
|
11
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {css} from "@linaria/core";
|
|
2
|
+
import {compact} from "lodash";
|
|
2
3
|
import {Button} from "../Button";
|
|
3
4
|
import {AntTabs} from "../antd";
|
|
4
5
|
import {space, textStyles} from "../designSystem";
|
|
@@ -16,14 +17,16 @@ const TabHeader: React.FC<React.PropsWithChildren> = ({children}) => (
|
|
|
16
17
|
</div>
|
|
17
18
|
);
|
|
18
19
|
|
|
19
|
-
type
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
type TabKey = "icons" | "emojis";
|
|
21
|
+
|
|
22
|
+
export type IconEmojiPickerProps = React.PropsWithChildren<{
|
|
23
|
+
onIconSelect?: AppIconPickerProps["onSelect"];
|
|
24
|
+
onEmojiSelect?: EmojiPickerProps["onSelect"];
|
|
22
25
|
onColorSelect?: (c: $TSFixMe) => void;
|
|
23
26
|
onReset?: () => void;
|
|
24
27
|
resetLabel?: string;
|
|
25
|
-
defaultTab?:
|
|
26
|
-
showColorPickerOnTabs?:
|
|
28
|
+
defaultTab?: TabKey;
|
|
29
|
+
showColorPickerOnTabs?: TabKey[];
|
|
27
30
|
color?: string;
|
|
28
31
|
getHoverColor?: (color: string) => string | undefined;
|
|
29
32
|
}>;
|
|
@@ -43,26 +46,25 @@ export const IconEmojiPicker: React.FC<IconEmojiPickerProps> = ({
|
|
|
43
46
|
animated={false}
|
|
44
47
|
destroyInactiveTabPane
|
|
45
48
|
defaultActiveKey={defaultTab}
|
|
46
|
-
items={[
|
|
47
|
-
{
|
|
49
|
+
items={compact([
|
|
50
|
+
onEmojiSelect && {
|
|
48
51
|
key: "emojis",
|
|
49
52
|
label: <TabHeader>Emojis</TabHeader>,
|
|
50
53
|
children: (
|
|
51
54
|
<EmojiPicker
|
|
52
|
-
autoFocus
|
|
53
55
|
color={color}
|
|
54
56
|
onColorSelect={showColorPickerOnTabs.includes("emojis") ? onColorSelect : undefined}
|
|
55
57
|
getHoverColor={getHoverColor}
|
|
56
58
|
onSelect={onEmojiSelect}
|
|
59
|
+
showSkinTones
|
|
57
60
|
/>
|
|
58
61
|
),
|
|
59
62
|
},
|
|
60
|
-
{
|
|
63
|
+
onIconSelect && {
|
|
61
64
|
key: "icons",
|
|
62
65
|
label: <TabHeader>Icons</TabHeader>,
|
|
63
66
|
children: (
|
|
64
67
|
<AppIconPicker
|
|
65
|
-
autoFocus
|
|
66
68
|
color={color}
|
|
67
69
|
onColorSelect={showColorPickerOnTabs.includes("icons") ? onColorSelect : undefined}
|
|
68
70
|
getHoverColor={getHoverColor}
|
|
@@ -70,7 +72,7 @@ export const IconEmojiPicker: React.FC<IconEmojiPickerProps> = ({
|
|
|
70
72
|
/>
|
|
71
73
|
),
|
|
72
74
|
},
|
|
73
|
-
]}
|
|
75
|
+
])}
|
|
74
76
|
tabBarExtraContent={
|
|
75
77
|
onReset ? (
|
|
76
78
|
<div
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {makeEmojiData} from "@fibery/emoji-data";
|
|
2
|
+
import {Suspense, lazy} from "react";
|
|
3
|
+
import {EmojiDataStoreProvider} from "./stores/emoji-data-store";
|
|
4
|
+
import {getEmojiSet} from "./utils/emoji-set";
|
|
5
|
+
import {loadEmojiData} from "./utils/load-emoji-data";
|
|
6
|
+
|
|
7
|
+
const LazyEmojiDataStoreComponent = lazy(async () => {
|
|
8
|
+
const rawData = await loadEmojiData();
|
|
9
|
+
|
|
10
|
+
const emojiData = makeEmojiData(rawData, {set: getEmojiSet()});
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
default: function EmojiDataStore({children}: React.PropsWithChildren) {
|
|
14
|
+
return <EmojiDataStoreProvider data={emojiData}>{children}</EmojiDataStoreProvider>;
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const LazyEmojiDataStore: React.FC<
|
|
20
|
+
React.PropsWithChildren<{
|
|
21
|
+
fallback: React.ReactNode;
|
|
22
|
+
}>
|
|
23
|
+
> = ({fallback, children}) => {
|
|
24
|
+
return (
|
|
25
|
+
<Suspense fallback={fallback}>
|
|
26
|
+
<LazyEmojiDataStoreComponent>{children}</LazyEmojiDataStoreComponent>
|
|
27
|
+
</Suspense>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {makeEmojiData} from "@fibery/emoji-data";
|
|
2
|
+
import {Suspense, lazy, useEffect, useState} from "react";
|
|
3
|
+
import {getThemedUrl} from "../AppIconWithFallback";
|
|
4
|
+
import {useThemeMode} from "../ThemeProvider";
|
|
5
|
+
import {alignTheme} from "../theme-settings";
|
|
6
|
+
import {EmojiDataStoreProvider, useEmojiDataStore} from "./stores/emoji-data-store";
|
|
7
|
+
|
|
8
|
+
const EMPTY_DATA = {
|
|
9
|
+
aliases: {},
|
|
10
|
+
emojis: {},
|
|
11
|
+
categories: [],
|
|
12
|
+
compressed: false,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const LazyAppIcons = lazy(async () => {
|
|
16
|
+
const {default: appIcons} = await import("../appIcons.json");
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
default: function EmojiDataStore({children}: React.PropsWithChildren) {
|
|
20
|
+
const emojiDataStore = useEmojiDataStore();
|
|
21
|
+
const themeMode = useThemeMode();
|
|
22
|
+
const alignedTheme = alignTheme(themeMode);
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
// TODO: icons should be rendered as svg elements
|
|
26
|
+
const custom = appIcons.map((icon) => {
|
|
27
|
+
return {
|
|
28
|
+
...icon,
|
|
29
|
+
imageUrl: getThemedUrl(icon.file, alignedTheme),
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
emojiDataStore.setCustom(custom);
|
|
34
|
+
}, [alignedTheme, emojiDataStore]);
|
|
35
|
+
|
|
36
|
+
return <>{children}</>;
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export const LazyIconDataStore: React.FC<
|
|
42
|
+
React.PropsWithChildren<{
|
|
43
|
+
fallback: React.ReactNode;
|
|
44
|
+
}>
|
|
45
|
+
> = ({fallback, children}) => {
|
|
46
|
+
const [emojiData] = useState(() => makeEmojiData(EMPTY_DATA));
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<EmojiDataStoreProvider data={emojiData}>
|
|
50
|
+
<Suspense fallback={fallback}>
|
|
51
|
+
<LazyAppIcons>{children}</LazyAppIcons>
|
|
52
|
+
</Suspense>
|
|
53
|
+
</EmojiDataStoreProvider>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {Category} from "@fibery/emoji-data";
|
|
2
|
+
import {CSSProperties, css} from "@linaria/core";
|
|
3
|
+
import {space, textStyles, themeVars} from "../../designSystem";
|
|
4
|
+
import {useEmojiPickerContentSettings} from "./content";
|
|
5
|
+
import {Emoji} from "./emoji";
|
|
6
|
+
import {emojiPadding} from "./layout";
|
|
7
|
+
import {useEmojiPickerCtx, useEmojiPickerI18N} from "./root";
|
|
8
|
+
import {useEmojiSkin} from "./skin-provider";
|
|
9
|
+
|
|
10
|
+
const categoryCss = css``;
|
|
11
|
+
|
|
12
|
+
const categoryLabelCss = css`
|
|
13
|
+
${textStyles.heading6}
|
|
14
|
+
color: ${themeVars.accentTextColor};
|
|
15
|
+
background-color: ${themeVars.actionMenuBg};
|
|
16
|
+
padding: ${space.l}px ${space.s}px ${space.xs}px ${space.s}px;
|
|
17
|
+
position: sticky;
|
|
18
|
+
top: 0;
|
|
19
|
+
|
|
20
|
+
/* is required only because contentVisibility lifts emoji up */
|
|
21
|
+
z-index: 1;
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
const categoryListCss = css`
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-wrap: wrap;
|
|
27
|
+
margin: 0;
|
|
28
|
+
padding: 0;
|
|
29
|
+
|
|
30
|
+
& li {
|
|
31
|
+
list-style: none;
|
|
32
|
+
margin: 0;
|
|
33
|
+
padding: 0;
|
|
34
|
+
|
|
35
|
+
line-height: 1;
|
|
36
|
+
}
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
const notFoundCss = css`
|
|
40
|
+
padding-top: 70px;
|
|
41
|
+
text-align: center;
|
|
42
|
+
|
|
43
|
+
${textStyles.regular};
|
|
44
|
+
color: ${themeVars.accentTextColor};
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
const NotFound = () => {
|
|
48
|
+
const i18n = useEmojiPickerI18N();
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div className={notFoundCss}>
|
|
52
|
+
<Emoji emoji="sleuth_or_spy" size={38} />
|
|
53
|
+
<div>{i18n.notfound}</div>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
type EmojiPickerCategoryProps = {
|
|
59
|
+
category: Category;
|
|
60
|
+
};
|
|
61
|
+
export const EmojiPickerCategory: React.FC<EmojiPickerCategoryProps> = ({category}) => {
|
|
62
|
+
const i18n = useEmojiPickerI18N();
|
|
63
|
+
const emojiPickerCtx = useEmojiPickerCtx();
|
|
64
|
+
const emojiContentSettings = useEmojiPickerContentSettings();
|
|
65
|
+
|
|
66
|
+
const label = i18n.categories[category.id as keyof (typeof i18n)["categories"]] || category.name;
|
|
67
|
+
|
|
68
|
+
const skin = useEmojiSkin();
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<section className={categoryCss} aria-label={label}>
|
|
72
|
+
<div className={categoryLabelCss}>
|
|
73
|
+
<span aria-hidden={true /* already labeled by the section aria-label */}>{label}</span>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
{category.emojis.length > 0 ? (
|
|
77
|
+
<ul className={categoryListCss}>
|
|
78
|
+
{category.emojis.map((id) => {
|
|
79
|
+
return (
|
|
80
|
+
<li
|
|
81
|
+
style={
|
|
82
|
+
// css-only virtualization until implementing the real one
|
|
83
|
+
{
|
|
84
|
+
contentVisibility: "auto",
|
|
85
|
+
containIntrinsicSize: `${emojiContentSettings.emojiSize + emojiPadding * 2}px`,
|
|
86
|
+
} as CSSProperties
|
|
87
|
+
}
|
|
88
|
+
key={id}
|
|
89
|
+
>
|
|
90
|
+
<Emoji
|
|
91
|
+
onClick={emojiPickerCtx.onEmojiSelect}
|
|
92
|
+
emoji={id}
|
|
93
|
+
forceSize
|
|
94
|
+
version={emojiPickerCtx.version}
|
|
95
|
+
skin={skin}
|
|
96
|
+
tooltip
|
|
97
|
+
/>
|
|
98
|
+
</li>
|
|
99
|
+
);
|
|
100
|
+
})}
|
|
101
|
+
</ul>
|
|
102
|
+
) : (
|
|
103
|
+
<NotFound />
|
|
104
|
+
)}
|
|
105
|
+
</section>
|
|
106
|
+
);
|
|
107
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {createContext} from "@fibery/react/src/create-context";
|
|
2
|
+
import {CSSProperties, css} from "@linaria/core";
|
|
3
|
+
import {useMemo} from "react";
|
|
4
|
+
import {themeVars} from "../../designSystem";
|
|
5
|
+
import {contentHorizontalPadding, emojiPadding} from "./layout";
|
|
6
|
+
|
|
7
|
+
const emojiPickerCss = css`
|
|
8
|
+
background-color: ${themeVars.actionMenuBg};
|
|
9
|
+
`;
|
|
10
|
+
|
|
11
|
+
type EmojiPickerContentSettings = {
|
|
12
|
+
perLine: number;
|
|
13
|
+
emojiSize: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const [Provider, useCtx] = createContext<EmojiPickerContentSettings>("EmojiPickerContentSettings");
|
|
17
|
+
|
|
18
|
+
export type EmojiPickerContentProps = {
|
|
19
|
+
emojiHoverColor?: string;
|
|
20
|
+
perLine?: number;
|
|
21
|
+
emojiSize?: number;
|
|
22
|
+
};
|
|
23
|
+
export const EmojiPickerContent: React.FC<React.PropsWithChildren<EmojiPickerContentProps>> = ({
|
|
24
|
+
emojiHoverColor,
|
|
25
|
+
perLine = 9,
|
|
26
|
+
emojiSize = 20,
|
|
27
|
+
children,
|
|
28
|
+
}) => {
|
|
29
|
+
const width = perLine * (emojiSize + emojiPadding * 2) + contentHorizontalPadding * 2;
|
|
30
|
+
|
|
31
|
+
const contentSettings = useMemo(
|
|
32
|
+
() => ({
|
|
33
|
+
emojiSize,
|
|
34
|
+
perLine,
|
|
35
|
+
}),
|
|
36
|
+
[emojiSize, perLine]
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<Provider value={contentSettings}>
|
|
41
|
+
<div style={{"--fibery-emoji-hover-color": emojiHoverColor, width} as CSSProperties} className={emojiPickerCss}>
|
|
42
|
+
{children}
|
|
43
|
+
</div>
|
|
44
|
+
</Provider>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const useEmojiPickerContentSettings = useCtx;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import {EmojiItem, EmojiSkin} from "@fibery/emoji-data";
|
|
2
|
+
import {css, cx} from "@linaria/core";
|
|
3
|
+
import {border, themeVars} from "../../designSystem";
|
|
4
|
+
import {useEmojiDataStoreSelector} from "../stores/emoji-data-store";
|
|
5
|
+
import {emojiPadding} from "./layout";
|
|
6
|
+
|
|
7
|
+
const emojiWrapperCss = css`
|
|
8
|
+
display: inline-flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
justify-content: center;
|
|
11
|
+
color: ${themeVars.textColor};
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
const clickableCss = css`
|
|
15
|
+
padding: ${emojiPadding}px;
|
|
16
|
+
cursor: pointer;
|
|
17
|
+
margin: 0;
|
|
18
|
+
border: none;
|
|
19
|
+
background: none;
|
|
20
|
+
box-shadow: none;
|
|
21
|
+
border-radius: ${border.radius8}px;
|
|
22
|
+
|
|
23
|
+
&:hover {
|
|
24
|
+
background-color: var(--fibery-emoji-hover-color, ${themeVars.badgeBgColor});
|
|
25
|
+
}
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
const getSizeStyle = (size: number) => ({
|
|
29
|
+
display: "inline-block",
|
|
30
|
+
width: size,
|
|
31
|
+
height: size,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export type EmojiProps = {
|
|
35
|
+
size?: number;
|
|
36
|
+
/** id or colons */
|
|
37
|
+
emoji: string;
|
|
38
|
+
tooltip?: boolean;
|
|
39
|
+
/** max supported version of emojis */
|
|
40
|
+
version?: number;
|
|
41
|
+
onClick?: (emoji: EmojiItem, event: React.MouseEvent) => void;
|
|
42
|
+
skin?: EmojiSkin;
|
|
43
|
+
|
|
44
|
+
forceSize?: boolean;
|
|
45
|
+
};
|
|
46
|
+
export const Emoji: React.FC<EmojiProps> = ({
|
|
47
|
+
emoji: idOrColons,
|
|
48
|
+
size = 20,
|
|
49
|
+
tooltip,
|
|
50
|
+
forceSize = false,
|
|
51
|
+
onClick,
|
|
52
|
+
version = 14,
|
|
53
|
+
skin,
|
|
54
|
+
}) => {
|
|
55
|
+
const emoji = useEmojiDataStoreSelector((store) => store.get(idOrColons, {skin}));
|
|
56
|
+
|
|
57
|
+
if (!emoji) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let emojiNode: React.ReactNode;
|
|
62
|
+
|
|
63
|
+
if ("native" in emoji) {
|
|
64
|
+
if (version >= emoji.version) {
|
|
65
|
+
emojiNode = (
|
|
66
|
+
<span
|
|
67
|
+
style={{
|
|
68
|
+
fontSize: size,
|
|
69
|
+
fontFamily:
|
|
70
|
+
'"Segoe UI Emoji", "Segoe UI Symbol", "Segoe UI", "Apple Color Emoji", "Twemoji Mozilla", "Noto Color Emoji", "Android Emoji"',
|
|
71
|
+
|
|
72
|
+
...(forceSize
|
|
73
|
+
? {
|
|
74
|
+
...getSizeStyle(size),
|
|
75
|
+
textAlign: "center",
|
|
76
|
+
lineHeight: 1,
|
|
77
|
+
wordBreak: "keep-all",
|
|
78
|
+
}
|
|
79
|
+
: {}),
|
|
80
|
+
}}
|
|
81
|
+
>
|
|
82
|
+
{emoji.native}
|
|
83
|
+
</span>
|
|
84
|
+
);
|
|
85
|
+
} else {
|
|
86
|
+
emojiNode = (
|
|
87
|
+
<span
|
|
88
|
+
style={{
|
|
89
|
+
...getSizeStyle(size),
|
|
90
|
+
|
|
91
|
+
backgroundImage: `url(${emoji.sprite.src})`,
|
|
92
|
+
backgroundSize: emoji.sprite.size,
|
|
93
|
+
backgroundPosition: emoji.sprite.position,
|
|
94
|
+
}}
|
|
95
|
+
/>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
emojiNode = (
|
|
100
|
+
<span
|
|
101
|
+
style={{
|
|
102
|
+
...getSizeStyle(size),
|
|
103
|
+
backgroundImage: `url(${emoji.imageUrl})`,
|
|
104
|
+
backgroundSize: "contain",
|
|
105
|
+
backgroundRepeat: "no-repeat",
|
|
106
|
+
backgroundPosition: "center",
|
|
107
|
+
}}
|
|
108
|
+
/>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// TODO: use fibery's tooltip?
|
|
113
|
+
const title = tooltip ? emoji.id : undefined;
|
|
114
|
+
|
|
115
|
+
return onClick ? (
|
|
116
|
+
<button
|
|
117
|
+
type="button"
|
|
118
|
+
onClick={(e) => onClick(emoji, e)}
|
|
119
|
+
className={cx(emojiWrapperCss, clickableCss)}
|
|
120
|
+
title={title}
|
|
121
|
+
aria-label={emoji.name}
|
|
122
|
+
>
|
|
123
|
+
{emojiNode}
|
|
124
|
+
</button>
|
|
125
|
+
) : (
|
|
126
|
+
<span className={emojiWrapperCss} title={title} aria-label={emoji.name}>
|
|
127
|
+
{emojiNode}
|
|
128
|
+
</span>
|
|
129
|
+
);
|
|
130
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {css} from "@linaria/core";
|
|
2
|
+
import {space} from "../../designSystem";
|
|
3
|
+
|
|
4
|
+
const footerCss = css`
|
|
5
|
+
border-top: 1px solid var(--fibery-color-separatorColor);
|
|
6
|
+
padding: 0 ${space.s}px;
|
|
7
|
+
`;
|
|
8
|
+
|
|
9
|
+
export const EmojiPickerFooter: React.FC<React.PropsWithChildren> = ({children}) => {
|
|
10
|
+
// TODO: Preview + ColorPicker + Skin Tone
|
|
11
|
+
return <div className={footerCss}>{children}</div>;
|
|
12
|
+
};
|