@fibery/ui-kit 1.38.0 → 1.38.1

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 (88) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/config/jest/testFrameworkSetup.js +1 -1
  3. package/package.json +16 -15
  4. package/src/abbreviate-name.ts +42 -0
  5. package/src/actions-menu/actions-menu-item.tsx +14 -5
  6. package/src/actions-menu/actions-menu-props.tsx +1 -0
  7. package/src/actions-menu/actions-menu.tsx +4 -1
  8. package/src/actions-menu/context-actions-menu.tsx +22 -0
  9. package/src/animated-height-container.tsx +24 -5
  10. package/src/antd/ant-upload.tsx +1 -1
  11. package/src/antd/input.tsx +4 -0
  12. package/src/avatar.tsx +1 -27
  13. package/src/button/base-button.tsx +2 -0
  14. package/src/button/button.tsx +3 -2
  15. package/src/button/icon-button.tsx +3 -3
  16. package/src/card-container.tsx +3 -2
  17. package/src/checkbox.tsx +17 -0
  18. package/src/collapsible-section.tsx +3 -1
  19. package/src/date-picker/autocomplete.tsx +11 -5
  20. package/src/date-picker/date-range-input.tsx +5 -1
  21. package/src/date-picker/date-range-picker-popup-content.tsx +31 -13
  22. package/src/date-picker/single-date-input.tsx +5 -1
  23. package/src/date-picker/styles.ts +20 -0
  24. package/src/design-system.ts +24 -0
  25. package/src/dropdown-menu/index.tsx +11 -1
  26. package/src/emoji-picker/emoji-picker-preview-footer.tsx +4 -0
  27. package/src/emoji-picker/emoji.tsx +4 -3
  28. package/src/emoji-picker/primitives/emoji.tsx +49 -51
  29. package/src/emoji-picker/primitives/footer.tsx +6 -0
  30. package/src/emoji-picker/primitives/search.tsx +8 -0
  31. package/src/emoji-picker/primitives/skin-tone.tsx +12 -2
  32. package/src/fibermoji-placeholder.tsx +1 -27
  33. package/src/file-item-2.tsx +408 -0
  34. package/src/file-item.tsx +63 -12
  35. package/src/header-layout.tsx +11 -0
  36. package/src/html-styles.ts +13 -4
  37. package/src/icons/ast/Back.ts +1 -1
  38. package/src/icons/ast/ExtensionFiles.ts +1 -1
  39. package/src/icons/ast/FileUpload.ts +1 -1
  40. package/src/icons/ast/SwitchToMention.ts +8 -0
  41. package/src/icons/ast/SwitchToText.ts +8 -0
  42. package/src/icons/ast/SwitchToWidget.ts +8 -0
  43. package/src/icons/ast/ViewGallery.ts +8 -0
  44. package/src/icons/ast/index.tsx +4 -0
  45. package/src/icons/react/SwitchToMention.tsx +13 -0
  46. package/src/icons/react/SwitchToText.tsx +13 -0
  47. package/src/icons/react/SwitchToWidget.tsx +13 -0
  48. package/src/icons/react/ViewGallery.tsx +13 -0
  49. package/src/icons/react/index.tsx +4 -0
  50. package/src/icons/svg/back.svg +3 -1
  51. package/src/icons/svg/extension/files.svg +3 -1
  52. package/src/icons/svg/file-upload.svg +3 -6
  53. package/src/icons/svg/switch-to-mention.svg +3 -0
  54. package/src/icons/svg/switch-to-text.svg +4 -0
  55. package/src/icons/svg/switch-to-widget.svg +4 -0
  56. package/src/icons/svg/view-gallery.svg +4 -0
  57. package/src/images-gallery/images-gallery-fall-through-provider.tsx +11 -0
  58. package/src/images-gallery/images-gallery.tsx +13 -10
  59. package/src/images-gallery/zoom.tsx +13 -2
  60. package/src/images-gallery-2/images-gallery-2.tsx +442 -0
  61. package/src/images-gallery-2/slide-buttons.tsx +230 -0
  62. package/src/images-gallery-2/zoom.tsx +35 -0
  63. package/src/kbd.tsx +46 -0
  64. package/src/modal-menu/modal-menu-item.tsx +10 -2
  65. package/src/modal-menu/modal-menu-trigger.tsx +19 -2
  66. package/src/notification-dot.tsx +16 -7
  67. package/src/platform-symbols.ts +9 -0
  68. package/src/platform.ts +2 -0
  69. package/src/popover/index.tsx +31 -8
  70. package/src/popover/modifiers.tsx +26 -19
  71. package/src/reactions/reaction-button.tsx +323 -101
  72. package/src/reactions/reaction-picker.tsx +14 -1
  73. package/src/reactions/reactions.tsx +11 -1
  74. package/src/reactions/tootltip.tsx +55 -0
  75. package/src/rich-text-embed-error.tsx +2 -1
  76. package/src/select/components/group-heading.tsx +8 -0
  77. package/src/select/components/menu-list-virtualized.tsx +6 -2
  78. package/src/select/components/option.tsx +4 -0
  79. package/src/select/select-control-settings-context.tsx +11 -2
  80. package/src/select/select-in-popover.tsx +10 -7
  81. package/src/select/styles.ts +21 -1
  82. package/src/tab-nav/tab-nav.tsx +1 -1
  83. package/src/toggle.tsx +8 -0
  84. package/src/tooltip.tsx +27 -18
  85. package/src/type-badge.tsx +5 -2
  86. package/src/unit/unit-with-tooltip.tsx +1 -1
  87. package/src/use-is-phone.tsx +27 -6
  88. package/src/shortcut-badge.tsx +0 -29
@@ -6,6 +6,7 @@ import {AntTransparentInput} from "../antd/input";
6
6
  import {inputOverrides} from "../antd/styles";
7
7
  import {space} from "../design-system";
8
8
  import {IconButton} from "../button/icon-button";
9
+ import {mobileRootSelector} from "../mobile-styles";
9
10
  import RemoveIcon from "../icons/react/Remove";
10
11
  import {AutoComplete, OnChangeArgs} from "./autocomplete";
11
12
  import {InputRef} from "./rc-input-types";
@@ -192,6 +193,9 @@ export const SingleDateInput = forwardRef<
192
193
  display: flex;
193
194
  margin-bottom: ${space.s12 + space.s6}px;
194
195
  padding-right: ${space.s12 - 2}px;
196
+ ${mobileRootSelector} & {
197
+ min-height: ${space.s44}px;
198
+ }
195
199
  `,
196
200
  {[selectedStyle]: selected, [disabledStyle]: disabled}
197
201
  )}
@@ -228,7 +232,7 @@ export const SingleDateInput = forwardRef<
228
232
  />
229
233
  </div>
230
234
  {allowClear && inputValue !== "" && (
231
- <IconButton onClick={onClear} size={"tiny"}>
235
+ <IconButton onClick={onClear} size={"tiny"} supportMobile>
232
236
  <RemoveIcon />
233
237
  </IconButton>
234
238
  )}
@@ -156,6 +156,10 @@ export const datePickerStylesOverrideClassName = css`
156
156
  position: relative;
157
157
 
158
158
  margin: 0;
159
+
160
+ ${mobileRootSelector} & {
161
+ --rdp-cell-size: 44px;
162
+ }
159
163
  }
160
164
 
161
165
  padding: ${space.s12}px;
@@ -171,6 +175,9 @@ export const datePickerStylesOverrideClassName = css`
171
175
  border: none;
172
176
  color: ${themeVars.textColor};
173
177
  border-radius: 0;
178
+ ${mobileRootSelector} & {
179
+ ${textStyles.big}
180
+ }
174
181
  }
175
182
 
176
183
  .rdp-day:focus {
@@ -237,6 +244,14 @@ export const datePickerStylesOverrideClassName = css`
237
244
  .rdp-head th {
238
245
  ${textStyles.small};
239
246
  color: ${themeVars.disabledTextColor};
247
+ ${mobileRootSelector} & {
248
+ ${textStyles.regular}
249
+ color: ${themeVars.accentTextColor};
250
+ }
251
+ }
252
+
253
+ ${mobileRootSelector} & .rdp-caption {
254
+ margin: ${space.s8}px 0;
240
255
  }
241
256
 
242
257
  .rdp-caption_label {
@@ -252,6 +267,11 @@ export const datePickerStylesOverrideClassName = css`
252
267
  padding: ${space.s6}px !important;
253
268
  }
254
269
 
270
+ ${mobileRootSelector} & .rdp-nav_button {
271
+ width: ${space.s44}px;
272
+ height: ${space.s44}px;
273
+ }
274
+
255
275
  .rdp-nav_icon {
256
276
  color: ${themeVars.textColor};
257
277
  }
@@ -33,6 +33,22 @@ export const fontWeight = {
33
33
  bold: 700,
34
34
  } as const;
35
35
 
36
+ // 300 basic, 310 experiment with a variable Inter
37
+ export const fontSize = {
38
+ xxl: 30,
39
+ xl: 24,
40
+ lg: 18,
41
+ md: 16,
42
+ sm: 15,
43
+ xs: 14,
44
+ xxs: 13,
45
+ xxxs: 12,
46
+ tiny: 11,
47
+ mini: 10,
48
+ micro: 9,
49
+ nano: 8,
50
+ } as const;
51
+
36
52
  const transparent = "rgba(255, 255, 255, 0)";
37
53
 
38
54
  export const minPanelWidth = 490;
@@ -246,6 +262,8 @@ export const themeColors = {
246
262
  menuFooterHoverColor: [getOpacities(whiteA.whiteA0).opacity60, slateDark.slate4],
247
263
  menuIconColor: [getOpacities(slate.slate12).opacity70, getOpacities(slateDark.slate12).opacity70],
248
264
  menuSelectedIconColor: [getOpacities(slate.slate1).opacity90, getOpacities(slate.slate1).opacity90],
265
+
266
+ // Text, links colors
249
267
  textColor: [slateDark.slate5, slate.slate6],
250
268
  textSelectionColor: [getOpacities(blue.blue10).opacity20, getOpacities(blueDark.blue10).opacity40],
251
269
  accentTextColor: [slate.slate10, slateDark.slate10],
@@ -575,6 +593,11 @@ export const themeColors = {
575
593
  // Drag and drop
576
594
  colorBgDropLine: [indigo.indigo8, indigoDark.indigo8],
577
595
 
596
+ // Views
597
+ viewBg: [slate.slate1, slateDark.slate1],
598
+ viewSecondaryBg: [slate.slate2, slateDark.slate2],
599
+ viewHighlightBg: [indigo.indigo3, indigoDark.indigo3],
600
+
578
601
  // Board
579
602
  boardBg: [slate.slate1, slateDark.slate1],
580
603
  boardBgWithOpacity80: [getOpacities(slate.slate1).opacity80, getOpacities(slateDark.slate1).opacity80],
@@ -972,6 +995,7 @@ export const space = {
972
995
  s44: 44,
973
996
  s48: 48,
974
997
  s56: 56,
998
+ s60: 60,
975
999
  s64: 64,
976
1000
  s80: 80,
977
1001
  s96: 96,
@@ -10,7 +10,17 @@ export const Root = (dropdownMenuProps: DropdownMenuPrimitive.DropdownMenuProps)
10
10
  <DropdownMenuPrimitive.Root dir="ltr" modal={false} {...dropdownMenuProps} />
11
11
  );
12
12
 
13
- export const Trigger = DropdownMenuPrimitive.Trigger;
13
+ export const Trigger = forwardRef<
14
+ HTMLButtonElement,
15
+ DropdownMenuPrimitive.DropdownMenuTriggerProps & {triggerEvent?: string}
16
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
17
+ >(({triggerEvent, className, children, ...otherProps}, ref) => {
18
+ return (
19
+ <DropdownMenuPrimitive.Trigger ref={ref} className={className} {...otherProps}>
20
+ {children}
21
+ </DropdownMenuPrimitive.Trigger>
22
+ );
23
+ });
14
24
 
15
25
  const rowItemStyles = {
16
26
  ...textStyles.regular,
@@ -9,6 +9,7 @@ import {useEmojiPreview} from "./primitives/preview-provider";
9
9
  import {useEmojiPickerCtx} from "./primitives/root";
10
10
  import {useEmojiSkin} from "./primitives/skin-provider";
11
11
  import {EmojiPickerSkinTone} from "./primitives/skin-tone";
12
+ import {mobileRootSelector} from "../mobile-styles";
12
13
 
13
14
  const previewCss = css`
14
15
  display: flex;
@@ -81,6 +82,9 @@ export const EmojiPickerPreviewFooter: React.FC<{
81
82
  position: absolute;
82
83
  top: 4px;
83
84
  right: ${space.s12}px;
85
+ ${mobileRootSelector} & {
86
+ top: 12px;
87
+ }
84
88
  `}
85
89
  style={{
86
90
  right: space.s12 + measureScrollbar(),
@@ -2,8 +2,9 @@ import {isAndroid} from "../is-android";
2
2
  import {Emoji as EmojiPrimitive, EmojiProps as EmojiPrimitiveProps} from "./primitives/emoji";
3
3
  import {isApple} from "./utils/emoji-set";
4
4
  import {detectEmojiSupportLevel} from "./utils/emoji-support";
5
+ import {forwardRef} from "react";
5
6
 
6
7
  export type EmojiProps = EmojiPrimitiveProps;
7
- export const Emoji: React.FC<EmojiPrimitiveProps> = (props) => {
8
- return <EmojiPrimitive version={detectEmojiSupportLevel()} native={isApple() || isAndroid()} {...props} />;
9
- };
8
+ export const Emoji = forwardRef<HTMLSpanElement, EmojiPrimitiveProps>((props, ref) => {
9
+ return <EmojiPrimitive ref={ref} version={detectEmojiSupportLevel()} native={isApple() || isAndroid()} {...props} />;
10
+ });
@@ -1,4 +1,4 @@
1
- import {FC} from "react";
1
+ import {forwardRef} from "react";
2
2
  import {EmojiItem, EmojiSkin, NativeEmoji} from "@fibery/emoji-data";
3
3
  import {css, cx} from "@linaria/core";
4
4
  import {themeVars} from "../../design-system";
@@ -43,77 +43,75 @@ export type EmojiProps = {
43
43
  emoji: string;
44
44
  /** max supported version of emojis */
45
45
  version?: number;
46
- onClick?: (emoji: EmojiItem, event: React.MouseEvent) => void;
47
46
  skin?: EmojiSkin;
48
47
  /** render emoji as native unicode if possible */
49
48
  native?: boolean;
50
49
  fallback?: "shortcode" | "hidden";
51
50
  showTitle?: boolean;
52
- };
51
+ } & React.HTMLAttributes<HTMLSpanElement>;
53
52
 
54
- function getTitle(emoji: EmojiItem) {
53
+ export function getTitle(emoji: EmojiItem) {
55
54
  if ((emoji as NativeEmoji).native) {
56
55
  return emoji.colons;
57
56
  }
58
57
  return `:${emoji.name}:`;
59
58
  }
60
59
 
61
- export const Emoji: FC<EmojiProps> = ({
62
- emoji: idOrColons,
63
- size,
64
- version = 14,
65
- fallback = "hidden",
66
- skin,
67
- native,
68
- showTitle = false,
69
- }) => {
70
- const emoji = useEmojiDataStoreSelector((store) => store.get(idOrColons, {skin}));
71
- const config = useEmojiRenderConfig();
72
- if (!emoji) {
73
- return fallback === "shortcode" ? <>{idOrColons}</> : null;
74
- }
60
+ export const Emoji = forwardRef<HTMLSpanElement, EmojiProps>(
61
+ (
62
+ {className, emoji: idOrColons, size, version = 14, fallback = "hidden", skin, native, showTitle = false, ...rest},
63
+ ref
64
+ ) => {
65
+ const emoji = useEmojiDataStoreSelector((store) => store.get(idOrColons, {skin}));
66
+ const config = useEmojiRenderConfig();
67
+ if (!emoji) {
68
+ return fallback === "shortcode" ? <>{idOrColons}</> : null;
69
+ }
75
70
 
76
- let emojiNode: React.ReactNode;
71
+ let emojiNode: React.ReactNode;
77
72
 
78
- if ("native" in emoji) {
79
- if (native && version >= emoji.version) {
80
- emojiNode = emoji.native;
73
+ if ("native" in emoji) {
74
+ if (native && version >= emoji.version) {
75
+ emojiNode = emoji.native;
76
+ } else {
77
+ emojiNode = (
78
+ <img
79
+ alt={emoji.name}
80
+ className={cx(size ? emojiSizedImgCss : emojiInlineImgCss)}
81
+ src={emptyPixel}
82
+ style={{
83
+ backgroundImage: `url(${emoji.sprite.src})`,
84
+ backgroundSize: emoji.sprite.size,
85
+ backgroundPosition: emoji.sprite.position,
86
+ }}
87
+ />
88
+ );
89
+ }
81
90
  } else {
82
91
  emojiNode = (
83
92
  <img
84
93
  alt={emoji.name}
94
+ loading={config.loading}
95
+ draggable={config.draggable}
85
96
  className={cx(size ? emojiSizedImgCss : emojiInlineImgCss)}
86
- src={emptyPixel}
87
- style={{
88
- backgroundImage: `url(${emoji.sprite.src})`,
89
- backgroundSize: emoji.sprite.size,
90
- backgroundPosition: emoji.sprite.position,
91
- }}
97
+ src={emoji.imageUrl}
92
98
  />
93
99
  );
94
100
  }
95
- } else {
96
- emojiNode = (
97
- <img
98
- alt={emoji.name}
99
- loading={config.loading}
100
- draggable={config.draggable}
101
- className={cx(size ? emojiSizedImgCss : emojiInlineImgCss)}
102
- src={emoji.imageUrl}
103
- />
104
- );
105
- }
106
101
 
107
- const style = size ? {fontSize: size, width: size, height: size} : {};
102
+ const style = size ? {fontSize: size, width: size, height: size} : {};
108
103
 
109
- return (
110
- <span
111
- style={style}
112
- title={showTitle ? getTitle(emoji) : undefined}
113
- className={cx(emojiWrapperCss, size && emojiWrapperSizedCss)}
114
- aria-label={emoji.name}
115
- >
116
- {emojiNode}
117
- </span>
118
- );
119
- };
104
+ return (
105
+ <span
106
+ ref={ref}
107
+ style={style}
108
+ title={showTitle ? getTitle(emoji) : undefined}
109
+ className={cx(className, emojiWrapperCss, size && emojiWrapperSizedCss)}
110
+ aria-label={emoji.name}
111
+ {...rest}
112
+ >
113
+ {emojiNode}
114
+ </span>
115
+ );
116
+ }
117
+ );
@@ -1,13 +1,19 @@
1
1
  import {css} from "@linaria/core";
2
2
  import {space} from "../../design-system";
3
+ import {mobileRootSelector} from "../../mobile-styles";
3
4
 
4
5
  const footerCss = css`
5
6
  position: relative;
6
7
  border-top: 1px solid var(--fibery-color-separatorColor);
7
8
  padding: ${space.s6}px ${space.s12}px;
9
+ height: 40px;
8
10
 
9
11
  display: flex;
10
12
  align-items: center;
13
+
14
+ ${mobileRootSelector} & {
15
+ height: 68px;
16
+ }
11
17
  `;
12
18
 
13
19
  export const EmojiPickerFooter: React.FC<React.PropsWithChildren> = ({children}) => {
@@ -12,6 +12,7 @@ import {useEmojiPickerCtx, useEmojiPickerI18N} from "./root";
12
12
  import {useFoundEmojis, useSetFoundEmojis} from "./search-provider";
13
13
  import {useEmojiSkin} from "./skin-provider";
14
14
  import ClearInputFilled from "../../icons/react/ClearInputFilled";
15
+ import {mobileRootSelector} from "../../mobile-styles";
15
16
 
16
17
  const searchCss = css`
17
18
  ${textStyles.regular}
@@ -27,6 +28,10 @@ const searchCss = css`
27
28
  padding: 0 ${space.s4}px;
28
29
  gap: ${space.s4}px;
29
30
 
31
+ ${mobileRootSelector} & {
32
+ height: 40px;
33
+ }
34
+
30
35
  /* For screenreaders only, via https://stackoverflow.com/a/19758620 */
31
36
  & .sr-only {
32
37
  position: absolute;
@@ -59,6 +64,9 @@ const searchInputCss = css`
59
64
 
60
65
  const searchIconCss = css`
61
66
  padding: ${space.s4}px;
67
+ ${mobileRootSelector} & {
68
+ padding: ${space.s6}px;
69
+ }
62
70
  `;
63
71
 
64
72
  const extraActionSlotCss = css`
@@ -1,8 +1,10 @@
1
1
  import {EmojiSkin} from "@fibery/emoji-data";
2
2
  import {css, cx} from "@linaria/core";
3
3
  import {useState} from "react";
4
+ import {useIsPhone} from "../../use-is-phone";
4
5
  import {IconButton} from "../../button/icon-button";
5
6
  import {textStyles, themeVars} from "../../design-system";
7
+ import {mobileRootSelector} from "../../mobile-styles";
6
8
  import {Emoji} from "./emoji";
7
9
  import {useEmojiPickerCtx, useEmojiPickerI18N} from "./root";
8
10
  import {useEmojiSkin, useSetEmojiSkin} from "./skin-provider";
@@ -44,6 +46,10 @@ const skinToneSwatchCss = css`
44
46
 
45
47
  const expandedCss = css`
46
48
  width: 32px;
49
+
50
+ ${mobileRootSelector} & {
51
+ width: 44px;
52
+ }
47
53
  `;
48
54
 
49
55
  const openedSkinTextCss = css`
@@ -55,12 +61,17 @@ const openedSkinTextCss = css`
55
61
  width: 98px;
56
62
  color: ${themeVars.accentTextColor};
57
63
  padding-left: 8px;
64
+
65
+ ${mobileRootSelector} & {
66
+ display: none;
67
+ }
58
68
  `;
59
69
 
60
70
  export type EmojiPickerSkinToneProps = {
61
71
  className?: string;
62
72
  };
63
73
  export const EmojiPickerSkinTone: React.FC<EmojiPickerSkinToneProps> = ({className}) => {
74
+ const isPhone = useIsPhone();
64
75
  const [opened, setOpened] = useState(false);
65
76
  const emojiPickerCtx = useEmojiPickerCtx();
66
77
  const i18n = useEmojiPickerI18N();
@@ -75,14 +86,13 @@ export const EmojiPickerSkinTone: React.FC<EmojiPickerSkinToneProps> = ({classNa
75
86
  skinToneNodes.push(
76
87
  <span key={`skin-tone-${skinTone}`} className={cx(skinToneSwatchCss, (selected || opened) && expandedCss)}>
77
88
  <IconButton
78
- size="large"
89
+ size={isPhone ? "xLarge" : "large"}
79
90
  onClick={() => {
80
91
  if (opened) {
81
92
  setSkin(skinTone as EmojiSkin);
82
93
  }
83
94
  setOpened(!opened);
84
95
  }}
85
- supportMobile
86
96
  className={css`
87
97
  &:hover:not(:disabled),
88
98
  &:focus-visible {
@@ -3,33 +3,7 @@ import {css} from "@linaria/core";
3
3
  import {FC, useEffect, useRef} from "react";
4
4
  import {textStyles} from "./design-system";
5
5
  import {useFeatures} from "./features";
6
-
7
- const preservedNameEmojis = ["🤖", "👻", "🧮", "🪄", "🔌", "🔗", "🔒"];
8
-
9
- const abbreviateName = (name?: string) => {
10
- if (name === undefined) {
11
- return "…";
12
- }
13
- if (preservedNameEmojis.includes(name)) {
14
- return name;
15
- }
16
- const filteredName = (name || "").replace(/[!~.\-@[\]]/g, "").trim();
17
- const match = filteredName.match(/\p{Letter}[\p{Letter} ]+/gu);
18
- const processedName = match ? match[0] : "";
19
- const parts = processedName.split(" ").filter((part) => part !== "");
20
- let result;
21
- if (parts.length === 1) {
22
- result = processedName.slice(0, 2);
23
- } else {
24
- result = parts
25
- .filter((part, index) => index < 2)
26
- .map((part) => {
27
- return part[0].toUpperCase();
28
- })
29
- .join("");
30
- }
31
- return result || "?";
32
- };
6
+ import {abbreviateName} from "./abbreviate-name";
33
7
 
34
8
  const cyrb53 = (str: string, seed = 0) => {
35
9
  let h1 = 0xdeadbeef ^ seed;