@fibery/ui-kit 1.38.0 → 1.38.2

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 (111) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/config/jest/testFrameworkSetup.js +1 -1
  3. package/package.json +23 -22
  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 +4 -4
  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/index.tsx +4 -2
  19. package/src/collapsible-section.tsx +3 -1
  20. package/src/date-picker/autocomplete.tsx +11 -5
  21. package/src/date-picker/date-range-input.tsx +5 -1
  22. package/src/date-picker/date-range-picker-popup-content.tsx +31 -13
  23. package/src/date-picker/single-date-input.tsx +5 -1
  24. package/src/date-picker/styles.ts +20 -0
  25. package/src/design-system.ts +26 -1
  26. package/src/dropdown-menu/index.tsx +11 -1
  27. package/src/emoji-picker/emoji-picker-preview-footer.tsx +4 -0
  28. package/src/emoji-picker/emoji.tsx +4 -3
  29. package/src/emoji-picker/primitives/emoji.tsx +49 -51
  30. package/src/emoji-picker/primitives/footer.tsx +6 -0
  31. package/src/emoji-picker/primitives/search.tsx +8 -0
  32. package/src/emoji-picker/primitives/skin-tone.tsx +12 -2
  33. package/src/fibermoji-placeholder.tsx +2 -29
  34. package/src/file-item-2.tsx +566 -0
  35. package/src/file-item.tsx +196 -162
  36. package/src/header-layout.tsx +11 -0
  37. package/src/html-styles.ts +13 -4
  38. package/src/icons/ast/Back.ts +1 -1
  39. package/src/icons/ast/ExtensionFiles.ts +1 -1
  40. package/src/icons/ast/FileArchive.ts +8 -0
  41. package/src/icons/ast/FileBroken.ts +8 -0
  42. package/src/icons/ast/FileDocument.ts +8 -0
  43. package/src/icons/ast/FileImage.ts +8 -0
  44. package/src/icons/ast/FilePresentation.ts +8 -0
  45. package/src/icons/ast/FileTable.ts +8 -0
  46. package/src/icons/ast/FileUpload.ts +1 -1
  47. package/src/icons/ast/FileVideo.ts +8 -0
  48. package/src/icons/ast/SwitchToMention.ts +8 -0
  49. package/src/icons/ast/SwitchToText.ts +8 -0
  50. package/src/icons/ast/SwitchToWidget.ts +8 -0
  51. package/src/icons/ast/ViewGallery.ts +8 -0
  52. package/src/icons/ast/index.tsx +11 -0
  53. package/src/icons/react/FileArchive.tsx +13 -0
  54. package/src/icons/react/FileBroken.tsx +13 -0
  55. package/src/icons/react/FileDocument.tsx +13 -0
  56. package/src/icons/react/FileImage.tsx +13 -0
  57. package/src/icons/react/FilePresentation.tsx +13 -0
  58. package/src/icons/react/FileTable.tsx +13 -0
  59. package/src/icons/react/FileVideo.tsx +13 -0
  60. package/src/icons/react/SwitchToMention.tsx +13 -0
  61. package/src/icons/react/SwitchToText.tsx +13 -0
  62. package/src/icons/react/SwitchToWidget.tsx +13 -0
  63. package/src/icons/react/ViewGallery.tsx +13 -0
  64. package/src/icons/react/index.tsx +11 -0
  65. package/src/icons/svg/back.svg +3 -1
  66. package/src/icons/svg/extension/files.svg +3 -1
  67. package/src/icons/svg/file-archive.svg +3 -0
  68. package/src/icons/svg/file-broken.svg +3 -0
  69. package/src/icons/svg/file-document.svg +3 -0
  70. package/src/icons/svg/file-image.svg +3 -0
  71. package/src/icons/svg/file-presentation.svg +3 -0
  72. package/src/icons/svg/file-table.svg +3 -0
  73. package/src/icons/svg/file-upload.svg +3 -6
  74. package/src/icons/svg/file-video.svg +3 -0
  75. package/src/icons/svg/switch-to-mention.svg +3 -0
  76. package/src/icons/svg/switch-to-text.svg +4 -0
  77. package/src/icons/svg/switch-to-widget.svg +4 -0
  78. package/src/icons/svg/view-gallery.svg +4 -0
  79. package/src/images-gallery/images-gallery-fall-through-provider.tsx +11 -0
  80. package/src/images-gallery/images-gallery.tsx +13 -10
  81. package/src/images-gallery/zoom.tsx +13 -2
  82. package/src/images-gallery-2/images-gallery-2.tsx +522 -0
  83. package/src/images-gallery-2/slide-buttons.tsx +271 -0
  84. package/src/images-gallery-2/zoom.tsx +35 -0
  85. package/src/kbd.tsx +46 -0
  86. package/src/loading-sausage.tsx +2 -2
  87. package/src/modal-menu/modal-menu-item.tsx +10 -2
  88. package/src/modal-menu/modal-menu-trigger.tsx +19 -2
  89. package/src/notification-dot.tsx +16 -7
  90. package/src/platform-symbols.ts +9 -0
  91. package/src/platform.ts +2 -0
  92. package/src/popover/index.tsx +31 -8
  93. package/src/popover/modifiers.tsx +26 -19
  94. package/src/reactions/reaction-button.tsx +323 -101
  95. package/src/reactions/reaction-picker.tsx +14 -1
  96. package/src/reactions/reactions.tsx +11 -1
  97. package/src/reactions/tootltip.tsx +55 -0
  98. package/src/rich-text-embed-error.tsx +2 -1
  99. package/src/select/components/group-heading.tsx +8 -0
  100. package/src/select/components/menu-list-virtualized.tsx +6 -2
  101. package/src/select/components/option.tsx +4 -0
  102. package/src/select/select-control-settings-context.tsx +11 -2
  103. package/src/select/select-in-popover.tsx +10 -7
  104. package/src/select/styles.ts +21 -1
  105. package/src/tab-nav/tab-nav.tsx +1 -1
  106. package/src/toggle.tsx +8 -0
  107. package/src/tooltip.tsx +28 -19
  108. package/src/type-badge.tsx +5 -2
  109. package/src/unit/unit-with-tooltip.tsx +1 -1
  110. package/src/use-is-phone.tsx +34 -6
  111. package/src/shortcut-badge.tsx +0 -29
@@ -1,107 +1,357 @@
1
- import {css, cx} from "@linaria/core";
2
- import {Fragment, ReactNode, useState} from "react";
1
+ import {css} from "@linaria/core";
2
+ import {Fragment, ReactEventHandler, ReactNode, useCallback, useState, useRef} from "react";
3
3
  import {createPortal} from "react-dom";
4
- import {border, lineHeight, space, themeVars} from "../design-system";
4
+ import {fontSize, fontWeight, space, themeVars} from "../design-system";
5
5
  import {Emoji} from "../emoji-picker/emoji";
6
6
  import {ToggleButton} from "../toggle-button/toggle-button";
7
7
  import {Tooltip} from "../tooltip";
8
8
  import {useIsPhone} from "../use-is-phone";
9
- import {safeAreaInsetBottomVar} from "../mobile-styles";
9
+ import {mobileRootSelector, safeAreaInsetBottomVar} from "../mobile-styles";
10
10
  import {useLongPress} from "../use-long-press";
11
11
  import {getWordDivider} from "./get-word-divider";
12
+ import {EmojiTooltipDescription, EmojiTooltipTitle} from "./tootltip";
13
+ import {TabNavLink, TabNavRoot, TabNavList, tabButton} from "../tab-nav/tab-nav";
14
+ import {AvatarImage} from "../avatar";
15
+ import {getPopupContainerElement} from "../utils-dom";
12
16
 
13
17
  export type Reaction = {
14
18
  count: number;
15
19
  key: string;
16
20
  selected: boolean;
17
21
  alias?: string;
18
- userNames?: Array<string>;
22
+ users?: Array<{name: string; avatarUrl: string | null}>;
19
23
  };
20
24
 
21
25
  type Props = {
22
26
  reaction: Reaction;
27
+ reactions: Array<Reaction>;
23
28
  disabled?: boolean;
24
29
  onSelect: (reactionKey: string) => unknown;
25
30
  };
26
31
 
27
- // @deprecated. remove it
28
- export const reactionButtonCss = css`
29
- all: unset;
30
- padding-inline: ${space.s6}px;
31
- box-sizing: border-box;
32
- display: flex;
33
- align-items: center;
32
+ const reactionButtonCss = css`
34
33
  min-width: ${space.s24 + space.s12}px;
35
- justify-content: center;
36
- height: ${space.s24 + 2}px;
37
- border: 1.5px solid ${themeVars.colorBgReactionsDefault};
38
- border-radius: ${border.radius8}px;
39
- background-color: ${themeVars.colorBgReactionsDefault};
40
- cursor: pointer;
41
- line-height: ${lineHeight.nowrap};
42
- -webkit-user-select: none;
43
-
44
- &[aria-disabled="true"] {
45
- cursor: default;
46
- border-color: ${themeVars.colorBorderReactionsHover};
47
- background-color: transparent;
48
- }
49
-
50
- &:not([aria-disabled="true"]):hover {
51
- background-color: ${themeVars.colorBgReactionsHover};
52
- border-color: ${themeVars.colorBorderReactionsHover};
34
+ ${mobileRootSelector} & {
35
+ height: ${space.s28}px;
36
+ padding-inline: ${space.s6}px;
53
37
  }
54
38
  `;
55
-
56
- const reactionButtonCssV2 = css`
57
- min-width: ${space.s24 + space.s12}px;
58
- `;
59
-
39
+ const zIndexOverlay = 10001;
60
40
  const overlayClassName = css`
61
41
  position: absolute;
62
- z-index: 10001;
42
+ z-index: ${zIndexOverlay};
63
43
  display: flex;
64
44
  flex-direction: column;
65
45
  inset: 0;
66
46
  `;
67
47
 
68
48
  function TouchOverlay({onClick}: {onClick: () => void}) {
69
- return createPortal(<div className={overlayClassName} onClick={onClick} />, document.body);
49
+ return createPortal(<div className={overlayClassName} onClick={onClick} />, getPopupContainerElement());
70
50
  }
71
51
 
72
52
  function MobileOverlay({content, onClose}: {content: ReactNode; onClose: () => void}) {
53
+ const handleClose: ReactEventHandler<HTMLDivElement> = useCallback(
54
+ (e) => {
55
+ e.stopPropagation();
56
+ onClose();
57
+ },
58
+ [onClose]
59
+ );
73
60
  return createPortal(
74
61
  <div className={overlayClassName}>
75
62
  <div
76
- onClick={onClose}
63
+ onPointerDown={handleClose}
77
64
  className={css`
78
65
  background-color: ${themeVars.modalBg};
79
66
  flex-grow: 1;
80
67
  `}
81
68
  />
82
69
  <div
70
+ onPointerDown={(e) => {
71
+ e.stopPropagation();
72
+ }}
83
73
  className={css`
84
74
  flex-shrink: 0;
85
- background-color: ${themeVars.tooltipBgColor};
75
+ height: 70vh;
76
+ background-color: ${themeVars.panelBg};
86
77
  padding-bottom: var(${safeAreaInsetBottomVar});
87
78
  `}
88
79
  >
89
80
  {content}
90
81
  </div>
91
82
  </div>,
92
- document.body
83
+ getPopupContainerElement()
84
+ );
85
+ }
86
+
87
+ const navLinkClassName = css`
88
+ &.${tabButton} {
89
+ height: 32px;
90
+ }
91
+ `;
92
+
93
+ const canScrollVar = "--can-scroll-fibery-reaction";
94
+
95
+ const tabNavRootWithShadow = css`
96
+ padding: 0;
97
+ display: flex;
98
+ overflow-x: auto;
99
+ scrollbar-width: none;
100
+ position: relative;
101
+ align-items: center;
102
+ border-bottom: 0.5px solid ${themeVars.separatorColor};
103
+ & > div {
104
+ flex-grow: 1;
105
+ top: ${space.s6}px;
106
+ }
107
+ :not(ul) ul {
108
+ gap: ${space.s12}px;
109
+ }
110
+ /* Only show Scroll Indicators when the scroller can actually scroll */
111
+ @keyframes detect-scroll {
112
+ from,
113
+ to {
114
+ ${canScrollVar}: 1;
115
+ }
116
+ }
117
+ /* Scroll Indicators */
118
+ @keyframes reveal {
119
+ 0% {
120
+ opacity: 0;
121
+ }
122
+
123
+ 100% {
124
+ opacity: 1;
125
+ }
126
+ }
127
+ &::before,
128
+ &::after {
129
+ content: "";
130
+ display: block;
131
+ flex-shrink: 0;
132
+ width: 12px;
133
+ height: ${44 + 12}px;
134
+ position: sticky;
135
+ visibility: hidden;
136
+ }
137
+
138
+ @supports (animation-timeline: scroll()) {
139
+ & {
140
+ scroll-timeline: --scroll-timeline x;
141
+ }
142
+ &::before {
143
+ background: linear-gradient(to right, rgba(0, 0, 0, 0.15), transparent);
144
+ }
145
+
146
+ &::after {
147
+ background: linear-gradient(to left, rgba(0, 0, 0, 0.15), transparent);
148
+ animation-direction: reverse;
149
+ animation-range: calc(100% - 40px) calc(100% - 20px);
150
+ }
151
+ &::before,
152
+ &::after {
153
+ animation-name: reveal;
154
+ animation-timeline: --scroll-timeline;
155
+ animation-fill-mode: both;
156
+ }
157
+
158
+ &::before {
159
+ left: 0;
160
+ animation-range: 1em 2em;
161
+ }
162
+
163
+ &::after {
164
+ right: 0;
165
+ animation-direction: reverse;
166
+ animation-range: calc(100% - 2em) calc(100% - 1em);
167
+ }
168
+
169
+ & {
170
+ animation: detect-scroll;
171
+ animation-timeline: --scroll-timeline;
172
+ animation-fill-mode: none;
173
+ }
174
+
175
+ &::before,
176
+ &::after {
177
+ --visibility-if-can-scroll: var(${canScrollVar}) visible;
178
+ --visibility-if-cant-scroll: hidden;
179
+ visibility: var(--visibility-if-can-scroll, var(--visibility-if-cant-scroll));
180
+ }
181
+ }
182
+ `;
183
+
184
+ function PhoneTooltipContent({reactions}: {reactions: Array<Reaction>}): JSX.Element {
185
+ const [selectedTab, setSelectedTab] = useState("all");
186
+ const listRef = useRef<HTMLUListElement>(null);
187
+ const hasMultipleReactions = reactions.length > 1;
188
+
189
+ const allReactionsContent = (
190
+ <div
191
+ className={css`
192
+ display: flex;
193
+ flex-direction: column;
194
+ gap: ${space.s16}px;
195
+ `}
196
+ >
197
+ {reactions.map((reaction) => (
198
+ <div
199
+ key={reaction.key}
200
+ className={css`
201
+ display: flex;
202
+ align-items: center;
203
+ gap: ${space.s16}px;
204
+ `}
205
+ >
206
+ <Emoji size={24} emoji={reaction.key} />
207
+ <div>
208
+ <div
209
+ className={css`
210
+ font-size: ${fontSize.sm}px;
211
+ font-weight: ${fontWeight.medium};
212
+ `}
213
+ >
214
+ {reaction.alias || reaction.key}
215
+ </div>
216
+ {reaction.users ? (
217
+ <div
218
+ className={css`
219
+ color: ${themeVars.accentTextColor};
220
+ display: -webkit-box;
221
+ -webkit-line-clamp: 3;
222
+ -webkit-box-orient: vertical;
223
+ overflow: hidden;
224
+ text-overflow: ellipsis;
225
+ `}
226
+ >
227
+ <PrintNames users={reaction.users} />
228
+ </div>
229
+ ) : null}
230
+ </div>
231
+ </div>
232
+ ))}
233
+ </div>
234
+ );
235
+
236
+ const singleReactionContent = (reaction: Reaction | undefined | null) => {
237
+ if (!reaction) {
238
+ return null;
239
+ }
240
+ return (
241
+ <div
242
+ className={css`
243
+ display: flex;
244
+ flex-direction: column;
245
+ gap: ${space.s16}px;
246
+ `}
247
+ >
248
+ <div
249
+ className={css`
250
+ font-weight: ${fontWeight.medium};
251
+ `}
252
+ >
253
+ {reaction.alias || reaction.key}
254
+ </div>
255
+ {reaction.users?.map(({name: userName, avatarUrl}) => (
256
+ <div
257
+ key={userName}
258
+ className={css`
259
+ display: flex;
260
+ align-items: center;
261
+ gap: ${space.s12}px;
262
+ `}
263
+ >
264
+ <AvatarImage avatarUrl={avatarUrl} title={userName} size={24} />
265
+ <span
266
+ className={css`
267
+ overflow-wrap: anywhere;
268
+ font-size: ${fontSize.sm}px;
269
+ font-weight: ${fontWeight.medium};
270
+ `}
271
+ >
272
+ {userName}
273
+ </span>
274
+ </div>
275
+ ))}
276
+ </div>
277
+ );
278
+ };
279
+
280
+ return (
281
+ <>
282
+ <TabNavRoot className={tabNavRootWithShadow}>
283
+ <TabNavList ref={listRef}>
284
+ {hasMultipleReactions ? (
285
+ <TabNavLink
286
+ className={navLinkClassName}
287
+ wide
288
+ active={selectedTab === "all"}
289
+ onClick={() => setSelectedTab("all")}
290
+ >
291
+ All
292
+ </TabNavLink>
293
+ ) : null}
294
+ {reactions.map((reaction) => (
295
+ <TabNavLink
296
+ className={navLinkClassName}
297
+ key={reaction.key}
298
+ wide
299
+ active={selectedTab === reaction.key || !hasMultipleReactions}
300
+ onClick={() => setSelectedTab(reaction.key)}
301
+ before={<Emoji emoji={reaction.key} />}
302
+ >
303
+ {reaction.count}
304
+ </TabNavLink>
305
+ ))}
306
+ </TabNavList>
307
+ </TabNavRoot>
308
+
309
+ <div
310
+ className={css`
311
+ padding: ${space.s12}px;
312
+ overflow-y: auto;
313
+ `}
314
+ >
315
+ {selectedTab === "all" && hasMultipleReactions
316
+ ? allReactionsContent
317
+ : singleReactionContent(hasMultipleReactions ? reactions.find((r) => r.key === selectedTab) : reactions[0])}
318
+ </div>
319
+ </>
320
+ );
321
+ }
322
+
323
+ function PrintNames({users}: {users: {name: string}[]}): JSX.Element {
324
+ return (
325
+ <>
326
+ {users.map(({name}, index, names) => {
327
+ return (
328
+ <Fragment key={index}>
329
+ <span
330
+ className={css`
331
+ overflow-wrap: anywhere;
332
+ `}
333
+ >
334
+ {name}
335
+ </span>
336
+ {getWordDivider(names.length, index)}
337
+ </Fragment>
338
+ );
339
+ })}{" "}
340
+ </>
93
341
  );
94
342
  }
95
343
 
96
- export const ReactionButton = ({reaction, disabled, onSelect}: Props): JSX.Element => {
344
+ export const ReactionButton = ({reaction, disabled, onSelect, reactions}: Props): JSX.Element => {
97
345
  const [showOverlay, setShowOverlay] = useState<true | undefined>(undefined);
98
346
  const isPhone = useIsPhone();
347
+
99
348
  const onLongPress = () => {
100
349
  // https://github.com/radix-ui/primitives/issues/931 - disable text selection
101
350
  if (isPhone) {
102
351
  document.body.style.userSelect = "none";
103
352
  document.body.style.webkitUserSelect = "none";
104
353
  }
354
+
105
355
  setShowOverlay(true);
106
356
  };
107
357
  const close = () => {
@@ -119,7 +369,7 @@ export const ReactionButton = ({reaction, disabled, onSelect}: Props): JSX.Eleme
119
369
 
120
370
  const button = (
121
371
  <ToggleButton
122
- className={reactionButtonCssV2}
372
+ className={reactionButtonCss}
123
373
  disabled={disabled}
124
374
  iconStart={
125
375
  <div
@@ -127,6 +377,27 @@ export const ReactionButton = ({reaction, disabled, onSelect}: Props): JSX.Eleme
127
377
  font-size: 16px;
128
378
  height: 16px;
129
379
  width: 16px;
380
+ font-size-adjust: none;
381
+
382
+ ${mobileRootSelector} & {
383
+ font-size: 18px;
384
+ height: 18px;
385
+ width: 18px;
386
+ line-height: 18px;
387
+ @supports (-webkit-touch-callout: none) {
388
+ /* Safari specific fix */
389
+ line-height: 17px;
390
+ .emoji {
391
+ font-size: 0.8em;
392
+ position: relative;
393
+ top: -1px;
394
+ img {
395
+ font-size: 1.2em;
396
+ top: 1px;
397
+ }
398
+ }
399
+ }
400
+ }
130
401
  `}
131
402
  {...longPressEvent}
132
403
  >
@@ -136,65 +407,15 @@ export const ReactionButton = ({reaction, disabled, onSelect}: Props): JSX.Eleme
136
407
  onPressedChange={() => onSelect(reaction.key)}
137
408
  pressed={reaction.selected}
138
409
  >
139
- {reaction.count}
410
+ <span>{reaction.count}</span>
140
411
  </ToggleButton>
141
412
  );
142
413
 
143
- if (reaction?.userNames?.length) {
144
- // noinspection CssInvalidPropertyValue
145
- const title = (
146
- <div
147
- className={css`
148
- padding-top: ${space.s6}px;
149
- padding-left: ${space.s6}px;
150
- padding-right: ${space.s6}px;
151
- display: flex;
152
- justify-content: center;
153
- `}
154
- >
155
- <div
156
- className={css`
157
- background-color: ${themeVars.whiteColor};
158
- padding: ${space.s4}px;
159
- border-radius: ${border.radius4}px;
160
- `}
161
- >
162
- <Emoji emoji={reaction.key} size={64} />
163
- </div>
164
- </div>
165
- );
414
+ if (reaction?.users?.length) {
415
+ const title = <EmojiTooltipTitle emoji={reaction.key} />;
166
416
  const description = (
167
- <div
168
- className={cx(
169
- css`
170
- padding-top: ${space.s8}px;
171
- padding-bottom: ${space.s6}px;
172
- padding-left: ${space.s6}px;
173
- padding-right: ${space.s6}px;
174
- text-align: center;
175
- word-wrap: break-word;
176
- color: ${themeVars.whiteColor};
177
- `,
178
- !isPhone &&
179
- css`
180
- max-width: 200px;
181
- `
182
- )}
183
- >
184
- {reaction.userNames.map((name, index, names) => {
185
- return (
186
- <Fragment key={index}>
187
- <span
188
- className={css`
189
- overflow-wrap: break-word;
190
- `}
191
- >
192
- {name}
193
- </span>
194
- {getWordDivider(names.length, index)}
195
- </Fragment>
196
- );
197
- })}
417
+ <EmojiTooltipDescription>
418
+ <PrintNames users={reaction.users} />
198
419
  <span
199
420
  className={css`
200
421
  color: ${themeVars.shortcutTextColor};
@@ -202,7 +423,7 @@ export const ReactionButton = ({reaction, disabled, onSelect}: Props): JSX.Eleme
202
423
  >
203
424
  reacted with {reaction.alias || reaction.key}
204
425
  </span>
205
- </div>
426
+ </EmojiTooltipDescription>
206
427
  );
207
428
  if (isPhone) {
208
429
  return (
@@ -214,11 +435,12 @@ export const ReactionButton = ({reaction, disabled, onSelect}: Props): JSX.Eleme
214
435
  content={
215
436
  <div
216
437
  className={css`
217
- padding: ${space.s12}px;
438
+ display: flex;
439
+ height: 100%;
440
+ flex-direction: column;
218
441
  `}
219
442
  >
220
- {title}
221
- {description}
443
+ <PhoneTooltipContent reactions={reactions} />
222
444
  </div>
223
445
  }
224
446
  />
@@ -10,6 +10,8 @@ import AddReactions from "../icons/react/AddReactions";
10
10
  import {Popover} from "../popover";
11
11
  import {ToggleButton} from "../toggle-button/toggle-button";
12
12
  import {Tooltip} from "../tooltip";
13
+ import {mobileRootSelector} from "../mobile-styles";
14
+ import {useIsPhone} from "../use-is-phone";
13
15
 
14
16
  type Props = {
15
17
  mode: "default" | "in-row";
@@ -27,6 +29,14 @@ const popupContainerCss = css`
27
29
  z-index: ${zIndex};
28
30
  `;
29
31
 
32
+ const buttonCss = css`
33
+ ${mobileRootSelector} & {
34
+ min-width: ${space.s28}px;
35
+ height: ${space.s28}px;
36
+ padding-inline: ${space.s8}px;
37
+ }
38
+ `;
39
+
30
40
  const popupCss = css`
31
41
  //background-color: transparent;
32
42
  //box-shadow: none;
@@ -60,11 +70,14 @@ export const ReactionPicker = ({mode, onSelect, open, onOpenChange, EmojiPicker}
60
70
  [onSelect, setOpen]
61
71
  );
62
72
 
73
+ const isPhone = useIsPhone();
74
+
63
75
  return (
64
76
  <div>
65
77
  <Tooltip side="bottom" title="Add reaction">
66
78
  {mode === "in-row" ? (
67
79
  <ToggleButton
80
+ className={buttonCss}
68
81
  ref={setTriggerDomElement}
69
82
  onClick={() => setOpen(!isOpen)}
70
83
  aria-expanded={isOpen}
@@ -76,7 +89,7 @@ export const ReactionPicker = ({mode, onSelect, open, onOpenChange, EmojiPicker}
76
89
  paddingInline: 0,
77
90
  }}
78
91
  >
79
- <AddReactions />
92
+ <AddReactions iconSize={isPhone ? 20 : 16} />
80
93
  </ToggleButton>
81
94
  ) : (
82
95
  <IconButton
@@ -4,6 +4,7 @@ import {ReactionPicker} from "./reaction-picker";
4
4
  import {EmojiPickerProps} from "../emoji-picker/emoji-picker";
5
5
  import {space} from "../design-system";
6
6
  import {type FC} from "react";
7
+ import {mobileRootSelector} from "../mobile-styles";
7
8
 
8
9
  type Props = {
9
10
  reactions: Array<Reaction>;
@@ -16,13 +17,22 @@ const blockReactionsCss = css`
16
17
  display: flex;
17
18
  flex-wrap: wrap;
18
19
  gap: ${space.s4}px;
20
+ ${mobileRootSelector} & {
21
+ gap: ${space.s6}px;
22
+ }
19
23
  `;
20
24
 
21
25
  export const Reactions: React.FC<Props> = ({reactions, onSelect, readonly, EmojiPicker}) => {
22
26
  return (
23
27
  <div className={blockReactionsCss}>
24
28
  {reactions.map((reaction) => (
25
- <ReactionButton disabled={readonly} key={reaction.key} reaction={reaction} onSelect={onSelect} />
29
+ <ReactionButton
30
+ reactions={reactions}
31
+ disabled={readonly}
32
+ key={reaction.key}
33
+ reaction={reaction}
34
+ onSelect={onSelect}
35
+ />
26
36
  ))}
27
37
  {!readonly ? <ReactionPicker EmojiPicker={EmojiPicker} mode={"in-row"} onSelect={onSelect} /> : null}
28
38
  </div>
@@ -0,0 +1,55 @@
1
+ import {css, cx} from "@linaria/core";
2
+ import {border, space, themeVars} from "../design-system";
3
+ import {Emoji} from "../emoji-picker/emoji";
4
+ import {useIsPhone} from "../use-is-phone";
5
+ import {ReactNode} from "react";
6
+
7
+ export const EmojiTooltipTitle = ({emoji}: {emoji: string}) => {
8
+ return (
9
+ <div
10
+ className={css`
11
+ padding-top: ${space.s6}px;
12
+ padding-left: ${space.s6}px;
13
+ padding-right: ${space.s6}px;
14
+ display: flex;
15
+ justify-content: center;
16
+ `}
17
+ >
18
+ <div
19
+ className={css`
20
+ display: flex;
21
+ background-color: ${themeVars.whiteColor};
22
+ padding: ${space.s4}px;
23
+ border-radius: ${border.radius4}px;
24
+ `}
25
+ >
26
+ <Emoji emoji={emoji} size={64} />
27
+ </div>
28
+ </div>
29
+ );
30
+ };
31
+
32
+ export const EmojiTooltipDescription = ({children}: {children: ReactNode}) => {
33
+ const isPhone = useIsPhone();
34
+ return (
35
+ <div
36
+ className={cx(
37
+ css`
38
+ padding-top: ${space.s8}px;
39
+ padding-bottom: ${space.s6}px;
40
+ padding-left: ${space.s6}px;
41
+ padding-right: ${space.s6}px;
42
+ text-align: center;
43
+ word-wrap: break-word;
44
+ color: ${themeVars.whiteColor};
45
+ `,
46
+ !isPhone &&
47
+ css`
48
+ max-width: 200px;
49
+ `
50
+ )}
51
+ >
52
+ {children}
53
+ </div>
54
+ );
55
+ };
@@ -4,7 +4,8 @@ import ImageXmark from "./icons/react/ImageXmark";
4
4
  import {type ReactNode} from "react";
5
5
 
6
6
  export const richTextEmbedErrorStyle = css`
7
- height: 380px;
7
+ min-height: 380px;
8
+ height: 100%;
8
9
  display: flex;
9
10
  flex-direction: column;
10
11
  align-items: center;
@@ -4,6 +4,7 @@ import {layout, space, textStyles, themeVars} from "../../design-system";
4
4
  import {TooltipIfOverflown} from "../../tooltip-if-overflown";
5
5
  import {GroupBase} from "../index";
6
6
  import {isPureTextChildren} from "../util";
7
+ import {mobileRootSelector} from "../../mobile-styles";
7
8
 
8
9
  export const groupHeadingNulledStyles = {
9
10
  paddingLeft: "",
@@ -15,6 +16,9 @@ export const groupHeadingNulledStyles = {
15
16
  ...textStyles.small,
16
17
  color: "",
17
18
  height: layout.menuItemHeight,
19
+ [`${mobileRootSelector} &`]: {
20
+ height: layout.mobileMenuItemHeight,
21
+ },
18
22
  };
19
23
  const groupHeadingClass = css`
20
24
  min-height: ${layout.menuItemHeight}px;
@@ -26,6 +30,10 @@ const groupHeadingClass = css`
26
30
  white-space: nowrap;
27
31
  overflow: hidden;
28
32
  text-overflow: ellipsis;
33
+ ${mobileRootSelector} & {
34
+ min-height: ${layout.mobileMenuItemHeight}px;
35
+ font-size: ${textStyles.regular.fontSize}px;
36
+ }
29
37
  `;
30
38
  const groupHeadingContentClass = css`
31
39
  white-space: nowrap;