@fibery/ui-kit 1.13.0 → 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.
Files changed (49) hide show
  1. package/package.json +5 -7
  2. package/src/designSystem.ts +2 -0
  3. package/src/emoji-picker/app-icon-picker.tsx +54 -0
  4. package/src/emoji-picker/emoji-index.ts +8 -0
  5. package/src/emoji-picker/emoji-picker.tsx +75 -0
  6. package/src/emoji-picker/emoji.tsx +11 -0
  7. package/src/{emoji-mart → emoji-picker}/icon-emoji-picker.tsx +13 -11
  8. package/src/emoji-picker/index.tsx +5 -0
  9. package/src/emoji-picker/lazy-emoji-data-store.tsx +29 -0
  10. package/src/emoji-picker/lazy-icon-data-store.tsx +55 -0
  11. package/src/emoji-picker/primitives/category.tsx +107 -0
  12. package/src/emoji-picker/primitives/content.tsx +48 -0
  13. package/src/emoji-picker/primitives/emoji.tsx +130 -0
  14. package/src/emoji-picker/primitives/footer.tsx +12 -0
  15. package/src/emoji-picker/primitives/grid.tsx +67 -0
  16. package/src/emoji-picker/primitives/header.tsx +11 -0
  17. package/src/emoji-picker/primitives/layout.ts +5 -0
  18. package/src/emoji-picker/primitives/root.tsx +79 -0
  19. package/src/emoji-picker/primitives/search-provider.tsx +22 -0
  20. package/src/emoji-picker/primitives/search.tsx +133 -0
  21. package/src/emoji-picker/primitives/skin-provider.tsx +27 -0
  22. package/src/emoji-picker/primitives/skin-tone.tsx +99 -0
  23. package/src/emoji-picker/stores/emoji-data-store.tsx +50 -0
  24. package/src/emoji-picker/utils/emoji-set.ts +5 -0
  25. package/src/{emoji-mart → emoji-picker/utils}/emoji-support.ts +6 -10
  26. package/src/emoji-picker/utils/frequently.ts +82 -0
  27. package/src/emoji-picker/utils/load-emoji-data.ts +4 -0
  28. package/src/emoji-picker/utils/store.ts +40 -0
  29. package/src/icons/ast/AddAfter.ts +8 -0
  30. package/src/icons/ast/AddBefore.ts +8 -0
  31. package/src/icons/ast/ArrowBarLeft.ts +8 -0
  32. package/src/icons/ast/ArrowBarRight.ts +8 -0
  33. package/src/icons/ast/Columns2.ts +8 -0
  34. package/src/icons/ast/Columns4.ts +8 -0
  35. package/src/icons/ast/index.tsx +6 -0
  36. package/src/icons/react/AddAfter.tsx +12 -0
  37. package/src/icons/react/AddBefore.tsx +12 -0
  38. package/src/icons/react/ArrowBarLeft.tsx +12 -0
  39. package/src/icons/react/ArrowBarRight.tsx +12 -0
  40. package/src/icons/react/Columns2.tsx +12 -0
  41. package/src/icons/react/Columns4.tsx +12 -0
  42. package/src/icons/react/index.tsx +6 -0
  43. package/src/emoji-mart/app-icon-picker.tsx +0 -68
  44. package/src/emoji-mart/data/get-data.tsx +0 -58
  45. package/src/emoji-mart/emoji/emoji.tsx +0 -26
  46. package/src/emoji-mart/emoji/index.tsx +0 -24
  47. package/src/emoji-mart/emoji-index.ts +0 -5
  48. package/src/emoji-mart/emoji-mart.tsx +0 -216
  49. 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.13.0",
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-mart/**/*.ts*",
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/emoji-data": "1.1.2",
64
- "@fibery/helpers": "1.0.4",
65
- "@fibery/react": "1.0.2"
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",
@@ -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 IconEmojiPickerProps = React.PropsWithChildren<{
20
- onIconSelect: NonNullable<AppIconPickerProps["onSelect"]>;
21
- onEmojiSelect: NonNullable<EmojiPickerProps["onSelect"]>;
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?: "icons" | "emojis";
26
- showColorPickerOnTabs?: ("icons" | "emojis")[];
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,5 @@
1
+ export {EmojiPickerRoot as Root} from "./root";
2
+ export {EmojiPickerContent as Content} from "./content";
3
+ export {EmojiPickerGrid as Grid} from "./grid";
4
+ export {EmojiPickerSearch as Search} from "./search";
5
+ export {EmojiPickerFooter as Footer} from "./footer";
@@ -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
+ };