@fibery/ui-kit 1.15.0 → 1.17.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 (72) hide show
  1. package/package.json +8 -6
  2. package/src/Button/actions-button-compact.tsx +13 -0
  3. package/src/Button/actions-button.tsx +30 -0
  4. package/src/Button/button-base.tsx +2 -12
  5. package/src/Button/button.tsx +3 -34
  6. package/src/Select/components.tsx +6 -0
  7. package/src/Select/styles.ts +9 -4
  8. package/src/antd/styles.ts +5 -5
  9. package/src/designSystem.ts +11 -26
  10. package/src/emoji-picker/app-icon-picker.tsx +31 -26
  11. package/src/emoji-picker/emoji-picker-content-with-color.tsx +110 -0
  12. package/src/emoji-picker/emoji-picker.tsx +34 -39
  13. package/src/emoji-picker/icon-emoji-picker.tsx +3 -14
  14. package/src/emoji-picker/primitives/category.tsx +82 -25
  15. package/src/emoji-picker/primitives/emoji.tsx +7 -49
  16. package/src/emoji-picker/primitives/footer.tsx +3 -1
  17. package/src/emoji-picker/primitives/grid.tsx +56 -24
  18. package/src/emoji-picker/primitives/search.tsx +21 -11
  19. package/src/emoji-picker/primitives/skin-tone.tsx +18 -12
  20. package/src/emoji-picker/stores/emoji-data-store.tsx +0 -1
  21. package/src/emoji-picker/stores/lazy-icon-data-store.tsx +19 -30
  22. package/src/icons/ast/AiAssistant.ts +1 -1
  23. package/src/icons/ast/AiAvatar.ts +1 -1
  24. package/src/icons/ast/AppDetails.ts +1 -1
  25. package/src/icons/ast/AppTemplates.ts +1 -1
  26. package/src/icons/ast/BellCircle.ts +8 -0
  27. package/src/icons/ast/BellX.ts +8 -0
  28. package/src/icons/ast/Bolt.ts +8 -0
  29. package/src/icons/ast/CheckCircle.ts +8 -0
  30. package/src/icons/ast/ClockForward.ts +8 -0
  31. package/src/icons/ast/ColorCodingFilled.ts +8 -0
  32. package/src/icons/ast/Delete.ts +1 -1
  33. package/src/icons/ast/EmojiDelete.ts +8 -0
  34. package/src/icons/ast/ExtensionAssignments.ts +1 -1
  35. package/src/icons/ast/Fields.ts +1 -1
  36. package/src/icons/ast/Figma.ts +8 -0
  37. package/src/icons/ast/FocusMode.ts +1 -1
  38. package/src/icons/ast/GoogleDrive.ts +8 -0
  39. package/src/icons/ast/GoogleMap.ts +8 -0
  40. package/src/icons/ast/InvitePeople.ts +1 -1
  41. package/src/icons/ast/Loom.ts +8 -0
  42. package/src/icons/ast/Miro.ts +8 -0
  43. package/src/icons/ast/Mixpanel.ts +8 -0
  44. package/src/icons/ast/RicheditorBlockCode.ts +1 -1
  45. package/src/icons/ast/RicheditorBlockEmbed.ts +8 -0
  46. package/src/icons/ast/RicheditorImageZoom.ts +1 -1
  47. package/src/icons/ast/RicheditorMarkCode.ts +1 -1
  48. package/src/icons/ast/RicheditorOpenLink.ts +1 -1
  49. package/src/icons/ast/Shared.ts +1 -1
  50. package/src/icons/ast/Twitter.ts +8 -0
  51. package/src/icons/ast/Youtube.ts +8 -0
  52. package/src/icons/ast/index.tsx +16 -0
  53. package/src/icons/react/BellCircle.tsx +12 -0
  54. package/src/icons/react/BellX.tsx +12 -0
  55. package/src/icons/react/Bolt.tsx +12 -0
  56. package/src/icons/react/CheckCircle.tsx +12 -0
  57. package/src/icons/react/ClockForward.tsx +12 -0
  58. package/src/icons/react/ColorCodingFilled.tsx +12 -0
  59. package/src/icons/react/EmojiDelete.tsx +12 -0
  60. package/src/icons/react/Figma.tsx +12 -0
  61. package/src/icons/react/GoogleDrive.tsx +12 -0
  62. package/src/icons/react/GoogleMap.tsx +12 -0
  63. package/src/icons/react/Loom.tsx +12 -0
  64. package/src/icons/react/Miro.tsx +12 -0
  65. package/src/icons/react/Mixpanel.tsx +12 -0
  66. package/src/icons/react/RicheditorBlockEmbed.tsx +12 -0
  67. package/src/icons/react/Twitter.tsx +12 -0
  68. package/src/icons/react/Youtube.tsx +12 -0
  69. package/src/icons/react/index.tsx +16 -0
  70. package/src/tooltip.tsx +75 -53
  71. package/src/Button/icon-button-with-tooltip.tsx +0 -15
  72. package/src/emoji-picker/index.tsx +0 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fibery/ui-kit",
3
- "version": "1.15.0",
3
+ "version": "1.17.0",
4
4
  "main": "index.ts",
5
5
  "private": false,
6
6
  "files": [
@@ -40,6 +40,7 @@
40
40
  "chroma-js": "2.1.2",
41
41
  "chrono-node": "^2.4.1",
42
42
  "classnames": "2.3.1",
43
+ "cmdk": "0.2.0",
43
44
  "color-hash": "1.0.3",
44
45
  "d3-shape": "1.3.7",
45
46
  "date-fns": "^2.29.2",
@@ -53,15 +54,16 @@
53
54
  "rc-progress": "3.4.1",
54
55
  "react-color": "2.19.3",
55
56
  "react-day-picker": "8.1.4",
57
+ "react-intersection-observer": "9.3.5",
56
58
  "react-popper": "2.3.0",
57
59
  "react-select": "5.3.2",
58
60
  "react-select-country-list": "2.2.1",
59
61
  "react-windowed-select": "5.0.0",
60
62
  "screenfull": "6.0.1",
61
63
  "ua-parser-js": "0.7.24",
62
- "@fibery/react": "1.0.3",
64
+ "@fibery/emoji-data": "2.1.1",
63
65
  "@fibery/helpers": "1.1.0",
64
- "@fibery/emoji-data": "2.1.0"
66
+ "@fibery/react": "1.0.3"
65
67
  },
66
68
  "peerDependencies": {
67
69
  "react": "^18.2.0",
@@ -91,10 +93,10 @@
91
93
  "react-dom": "18.2.0",
92
94
  "svg-parser": "2.0.4",
93
95
  "svgo": "2.8.0",
94
- "typescript": "5.0.3",
96
+ "typescript": "5.1.6",
95
97
  "unist-util-reduce": "0.2.2",
96
- "@fibery/eslint-config": "8.3.0",
97
- "@fibery/babel-preset": "7.2.0"
98
+ "@fibery/babel-preset": "7.2.0",
99
+ "@fibery/eslint-config": "8.3.0"
98
100
  },
99
101
  "jest": {
100
102
  "testEnvironment": "jsdom",
@@ -0,0 +1,13 @@
1
+ import {IconButton, IconButtonProps} from "./icon-button";
2
+ import MoreCompactIcon from "../icons/react/MoreCompact";
3
+ import {forwardRef} from "react";
4
+
5
+ export const ActionsButtonCompact = forwardRef<HTMLButtonElement, IconButtonProps>(
6
+ function ActionsButtonCompactComponent({...rest}, ref) {
7
+ return (
8
+ <IconButton ref={ref} size="small" {...rest}>
9
+ <MoreCompactIcon iconSize={18} containerSize={20} />
10
+ </IconButton>
11
+ );
12
+ }
13
+ );
@@ -0,0 +1,30 @@
1
+ import {IconButton, IconButtonProps} from "./icon-button";
2
+ import MoreCompact from "../icons/react/MoreCompact";
3
+ import {forwardRef} from "react";
4
+ import {css} from "@linaria/core";
5
+ import {border, themeVars} from "../designSystem";
6
+
7
+ export const ActionsButton = forwardRef<HTMLButtonElement, IconButtonProps>(function ActionsButtonComponent(
8
+ {...rest},
9
+ ref
10
+ ) {
11
+ return (
12
+ <IconButton ref={ref} size="normal" {...rest}>
13
+ <MoreCompact iconSize={18} containerSize={20} />
14
+ </IconButton>
15
+ );
16
+ });
17
+
18
+ // TODO: Shoud be a variant inside button?
19
+ export const actionButtonInlineStyle = css`
20
+ background-color: ${themeVars.actionMenuButtonColor};
21
+ box-shadow: ${themeVars.entityCardShadow};
22
+ border-radius: ${border.radius6};
23
+
24
+ .Button:hover {
25
+ background-color: ${themeVars.actionMenuButtonHoverColor} !important;
26
+ }
27
+ .Button:focus {
28
+ background-color: ${themeVars.actionMenuButtonHoverColor} !important;
29
+ }
30
+ `;
@@ -143,14 +143,13 @@ const StyledButtonBase = styled.button<StyledProps>`
143
143
  `;
144
144
 
145
145
  export type ButtonBaseProps = {
146
- dataId?: string;
147
146
  color?: string;
148
147
  primary?: boolean;
149
148
  dangerous?: boolean;
150
149
  borderless?: boolean;
151
150
  } & ComponentPropsWithoutRef<"button">;
152
151
  export const ButtonBase = forwardRef<HTMLButtonElement, ButtonBaseProps>(function ButtonBase(
153
- {primary, color, borderless, dangerous, className, type = "button", dataId, ...buttonProps},
152
+ {primary, color, borderless, dangerous, className, type = "button", ...buttonProps},
154
153
  ref
155
154
  ) {
156
155
  const theme = useTheme();
@@ -177,14 +176,5 @@ export const ButtonBase = forwardRef<HTMLButtonElement, ButtonBaseProps>(functio
177
176
  borderWidth,
178
177
  };
179
178
 
180
- return (
181
- <StyledButtonBase
182
- ref={ref}
183
- type={type}
184
- className={className}
185
- data-testid={dataId}
186
- {...styledProps}
187
- {...buttonProps}
188
- />
189
- );
179
+ return <StyledButtonBase ref={ref} type={type} className={className} {...styledProps} {...buttonProps} />;
190
180
  });
@@ -4,14 +4,12 @@ import {styled} from "@linaria/react";
4
4
  import cx from "classnames";
5
5
  import _ from "lodash";
6
6
  import React, {forwardRef} from "react";
7
- import {ButtonBase, ButtonBaseProps, buttonBorderWidth, getMainColor, getTextColor} from "./button-base";
7
+ import {Item} from "../Item";
8
+ import {useTheme} from "../ThemeProvider";
8
9
  import {border, colors, getOpacities, opacity, space, textStyles} from "../designSystem";
9
- import MoreIcon from "../icons/react/More";
10
- import MoreCompactIcon from "../icons/react/MoreCompact";
11
10
  import {IconBaseProps} from "../icons/types";
12
- import {Item} from "../Item";
13
11
  import {Spinner} from "../loaders";
14
- import {useTheme} from "../ThemeProvider";
12
+ import {ButtonBase, ButtonBaseProps, buttonBorderWidth, getMainColor, getTextColor} from "./button-base";
15
13
 
16
14
  type PositionInGroup = "first" | "middle" | "last";
17
15
 
@@ -218,32 +216,3 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button
218
216
  </StyledButton>
219
217
  );
220
218
  });
221
-
222
- // TODO can be inlined
223
- export function ActionsButton({
224
- onClick,
225
- label = ``,
226
- disabled,
227
- }: {
228
- onClick: ButtonProps["onClick"];
229
- label?: string;
230
- disabled?: boolean;
231
- }) {
232
- return <Button Icon={MoreIcon} onClick={onClick} borderless aria-label={label} disabled={disabled} />;
233
- }
234
-
235
- export const ActionsButtonCompact = forwardRef<HTMLButtonElement, ButtonProps & {inverted?: boolean}>(
236
- ({inverted, ...rest}, ref) => {
237
- const theme = useTheme();
238
- return (
239
- <Button
240
- ref={ref}
241
- size={":button-size/small"}
242
- Icon={MoreCompactIcon}
243
- color={inverted ? colors.inversedTextColor : theme.buttonColor}
244
- borderless
245
- {...rest}
246
- />
247
- );
248
- }
249
- );
@@ -28,6 +28,9 @@ export function DropdownIndicator<
28
28
  IsMulti extends boolean = boolean,
29
29
  Group extends GroupBase<TOption> = GroupBase<TOption>
30
30
  >(props: DropdownIndicatorProps<TOption, IsMulti, Group>) {
31
+ if (props.selectProps.isDisabled) {
32
+ return null;
33
+ }
31
34
  return (
32
35
  <div className={cn(expanderStyle, props.selectProps.menuIsOpen && expanderExpandedStyle)}>
33
36
  <ArrowCollapseVertical color={themeVars.disabledTextColor} />
@@ -43,6 +46,9 @@ export function ClearIndicator<
43
46
  innerProps,
44
47
  selectProps,
45
48
  }: ClearIndicatorProps<TOption, IsMulti, Group> | MultiValueRemoveProps<TOption, IsMulti, Group>) {
49
+ if (selectProps.isDisabled) {
50
+ return null;
51
+ }
46
52
  return (
47
53
  <div {...innerProps}>
48
54
  <Button borderless Icon={RemoveIcon} disabled={selectProps.isDisabled} size={":button-size/super-small"} />
@@ -165,10 +165,15 @@ export const componentsStyles: StylesConfig = {
165
165
  cursor: state.isDisabled ? "not-allowed" : "default",
166
166
  }),
167
167
  indicatorSeparator: () => ({}),
168
- input: (provided) => ({
169
- ...provided,
170
- color: themeVars.textColor,
171
- }),
168
+ input: (provided, state) => {
169
+ if (state.isDisabled) {
170
+ return {display: "none"};
171
+ }
172
+ return {
173
+ ...provided,
174
+ color: themeVars.textColor,
175
+ };
176
+ },
172
177
  };
173
178
 
174
179
  export const singleLineComponentsStyle: StylesConfig = {
@@ -22,7 +22,7 @@ export const inputOverrides = {
22
22
  font: "inherit",
23
23
  color: themeVars.textColor,
24
24
  outline: "none",
25
- backgroundColor: themeVars.inputBgColor,
25
+ backgroundColor: `${themeVars.inputBgColor} !important`,
26
26
  borderRadius: border.radius6,
27
27
  minHeight: layout.inputHeight,
28
28
  paddingTop: 0,
@@ -32,22 +32,22 @@ export const inputOverrides = {
32
32
  border: 0,
33
33
  borderColor: colors.transparent,
34
34
  transition: `box-shadow ${transition}`,
35
- boxShadow: "var(--input-border)",
35
+ boxShadow: `var(--input-border, ${themeVars.inputBorderColor})`,
36
36
  letterSpacing: 0,
37
37
  "::placeholder": {
38
38
  color: themeVars.inputPlaceholderTextColor,
39
39
  },
40
40
  },
41
41
  hover: {
42
- boxShadow: "var(--input-hover-border) !important",
42
+ boxShadow: `var(--input-hover-border, 0 0 0 1px ${themeVars.opacity.opacity40}) !important`,
43
43
  transition: `box-shadow ${transition}`,
44
44
  },
45
45
  focus: {
46
- boxShadow: "var(--input-focus-border), var(--input-focus-shadow) !important",
46
+ boxShadow: `var(--input-focus-border, 0 0 0 1px ${themeVars.opacity.opacity25}), var(--input-focus-shadow, 0 0 0 3px ${themeVars.focus}) !important`,
47
47
  transition: `box-shadow ${transition}`,
48
48
  },
49
49
  disabled: {
50
- color: `${themeVars.accentTextColor}`,
50
+ color: `${themeVars.textColor}`,
51
51
  backgroundColor: `${themeVars.inputDisabledBgColor} !important`,
52
52
  borderColor: colors.transparent,
53
53
  boxShadow: `${themeVars.inputDisabledBorderColor} !important`,
@@ -61,7 +61,7 @@ const makeChromaColor = (color: string) => {
61
61
  try {
62
62
  return chroma(color);
63
63
  } catch (e) {
64
- return chroma(brandColors.green);
64
+ return chroma(brandColors.blue);
65
65
  }
66
66
  };
67
67
 
@@ -103,7 +103,7 @@ const brandColors = {
103
103
  } as const;
104
104
 
105
105
  export const getDarkenColor = _.memoize((color: string): string => {
106
- return makeChromaColor(color).darken(0.4).css();
106
+ return makeChromaColor(color).darken(0.6).css();
107
107
  });
108
108
 
109
109
  const lightColors = {
@@ -245,6 +245,8 @@ const lightColors = {
245
245
  filterGroup1Bg: slate.slate2,
246
246
  filterGroup2Bg: slate.slate3,
247
247
  filterGroupBorder: blackA.blackA3,
248
+ colorBgNotificationFocusedDefault: indigo.indigo3,
249
+ colorBgNotificationFocusedHover: indigo.indigo4,
248
250
  //end
249
251
  disabledInversedTextColor: shades.opacity25,
250
252
  danger: red.red9,
@@ -370,7 +372,7 @@ const darkColors = {
370
372
  }`,
371
373
  separatorColor: getOpacities(slateDark.slate8).opacity50,
372
374
  separatorBreadcrumbsColor: slateDark.slate8,
373
- iconColor: slate.slate9,
375
+ iconColor: slateDark.slate10,
374
376
  appIconColor: getOpacities(slate.slate2).opacity70,
375
377
  mentionBgColor: slateDark.slate5,
376
378
  entityCardBgColor: slateDark.slate5,
@@ -430,6 +432,8 @@ const darkColors = {
430
432
  filterGroup1Bg: slateDark.slate6,
431
433
  filterGroup2Bg: slateDark.slate7,
432
434
  filterGroupBorder: whiteA.whiteA3,
435
+ colorBgNotificationFocusedDefault: getOpacities(indigoDark.indigo11).opacity20,
436
+ colorBgNotificationFocusedHover: getOpacities(indigoDark.indigo11).opacity30,
433
437
  //end
434
438
  disabledInversedTextColor: lights.opacity25,
435
439
  danger: redDark.red10,
@@ -579,11 +583,7 @@ export const getThemeColors: (themeColor: string, theme: ThemeMode) => ThemeColo
579
583
  );
580
584
 
581
585
  export const getTextColor = _.memoize((color: string): string => {
582
- return makeChromaColor(color).luminance() > 0.45 ? makeChromaColor(color).luminance(0.1).saturate(3).hex() : "white";
583
- });
584
-
585
- export const getBrightenColor = _.memoize((color: string): string => {
586
- return makeChromaColor(color).brighten(0.15).hex();
586
+ return makeChromaColor(color).hex();
587
587
  });
588
588
 
589
589
  export const getLinkedHighlightBackgroundColor = _.memoize(
@@ -600,24 +600,11 @@ export const getLinkedHighlightBackgroundColor = _.memoize(
600
600
  (themeMode, color) => `${themeMode}-${color}`
601
601
  );
602
602
 
603
- // REMOVE AFTER MIGRATION ON BACKEND
604
- export const migrateEnumColor = (color: string) => {
605
- const pastelIdx = pastelColors.indexOf(color);
606
-
607
- if (pastelIdx > -1) {
608
- return cardTypeColors[pastelIdx];
609
- }
610
-
611
- return color;
612
- };
613
-
614
603
  export const getEnumBackgroundColor = _.memoize(
615
604
  (mode: ThemeMode, color: string) => {
616
- const migratedColor = migrateEnumColor(color);
617
-
618
605
  return getThemeValue(mode, {
619
- light: a11yColor(getOpacities(migratedColor).opacity20, lightColors.whiteColor, 1),
620
- dark: a11yColor(getOpacities(migratedColor).opacity30, darkColors.menuBg, 1.3),
606
+ light: a11yColor(getOpacities(color).opacity20, lightColors.whiteColor, 1),
607
+ dark: a11yColor(getOpacities(color).opacity30, darkColors.menuBg, 1.3),
621
608
  });
622
609
  },
623
610
  (themeMode, color) => `${themeMode}-${color}`
@@ -660,8 +647,6 @@ export const cardTypeColors = [
660
647
  "#B04E31",
661
648
  ] as const;
662
649
 
663
- const pastelColors = cardTypeColors.map((color) => makeChromaColor(color).luminance(0.8).hex());
664
-
665
650
  export const dropCursorColor = chroma(brandColors.blue).alpha(0.7).css();
666
651
  export const colors = {
667
652
  ...getTypeColors(brandColors.green, "light"),
@@ -784,7 +769,7 @@ export const getObjectColorMemoized = _.memoize((name) => {
784
769
  return "#999999";
785
770
  }
786
771
  const colorHash = new ColorHash({
787
- lightness: [0.4, 0.5, 0.6],
772
+ lightness: [0.5, 0.6, 0.7],
788
773
  saturation: [0.6, 0.7, 0.8, 0.9, 1],
789
774
  });
790
775
  return colorHash.hex(name);
@@ -1,14 +1,11 @@
1
- import {EmojiItem} from "@fibery/emoji-data/lib/src";
2
- import {ColorPicker} from "../ColorPicker";
1
+ import {CustomEmoji, EmojiItem} from "@fibery/emoji-data";
2
+ import {useThemeMode} from "../ThemeProvider";
3
+ import {cardTypeColors, getIconColor} from "../designSystem";
3
4
  import {$TSFixMe} from "../tsfixme";
4
- import {LazyIconDataStore} from "./stores/lazy-icon-data-store";
5
+ import {EmojiPickerContentWithColor} from "./emoji-picker-content-with-color";
5
6
  import {EmojiPickerContent} from "./primitives/content";
6
- import {EmojiPickerFooter} from "./primitives/footer";
7
- import {EmojiPickerGrid} from "./primitives/grid";
8
- import {EmojiPickerHeader} from "./primitives/header";
9
7
  import {EmojiPickerRoot} from "./primitives/root";
10
- import {EmojiPickerSearch} from "./primitives/search";
11
- import {cardTypeColors} from "../designSystem";
8
+ import {LazyIconDataStore} from "./stores/lazy-icon-data-store";
12
9
 
13
10
  const i18n = {search: "Search icon...", notfound: "No Icons Found"};
14
11
 
@@ -19,35 +16,43 @@ export type AppIconPickerProps = {
19
16
  onColorSelect?: (c: $TSFixMe) => void;
20
17
 
21
18
  onSelect: (emoji: EmojiItem) => void;
19
+ autoFocus?: boolean;
22
20
  };
23
21
  export const AppIconPicker: React.FC<AppIconPickerProps> = ({
24
22
  onSelect,
25
23
  color,
26
- colors = cardTypeColors,
24
+ colors = cardTypeColors as unknown as string[],
27
25
  onColorSelect,
28
26
  getHoverColor,
27
+ autoFocus = true,
29
28
  }) => {
29
+ const themeMode = useThemeMode();
30
+
30
31
  return (
31
32
  <EmojiPickerRoot i18n={i18n} onEmojiSelect={onSelect}>
32
33
  <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 />
34
+ <LazyIconDataStore fallback={<div style={{height: "302px"}}></div>}>
35
+ <EmojiPickerContentWithColor
36
+ color={color}
37
+ colors={colors}
38
+ getHoverColor={getHoverColor}
39
+ onColorSelect={onColorSelect}
40
+ searchAutofocus={autoFocus}
41
+ renderEmoji={({emoji}) => (
42
+ <svg
43
+ style={{
44
+ display: "block",
45
+ width: 20,
46
+ height: 20,
47
+ fill: color ? getIconColor(themeMode, color) : undefined,
48
+ }}
49
+ viewBox="0 0 24 24"
50
+ >
51
+ <use href={(emoji as CustomEmoji).imageUrl} />
52
+ </svg>
53
+ )}
54
+ />
39
55
  </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
56
  </EmojiPickerContent>
52
57
  </EmojiPickerRoot>
53
58
  );
@@ -0,0 +1,110 @@
1
+ import {css} from "@linaria/core";
2
+ import {useState} from "react";
3
+ import {IconButton} from "../Button/icon-button";
4
+ import {CollapsibleContent, CollapsibleRoot, CollapsibleTrigger} from "../Collapsible";
5
+ import {ColorPicker} from "../ColorPicker";
6
+ import {useThemeMode} from "../ThemeProvider";
7
+ import {getIconColor, space} from "../designSystem";
8
+ import ColorCoding from "../icons/react/ColorCoding";
9
+ import ColorCodingFilled from "../icons/react/ColorCodingFilled";
10
+ import {$TSFixMe} from "../tsfixme";
11
+ import {EmojiPickerGrid, EmojiPickerGridProps} from "./primitives/grid";
12
+ import {EmojiPickerHeader} from "./primitives/header";
13
+ import {EmojiPickerSearch} from "./primitives/search";
14
+ import {Tooltip} from "../tooltip";
15
+
16
+ const colorPickerWrapperCss = css`
17
+ padding-top: ${space.l}px;
18
+ `;
19
+
20
+ const disableScrollCss = css`
21
+ overflow-y: hidden;
22
+ `;
23
+
24
+ type ColorPickerCollapsibleTrigger = {opened: boolean; color?: string};
25
+
26
+ const ColorPickerCollapsibleTrigger: React.FC<ColorPickerCollapsibleTrigger> = ({opened, color}) => {
27
+ const themeMode = useThemeMode();
28
+ const iconColor = color ? getIconColor(themeMode, color) : undefined;
29
+
30
+ const Icon = opened ? ColorCodingFilled : ColorCoding;
31
+
32
+ const label = opened ? "Hide color picker" : "Show color picker";
33
+
34
+ return (
35
+ <CollapsibleTrigger asChild>
36
+ <Tooltip title={label}>
37
+ <IconButton
38
+ data-testid="emoji-picker-color-trigger"
39
+ size="big"
40
+ className={css`
41
+ border-top-left-radius: 0;
42
+ border-bottom-left-radius: 0;
43
+ `}
44
+ >
45
+ <Icon color={iconColor} iconSize={18} containerSize={20} />
46
+ </IconButton>
47
+ </Tooltip>
48
+ </CollapsibleTrigger>
49
+ );
50
+ };
51
+
52
+ const collapsibleWrapperCss = css`
53
+ max-height: 302px; // search + emoji grid
54
+ overflow: hidden;
55
+ `;
56
+
57
+ export type EmojiPickerContentWithColorProps = {
58
+ color?: string;
59
+ getHoverColor?: (color: string) => string | undefined;
60
+ colors?: string[];
61
+ onColorSelect?: (c: $TSFixMe) => void;
62
+
63
+ searchRef?: React.RefObject<HTMLInputElement>;
64
+ searchAutofocus?: boolean;
65
+ } & Pick<EmojiPickerGridProps, "renderEmoji">;
66
+
67
+ export const EmojiPickerContentWithColor: React.FC<EmojiPickerContentWithColorProps> = ({
68
+ searchRef,
69
+ colors,
70
+ color,
71
+ onColorSelect,
72
+ getHoverColor,
73
+ searchAutofocus = true,
74
+ renderEmoji,
75
+ }) => {
76
+ const [colorOpened, setColorOpened] = useState(false);
77
+
78
+ const showColorPicker = Boolean(onColorSelect);
79
+
80
+ return (
81
+ <CollapsibleRoot
82
+ className={collapsibleWrapperCss}
83
+ open={colorOpened}
84
+ onOpenChange={setColorOpened}
85
+ onWheel={() => setColorOpened(false)}
86
+ >
87
+ <EmojiPickerHeader>
88
+ <EmojiPickerSearch
89
+ searchRef={searchRef}
90
+ autoFocus={searchAutofocus}
91
+ extraAction={showColorPicker ? <ColorPickerCollapsibleTrigger opened={colorOpened} color={color} /> : null}
92
+ />
93
+ {showColorPicker ? (
94
+ <CollapsibleContent>
95
+ <div className={colorPickerWrapperCss}>
96
+ <ColorPicker
97
+ colors={colors}
98
+ getSwatchHoverColor={getHoverColor}
99
+ color={color}
100
+ onChangeComplete={onColorSelect}
101
+ />
102
+ </div>
103
+ </CollapsibleContent>
104
+ ) : null}
105
+ </EmojiPickerHeader>
106
+
107
+ <EmojiPickerGrid className={colorOpened ? disableScrollCss : undefined} renderEmoji={renderEmoji} />
108
+ </CollapsibleRoot>
109
+ );
110
+ };
@@ -1,15 +1,13 @@
1
1
  import {EmojiItem} from "@fibery/emoji-data/lib/src";
2
+ import {css} from "@linaria/core";
3
+ import {Button} from "../Button";
4
+ import {cardTypeColors, space} from "../designSystem";
2
5
  import {$TSFixMe} from "../tsfixme";
3
- import {EmojiPickerRoot} from "./primitives/root";
6
+ import {EmojiPickerContentWithColor} from "./emoji-picker-content-with-color";
4
7
  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 {ColorPicker} from "../ColorPicker";
11
8
  import {EmojiPickerFooter} from "./primitives/footer";
12
- import {cardTypeColors} from "../designSystem";
9
+ import {EmojiPickerRoot} from "./primitives/root";
10
+ import {EmojiPickerSkinTone} from "./primitives/skin-tone";
13
11
 
14
12
  const i18n = {search: "Search emoji..."};
15
13
 
@@ -19,6 +17,7 @@ export type EmojiPickerProps = {
19
17
  colors?: string[];
20
18
  onColorSelect?: (c: $TSFixMe) => void;
21
19
  onCustomCategorySettings?: () => void;
20
+ onAddCustomEmoji?: () => void;
22
21
  onSelect: (emoji: EmojiItem) => void;
23
22
  showSkinTones?: boolean;
24
23
 
@@ -27,48 +26,44 @@ export type EmojiPickerProps = {
27
26
  export const EmojiPicker: React.FC<EmojiPickerProps> = ({
28
27
  onSelect,
29
28
  color,
30
- colors = cardTypeColors,
29
+ colors = cardTypeColors as unknown as string[],
31
30
  onColorSelect,
32
31
  onCustomCategorySettings,
32
+ onAddCustomEmoji,
33
33
  getHoverColor,
34
34
  showSkinTones,
35
35
  searchRef,
36
36
  }) => {
37
+ const showFooter = Boolean(onAddCustomEmoji) || showSkinTones;
38
+
37
39
  return (
38
40
  <EmojiPickerRoot i18n={i18n} onEmojiSelect={onSelect} onCustomCategorySettings={onCustomCategorySettings}>
39
41
  <EmojiPickerContent emojiHoverColor={color ? getHoverColor?.(color) : undefined} emojiSize={20}>
40
- <EmojiPickerHeader>
41
- <EmojiPickerSearch
42
- searchRef={searchRef}
43
- autoFocus
44
- className={cx(
45
- showSkinTones &&
46
- css`
47
- width: 250px !important;
48
- `
49
- )}
50
- />
51
- {/* Temporary hacks. SkinTone should move to footer */}
52
- {showSkinTones ? (
53
- <EmojiPickerSkinTone
54
- className={css`
55
- position: absolute;
56
- top: 4px;
57
- right: 12px;
58
- `}
59
- />
60
- ) : null}
61
- </EmojiPickerHeader>
62
- <EmojiPickerGrid />
42
+ <EmojiPickerContentWithColor
43
+ color={color}
44
+ colors={colors}
45
+ getHoverColor={getHoverColor}
46
+ onColorSelect={onColorSelect}
47
+ searchRef={searchRef}
48
+ />
63
49
 
64
- {onColorSelect ? (
50
+ {showFooter ? (
65
51
  <EmojiPickerFooter>
66
- <ColorPicker
67
- colors={colors}
68
- getSwatchHoverColor={getHoverColor}
69
- color={color}
70
- onChangeComplete={onColorSelect}
71
- />
52
+ {onAddCustomEmoji ? (
53
+ <Button size=":button-size/small" onClick={onAddCustomEmoji}>
54
+ Add Emoji
55
+ </Button>
56
+ ) : null}
57
+
58
+ {showSkinTones ? (
59
+ <EmojiPickerSkinTone
60
+ className={css`
61
+ position: absolute;
62
+ top: 4px;
63
+ right: ${space.l}px;
64
+ `}
65
+ />
66
+ ) : null}
72
67
  </EmojiPickerFooter>
73
68
  ) : null}
74
69
  </EmojiPickerContent>