@fibery/ui-kit 1.21.0 → 1.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fibery/ui-kit",
3
- "version": "1.21.0",
3
+ "version": "1.22.0",
4
4
  "main": "index.ts",
5
5
  "private": false,
6
6
  "files": [
@@ -21,6 +21,7 @@
21
21
  "src/error-alert.tsx",
22
22
  "src/pallete.ts",
23
23
  "src/tooltip.tsx",
24
+ "src/tooltip-if-overflown.tsx",
24
25
  "src/back-button.tsx",
25
26
  "src/button",
26
27
  "src/emoji-picker/**/*.ts*",
@@ -68,9 +69,9 @@
68
69
  "react-windowed-select": "5.0.0",
69
70
  "screenfull": "6.0.1",
70
71
  "ua-parser-js": "0.7.24",
71
- "@fibery/emoji-data": "2.2.1",
72
72
  "@fibery/react": "1.3.0",
73
- "@fibery/helpers": "1.2.0"
73
+ "@fibery/helpers": "1.2.0",
74
+ "@fibery/emoji-data": "2.2.1"
74
75
  },
75
76
  "peerDependencies": {
76
77
  "react": "^18.2.0",
@@ -103,8 +104,8 @@
103
104
  "svgo": "2.8.0",
104
105
  "typescript": "5.1.6",
105
106
  "unist-util-reduce": "0.2.2",
106
- "@fibery/babel-preset": "7.3.0",
107
- "@fibery/eslint-config": "8.5.0"
107
+ "@fibery/eslint-config": "8.5.0",
108
+ "@fibery/babel-preset": "7.3.0"
108
109
  },
109
110
  "jest": {
110
111
  "testEnvironment": "jsdom",
@@ -124,7 +125,24 @@
124
125
  "createInlineTheme\\.js$": "<rootDir>/src/__mocks__/createInlineTheme.js"
125
126
  },
126
127
  "transform": {
127
- "^.+\\.(js|jsx|ts|tsx)$": "babel-jest"
128
+ "^.+\\.(js|jsx|ts|tsx)$": [
129
+ "@swc/jest",
130
+ {
131
+ "jsc": {
132
+ "parser": {
133
+ "syntax": "typescript",
134
+ "tsx": true,
135
+ "decorators": true,
136
+ "dynamicImport": true
137
+ },
138
+ "transform": {
139
+ "react": {
140
+ "runtime": "automatic"
141
+ }
142
+ }
143
+ }
144
+ }
145
+ ]
128
146
  },
129
147
  "setupFilesAfterEnv": [
130
148
  "<rootDir>/config/jest/testFrameworkSetup.js"
@@ -12,6 +12,8 @@ import {
12
12
  redDark,
13
13
  slate,
14
14
  slateDark,
15
+ teal,
16
+ tealDark,
15
17
  whiteA,
16
18
  yellow,
17
19
  yellowDark,
@@ -376,6 +378,12 @@ export const themeColors = {
376
378
  relationViewBgColor: [slate.slate2, slateDark.slate3],
377
379
  unitBg: [slate.slate3, slateDark.slate6],
378
380
  unitBgHover: [slate.slate4, slateDark.slate7],
381
+ colorBgBadgeSuccess: [teal.teal5, tealDark.teal5],
382
+ colorBgBadgeWarning: [yellow.yellow5, yellowDark.yellow5],
383
+ colorBgBadgeError: [red.red5, redDark.red5],
384
+ colorTextBadgeSuccess: [teal.teal11, tealDark.teal11],
385
+ colorTextBadgeWarning: [yellow.yellow11, yellowDark.yellow11],
386
+ colorTextBadgeError: [red.red11, redDark.red11],
379
387
  badgeBgColor: [getOpacities(slate.slate10).opacity20, getOpacities(slate.slate10).opacity20],
380
388
  panelBadgeBgColor: [getOpacities(slate.slate10).opacity20, getOpacities(slate.slate10).opacity20],
381
389
  tooltipBgColor: [slateDark.slate5, slateDark.slate6],
@@ -449,6 +457,8 @@ export const themeColors = {
449
457
  progressText: [slate.slate11, slate.slate11],
450
458
  progressFillBg: [slate.slate11, slate.slate11],
451
459
  progressFillText: [whiteA.whiteA0, whiteA.whiteA0],
460
+ progressBarFill: [indigo.indigo9, indigoDark.indigo10],
461
+ progressBarBg: [getOpacities(indigo.indigo9).opacity25, getOpacities(indigoDark.indigo10).opacity25],
452
462
  searchFiltersBg: [slate.slate1, slateDark.slate2],
453
463
  colorPickerSwatchBorder: [blackA.blackA7, blackA.blackA7],
454
464
  richTextTableBorder: [blackA.blackA5, whiteA.whiteA7],
@@ -475,6 +485,7 @@ export const themeColors = {
475
485
  colorTextReactionsSelected: [indigo.indigo10, slateDark.slate12],
476
486
  colorBgToastDefault: [slate.slate1, slateDark.slate1],
477
487
  colorBgDbTabHover: [slate.slate3, slateDark.slate4],
488
+ colorBgDbTabFocus: [slate.slate6, slateDark.slate6],
478
489
  } as const;
479
490
 
480
491
  type ThemeDefs = typeof themeColors;
@@ -786,6 +797,7 @@ export const border = {
786
797
  export const lineHeight = {
787
798
  text: 1.6,
788
799
  regular: 1.5,
800
+ narrow: 1.4,
789
801
  heading: 1.25,
790
802
  nowrap: 1,
791
803
  } as const;
@@ -6,7 +6,7 @@ import {useEmojiDataStoreSelector} from "../stores/emoji-data-store";
6
6
 
7
7
  const emojiWrapperCss = css`
8
8
  color: ${themeVars.textColor};
9
- font-family: "Segoe UI Emoji", "Segoe UI Symbol", "Segoe UI", "Apple Color Emoji", "Twemoji Mozilla",
9
+ font-family: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Segoe UI", "Twemoji Mozilla",
10
10
  "Noto Color Emoji", "Android Emoji";
11
11
  line-height: 1;
12
12
  `;
@@ -3,7 +3,7 @@ import {space} from "../../design-system";
3
3
  import {contentHorizontalPadding} from "./layout";
4
4
 
5
5
  const pickerHeader = css`
6
- padding: ${space.s6}px ${contentHorizontalPadding}px;
6
+ padding: ${space.s12}px ${contentHorizontalPadding}px ${space.s6}px ${contentHorizontalPadding}px;
7
7
  position: relative;
8
8
  `;
9
9
  export const EmojiPickerHeader: React.FC<React.PropsWithChildren> = ({children}) => {
@@ -0,0 +1,8 @@
1
+
2
+ // This icon file is generated automatically.
3
+
4
+ import { IconDefinition } from '../types';
5
+
6
+ const Chat: IconDefinition = {"icon":{"type":"root","children":[{"type":"element","tagName":"svg","properties":{"viewBox":"0 0 20 20"},"children":[{"type":"element","tagName":"path","properties":{"fillRule":"evenodd","clipRule":"evenodd","d":"M8.854 3.524a.083.083 0 0 1 .06-.024h7.5a.083.083 0 0 1 .083.083v6.523l-1.22-1.22a.75.75 0 0 0-.53-.22H8.913a.083.083 0 0 1-.083-.083v-5c0-.022.009-.043.024-.059ZM8.914 2A1.583 1.583 0 0 0 7.33 3.583v5a1.583 1.583 0 0 0 1.583 1.584h5.523l2.28 2.28a.75.75 0 0 0 1.28-.53V3.583A1.583 1.583 0 0 0 16.414 2h-7.5Zm-5.39 6.854a.083.083 0 0 1 .06-.024H5.25a.75.75 0 0 0 0-1.5H3.583A1.583 1.583 0 0 0 2 8.913v8.334a.75.75 0 0 0 1.28.53l2.28-2.28h5.523a1.583 1.583 0 0 0 1.584-1.584v-1.666a.75.75 0 0 0-1.5 0v1.666a.083.083 0 0 1-.084.084H5.25a.75.75 0 0 0-.53.22L3.5 15.435V8.913c0-.022.009-.043.024-.059Z"},"children":[]}],"metadata":""}]},"name":"chat"};
7
+
8
+ export default Chat;
@@ -0,0 +1,8 @@
1
+
2
+ // This icon file is generated automatically.
3
+
4
+ import { IconDefinition } from '../types';
5
+
6
+ const ChatAi: IconDefinition = {"icon":{"type":"root","children":[{"type":"element","tagName":"svg","properties":{"viewBox":"0 0 20 20"},"children":[{"type":"element","tagName":"path","properties":{"d":"M11.263.181a.281.281 0 0 0-.526 0l-.57 1.498a.844.844 0 0 1-.488.488l-1.498.57a.281.281 0 0 0 0 .526l1.498.57a.844.844 0 0 1 .488.488l.57 1.498a.281.281 0 0 0 .526 0l.57-1.498a.844.844 0 0 1 .488-.488l1.498-.57a.281.281 0 0 0 0-.526l-1.498-.57a.844.844 0 0 1-.488-.488L11.263.18ZM17 4c.117 0 .221.072.263.181l.57 1.498a.844.844 0 0 0 .488.488l1.498.57a.281.281 0 0 1 0 .526l-1.498.57a.844.844 0 0 0-.488.488l-.57 1.498a.281.281 0 0 1-.526 0l-.57-1.498a.843.843 0 0 0-.488-.488l-1.498-.57a.281.281 0 0 1 0-.526l1.498-.57a.843.843 0 0 0 .488-.488l.57-1.498A.281.281 0 0 1 17 4Z"},"children":[]},{"type":"element","tagName":"path","properties":{"fillRule":"evenodd","clipRule":"evenodd","d":"M11.087 7.33a1.583 1.583 0 0 1 1.583 1.583v5a1.583 1.583 0 0 1-1.583 1.584H5.564l-2.28 2.28a.75.75 0 0 1-1.28-.53V8.913A1.583 1.583 0 0 1 3.586 7.33h7.5Zm.059 1.524a.083.083 0 0 0-.06-.024h-7.5a.083.083 0 0 0-.083.083v6.523l1.22-1.22a.75.75 0 0 1 .53-.22h5.834a.084.084 0 0 0 .083-.083v-5a.083.083 0 0 0-.024-.059Z"},"children":[]}],"metadata":""}]},"name":"chat-ai"};
7
+
8
+ export default ChatAi;
@@ -0,0 +1,8 @@
1
+
2
+ // This icon file is generated automatically.
3
+
4
+ import { IconDefinition } from '../types';
5
+
6
+ const Headphones: IconDefinition = {"icon":{"type":"root","children":[{"type":"element","tagName":"svg","properties":{"viewBox":"0 0 20 20"},"children":[{"type":"element","tagName":"path","properties":{"fillRule":"evenodd","clipRule":"evenodd","d":"M17.626 9.012C17.108 5.055 13.927 2 10 2 6.072 2 2.892 5.056 2.374 9.012c-.154.1-.293.224-.41.367a2.107 2.107 0 0 0-.464 1.335v1.822c0 .479.155.96.464 1.335.312.379.77.629 1.286.629h1.5c.19 0 .365-.071.497-.188C6.037 16.438 7.806 18 10 18s3.963-1.562 4.753-3.688a.75.75 0 0 0 .497.188h1.5c.516 0 .974-.25 1.286-.629.309-.375.464-.856.464-1.335v-1.822c0-.479-.155-.96-.464-1.335a1.795 1.795 0 0 0-.41-.367Zm-1.459-.262C15.566 5.638 13 3.35 10 3.35s-5.566 2.288-6.167 5.4h.916a.751.751 0 0 1 .595.292C6.176 7.042 7.892 5.6 10 5.6c2.108 0 3.824 1.443 4.656 3.442a.75.75 0 0 1 .595-.292h.916ZM10 7c1.974 0 3.8 2.011 3.8 4.8 0 2.789-1.826 4.8-3.8 4.8-1.974 0-3.8-2.011-3.8-4.8C6.2 9.011 8.026 7 10 7Z"},"children":[]}],"metadata":""}]},"name":"headphones"};
7
+
8
+ export default Headphones;
@@ -0,0 +1,8 @@
1
+
2
+ // This icon file is generated automatically.
3
+
4
+ import { IconDefinition } from '../types';
5
+
6
+ const HeadphonesAi: IconDefinition = {"icon":{"type":"root","children":[{"type":"element","tagName":"svg","properties":{"viewBox":"0 0 20 20"},"children":[{"type":"element","tagName":"path","properties":{"d":"m17.833 8.679-.712-1.652C15.973 4.065 13.24 2 10 2 6.759 2 4.027 4.065 2.88 7.027L2.166 8.68a.844.844 0 0 1-.488.488l-1.498.57a.281.281 0 0 0 0 .526l1.498.57a.844.844 0 0 1 .488.488l.57 1.498a.281.281 0 0 0 .526 0l.57-1.498a.844.844 0 0 1 .488-.488l1.498-.57a.281.281 0 0 0 0-.526l-1.498-.57c-.217-.083-.512-.299-.479-.465C4.461 5.614 7.015 3.35 10 3.35c2.985 0 5.54 2.264 6.157 5.352.038.187-.261.382-.478.465l-1.498.57a.281.281 0 0 0 0 .526l1.498.57a.844.844 0 0 1 .488.488l.57 1.498a.281.281 0 0 0 .526 0l.57-1.498a.844.844 0 0 1 .488-.488l1.498-.57a.281.281 0 0 0 0-.526l-1.498-.57a.844.844 0 0 1-.488-.488Z"},"children":[]},{"type":"element","tagName":"path","properties":{"fillRule":"evenodd","clipRule":"evenodd","d":"M10.319 7.265a.562.562 0 0 0-.844.262l-1.015 2.64c-.176.457-.232.592-.31.702a1.194 1.194 0 0 1-.28.281c-.11.077-.245.133-.702.31l-2.64 1.015a.562.562 0 0 0 0 1.05l2.64 1.015c.457.176.592.232.701.31.11.078.204.173.281.281.078.11.134.244.31.701l1.015 2.64a.562.562 0 0 0 1.05 0l1.016-2.64c.175-.457.232-.592.31-.701.077-.109.172-.204.28-.281.11-.078.245-.134.701-.31l2.64-1.015a.561.561 0 0 0 0-1.05l-2.64-1.016c-.456-.175-.591-.232-.7-.31a1.198 1.198 0 0 1-.282-.28c-.077-.11-.134-.245-.31-.701l-1.015-2.64a.562.562 0 0 0-.206-.263Zm-.833 3.37L10 9.294l.514 1.335c.143.372.25.65.42.89.15.211.335.396.546.546.239.17.516.276.886.42l1.339.514-1.336.514c-.372.142-.65.249-.889.42-.211.15-.396.334-.546.545-.169.238-.275.512-.416.88L10 16.703l-.514-1.335c-.142-.373-.249-.65-.42-.89a2.342 2.342 0 0 0-.545-.546c-.24-.17-.517-.277-.889-.42L6.295 13l1.34-.515c.37-.143.647-.25.886-.419.211-.15.396-.335.546-.546.17-.238.276-.515.419-.886Z"},"children":[]}],"metadata":""}]},"name":"headphones-ai"};
7
+
8
+ export default HeadphonesAi;
@@ -41,6 +41,8 @@ export { default as BellRinging } from './BellRinging';
41
41
  export { default as BellX } from './BellX';
42
42
  export { default as Bolt } from './Bolt';
43
43
  export { default as Cards } from './Cards';
44
+ export { default as ChatAi } from './ChatAi';
45
+ export { default as Chat } from './Chat';
44
46
  export { default as CheckCircle } from './CheckCircle';
45
47
  export { default as Checked } from './Checked';
46
48
  export { default as ClearValue } from './ClearValue';
@@ -91,6 +93,8 @@ export { default as GlobeSimple } from './GlobeSimple';
91
93
  export { default as Globe } from './Globe';
92
94
  export { default as GoogleDrive } from './GoogleDrive';
93
95
  export { default as GoogleMap } from './GoogleMap';
96
+ export { default as HeadphonesAi } from './HeadphonesAi';
97
+ export { default as Headphones } from './Headphones';
94
98
  export { default as Hide } from './Hide';
95
99
  export { default as Hint } from './Hint';
96
100
  export { default as Home } from './Home';
@@ -0,0 +1,12 @@
1
+ // This icon file is generated automatically.
2
+
3
+ import ChatSvg from '../ast/Chat';
4
+ import { Icon } from '../Icon';
5
+ import { IconBaseProps } from '../types';
6
+
7
+ const Chat = (
8
+ props: IconBaseProps,
9
+ ): JSX.Element => <Icon {...props} icon={ChatSvg} />;
10
+
11
+ Chat.displayName = 'Chat';
12
+ export default Chat;
@@ -0,0 +1,12 @@
1
+ // This icon file is generated automatically.
2
+
3
+ import ChatAiSvg from '../ast/ChatAi';
4
+ import { Icon } from '../Icon';
5
+ import { IconBaseProps } from '../types';
6
+
7
+ const ChatAi = (
8
+ props: IconBaseProps,
9
+ ): JSX.Element => <Icon {...props} icon={ChatAiSvg} />;
10
+
11
+ ChatAi.displayName = 'ChatAi';
12
+ export default ChatAi;
@@ -0,0 +1,12 @@
1
+ // This icon file is generated automatically.
2
+
3
+ import HeadphonesSvg from '../ast/Headphones';
4
+ import { Icon } from '../Icon';
5
+ import { IconBaseProps } from '../types';
6
+
7
+ const Headphones = (
8
+ props: IconBaseProps,
9
+ ): JSX.Element => <Icon {...props} icon={HeadphonesSvg} />;
10
+
11
+ Headphones.displayName = 'Headphones';
12
+ export default Headphones;
@@ -0,0 +1,12 @@
1
+ // This icon file is generated automatically.
2
+
3
+ import HeadphonesAiSvg from '../ast/HeadphonesAi';
4
+ import { Icon } from '../Icon';
5
+ import { IconBaseProps } from '../types';
6
+
7
+ const HeadphonesAi = (
8
+ props: IconBaseProps,
9
+ ): JSX.Element => <Icon {...props} icon={HeadphonesAiSvg} />;
10
+
11
+ HeadphonesAi.displayName = 'HeadphonesAi';
12
+ export default HeadphonesAi;
@@ -41,6 +41,8 @@ export { default as BellRinging } from './BellRinging';
41
41
  export { default as BellX } from './BellX';
42
42
  export { default as Bolt } from './Bolt';
43
43
  export { default as Cards } from './Cards';
44
+ export { default as ChatAi } from './ChatAi';
45
+ export { default as Chat } from './Chat';
44
46
  export { default as CheckCircle } from './CheckCircle';
45
47
  export { default as Checked } from './Checked';
46
48
  export { default as ClearValue } from './ClearValue';
@@ -91,6 +93,8 @@ export { default as GlobeSimple } from './GlobeSimple';
91
93
  export { default as Globe } from './Globe';
92
94
  export { default as GoogleDrive } from './GoogleDrive';
93
95
  export { default as GoogleMap } from './GoogleMap';
96
+ export { default as HeadphonesAi } from './HeadphonesAi';
97
+ export { default as Headphones } from './Headphones';
94
98
  export { default as Hide } from './Hide';
95
99
  export { default as Hint } from './Hint';
96
100
  export { default as Home } from './Home';
@@ -22,7 +22,7 @@ export const IntegrationCompactInfoButton: React.FC<{
22
22
  name: string;
23
23
  icon: React.ReactNode;
24
24
  title?: React.ReactNode;
25
- error?: Error;
25
+ error?: Error | null;
26
26
  loading?: boolean;
27
27
  onClick?: () => void;
28
28
  }> = ({onClick, loading, icon, name, title, error}) => {
@@ -39,7 +39,7 @@ export const IntegrationCompactInfoButton: React.FC<{
39
39
  const className = cx(buttonCss, loading && loadingCss);
40
40
 
41
41
  const button = onClick ? (
42
- <ButtonBase dangerous={Boolean(error)} borderless className={className}>
42
+ <ButtonBase dangerous={Boolean(error)} borderless className={className} onClick={onClick}>
43
43
  {iconNode}
44
44
  {name}
45
45
  </ButtonBase>
@@ -0,0 +1,220 @@
1
+ import {ForwardedRef, forwardRef, HTMLAttributes, PropsWithChildren, ReactNode} from "react";
2
+ import {border, space, textStyles, themeVars} from "../design-system";
3
+ import {css, cx} from "@linaria/core";
4
+
5
+ export interface ListRowProps extends HTMLAttributes<HTMLDivElement> {
6
+ darker?: boolean;
7
+ className?: string;
8
+ selected?: boolean;
9
+ done?: boolean;
10
+ focused?: boolean;
11
+ hovered?: boolean;
12
+ }
13
+ export const NATUAL_HOVER_CLASS = "natural-hover";
14
+ export const NATUAL_FOCUS_CLASS = "natural-focus";
15
+ const NATURAL_HOVER = `&.${NATUAL_HOVER_CLASS}:hover`;
16
+ const NATURAL_FOCUS = `&.${NATUAL_FOCUS_CLASS}:focus`;
17
+
18
+ export const listRowSurfaceStylesBase = css`
19
+ max-width: 100%;
20
+ flex-basis: 100%;
21
+ display: flex;
22
+ cursor: default;
23
+ align-items: center;
24
+ align-content: center;
25
+ padding: 0 ${space.s4}px;
26
+ column-gap: ${space.s4}px;
27
+ ${{...textStyles.regular}};
28
+ color: ${themeVars.textColor};
29
+ border-radius: ${border.radius6}px;
30
+ transition: background-color;
31
+ ${NATURAL_HOVER},
32
+ &.hover {
33
+ cursor: pointer;
34
+ }
35
+ &.done {
36
+ cursor: not-allowed;
37
+ color: ${themeVars.colorTextListItemGeneralDisabled};
38
+ > * {
39
+ opacity: ${themeVars.opacityListItemGeneralDisabled};
40
+ }
41
+ }
42
+ &.selected {
43
+ cursor: default;
44
+ }
45
+ `;
46
+ /* Use it on white background like select option and etc */
47
+ export const listRowSurfaceStatesDefault = css`
48
+ background-color: ${themeVars.colorBgListItemGeneral};
49
+ ${NATURAL_HOVER},
50
+ &.hover {
51
+ cursor: pointer;
52
+ background-color: ${themeVars.colorBgListItemGeneralHover};
53
+ }
54
+
55
+ ${NATURAL_FOCUS}, &.focus {
56
+ background-color: ${themeVars.colorBgListItemGeneralFocus};
57
+ ${NATURAL_HOVER},
58
+ &.hover {
59
+ background-color: ${themeVars.colorBgListItemGeneralFocus};
60
+ }
61
+ }
62
+ &.done {
63
+ cursor: not-allowed;
64
+ color: ${themeVars.colorTextListItemGeneralDisabled};
65
+ background-color: ${themeVars.colorBgListItemGeneralDisabled};
66
+ ${NATURAL_HOVER},
67
+ &.hover,
68
+ ${NATURAL_FOCUS},
69
+ &.focus {
70
+ background-color: ${themeVars.colorBgListItemGeneralDisabled};
71
+ }
72
+ > * {
73
+ opacity: ${themeVars.opacityListItemGeneralDisabled};
74
+ }
75
+ }
76
+ &.selected {
77
+ cursor: default;
78
+ background-color: ${themeVars.colorBgListItemGeneralSelected};
79
+ ${NATURAL_HOVER},
80
+ &.hover {
81
+ background-color: ${themeVars.colorBgListItemGeneralSelectedHover};
82
+ }
83
+ ${NATURAL_FOCUS},
84
+ &.focus {
85
+ background-color: ${themeVars.colorBgListItemGeneralSelectedFocus};
86
+ }
87
+ }
88
+ `;
89
+ /* Use it on dimmed background like left menu or automations list */
90
+ export const listRowSurfaceStatesDark = css`
91
+ background-color: ${themeVars.colorBgMenuItem};
92
+ ${NATURAL_HOVER},
93
+ &.hover {
94
+ cursor: pointer;
95
+ background-color: ${themeVars.colorBgMenuItemHover};
96
+ }
97
+
98
+ ${NATURAL_FOCUS}, &.focus {
99
+ background-color: ${themeVars.colorBgMenuItemFocus};
100
+ ${NATURAL_HOVER},
101
+ &.hover {
102
+ background-color: ${themeVars.colorBgMenuItemFocus};
103
+ }
104
+ }
105
+ &.done {
106
+ cursor: not-allowed;
107
+ color: ${themeVars.colorTextListItemGeneralDisabled};
108
+ background-color: ${themeVars.colorBgListItemGeneralDisabled};
109
+ ${NATURAL_HOVER},
110
+ &.hover,
111
+ ${NATURAL_FOCUS},
112
+ &.focus {
113
+ background-color: ${themeVars.colorBgListItemGeneralDisabled};
114
+ }
115
+ > * {
116
+ opacity: ${themeVars.opacityListItemGeneralDisabled};
117
+ }
118
+ }
119
+ &.selected {
120
+ cursor: default;
121
+ background-color: ${themeVars.colorBgMenuItemSelected};
122
+ ${NATURAL_HOVER},
123
+ &.hover {
124
+ background-color: ${themeVars.colorBgMenuItemSelectedHover};
125
+ cursor: default;
126
+ }
127
+ ${NATURAL_FOCUS},
128
+ &.focus {
129
+ background-color: ${themeVars.colorBgMenuItemSelectedFocused};
130
+ }
131
+ }
132
+ `;
133
+
134
+ /**
135
+ * This should be responsible for all flat list items existing in UI in Fibery. Options, action menus and etc.
136
+ * You can enforce it's state with props focused, hovered, selected, done (use it for disabled too)
137
+ */
138
+
139
+ export const ListRowSurface = forwardRef<HTMLDivElement, ListRowProps>(
140
+ ({selected, focused, darker, hovered, done, className, children, ...rest}, ref) => {
141
+ return (
142
+ <div
143
+ {...rest}
144
+ className={cx(
145
+ listRowSurfaceStylesBase,
146
+ darker ? listRowSurfaceStatesDark : listRowSurfaceStatesDefault,
147
+ className,
148
+ hovered === undefined && NATUAL_HOVER_CLASS,
149
+ focused === undefined && NATUAL_FOCUS_CLASS,
150
+ focused && "focus",
151
+ hovered && "hover",
152
+ selected && "selected",
153
+ done && "done"
154
+ )}
155
+ ref={ref}
156
+ >
157
+ {children}
158
+ </div>
159
+ );
160
+ }
161
+ );
162
+
163
+ export interface ListRowContentProps extends PropsWithChildren<HTMLAttributes<HTMLDivElement>> {
164
+ rootClassName?: string;
165
+ before?: ReactNode;
166
+ after?: ReactNode;
167
+ contentRef?: ForwardedRef<HTMLDivElement>;
168
+ contentClassName?: string;
169
+ beforeClassName?: string;
170
+ afterClassName?: string;
171
+ }
172
+
173
+ const ListRowContentRoot = css`
174
+ flex-basis: 100%;
175
+ display: flex;
176
+ justify-content: stretch;
177
+ align-items: center;
178
+ overflow: hidden;
179
+ gap: ${space.s4}px;
180
+ `;
181
+ const ListRowLayoutSegment = css`
182
+ display: flex; // never new line
183
+ align-items: center;
184
+ `;
185
+ const ListRowContentSegment = css`
186
+ ${{...textStyles.regular}}
187
+ overflow: hidden;
188
+ text-overflow: ellipsis;
189
+ white-space: nowrap;
190
+ min-width: 0; // allows ellipsis in flex contexts :)
191
+ max-width: 100%;
192
+ width: 100%;
193
+ `;
194
+ export const ListRowContent = forwardRef<HTMLDivElement, ListRowContentProps>(
195
+ (
196
+ {
197
+ before,
198
+ after,
199
+ className,
200
+ contentRef,
201
+ rootClassName,
202
+ afterClassName,
203
+ beforeClassName,
204
+ contentClassName,
205
+ children,
206
+ ...rest
207
+ },
208
+ ref
209
+ ) => {
210
+ return (
211
+ <div {...rest} ref={ref} className={cx(ListRowContentRoot, rootClassName, className)}>
212
+ {before ? <div className={cx(ListRowLayoutSegment, beforeClassName)}>{before}</div> : null}
213
+ <div ref={contentRef} className={cx(ListRowContentSegment, contentClassName)}>
214
+ {children}
215
+ </div>
216
+ {after ? <div className={cx(ListRowLayoutSegment, afterClassName)}>{after}</div> : null}
217
+ </div>
218
+ );
219
+ }
220
+ );
@@ -3,10 +3,10 @@ import {components} from "react-windowed-select";
3
3
  import {GroupBase} from "../index";
4
4
  import {css} from "@linaria/core";
5
5
  import {layout, space} from "../../design-system";
6
- import {ListItem} from "../../lists/list-item";
6
+ import {ListRowContent, ListRowSurface} from "../../lists/list-row-surface";
7
7
  import cn from "classnames";
8
- import {Children, ReactNode, useCallback, useMemo, useRef, useState} from "react";
9
- import {Tooltip} from "antd";
8
+ import {Children, ReactNode, useMemo} from "react";
9
+ import {TooltipIfOverflown} from "../../tooltip-if-overflown";
10
10
 
11
11
  export const OptionNulledVitualizedStyles = {
12
12
  height: layout.menuItemHeight,
@@ -30,56 +30,12 @@ const OptionRootClass = css`
30
30
  cursor: pointer;
31
31
  max-width: 100%;
32
32
  `;
33
-
34
- const optionWithTextContent = css`
35
- max-width: 100%;
36
- padding: 0 ${space.s4}px;
37
- `;
38
-
39
- const optionTextOverflowWrapper = css`
40
- max-width: 100%;
41
- flex-basis: 100%;
42
- overflow: hidden;
43
- text-overflow: ellipsis;
44
- white-space: nowrap;
45
- padding: 0 ${space.s4}px;
33
+ const OptionWithTextContent = css`
34
+ padding-left: 0 ${space.s6}px;
46
35
  `;
47
-
48
36
  function isOptionContentIsPureText(children: ReactNode) {
49
37
  return !Children.toArray(children).find((child) => !(typeof child === "string" || typeof child === "number"));
50
38
  }
51
-
52
- type OptionTextContentWithTooltipProps = {
53
- children: string | number;
54
- isFocused?: boolean;
55
- };
56
-
57
- function OptionTextContentWithTooltip({children, isFocused}: OptionTextContentWithTooltipProps) {
58
- const [isTooltipActive, setTooltipActive] = useState<boolean>(false);
59
- const textWrapperRef = useRef<HTMLDivElement>(null);
60
- const textContentRef = useRef<HTMLSpanElement>(null);
61
- const onOptionMouseEnter = useCallback(() => {
62
- setTimeout(() => {
63
- if (!textContentRef.current || !textWrapperRef.current) {
64
- return;
65
- }
66
- isFocused && setTooltipActive(textContentRef.current?.offsetWidth > textWrapperRef.current?.offsetWidth);
67
- }, 300);
68
- }, [textContentRef, textWrapperRef, isFocused]);
69
- return (
70
- <div
71
- className={optionWithTextContent}
72
- onMouseEnter={onOptionMouseEnter}
73
- onMouseLeave={() => setTooltipActive(false)}
74
- >
75
- <Tooltip title={children} mouseEnterDelay={300} placement={"right"} open={isTooltipActive}>
76
- <div ref={textWrapperRef} className={optionTextOverflowWrapper}>
77
- <span ref={textContentRef}>{children}</span>
78
- </div>
79
- </Tooltip>
80
- </div>
81
- );
82
- }
83
39
  /**
84
40
  * Attention! Do not use this option for potentially long lists inside React Select
85
41
  * Why is this option slow? Read here https://the.fibery.io/SoftDev/bug/Select-option-behaviour-is-incorrect-9413
@@ -93,13 +49,33 @@ export function OptionSlow<
93
49
  const isPureText = useMemo(() => isOptionContentIsPureText(children), [children]);
94
50
  return (
95
51
  <components.Option {...rest} className={cn(rest.className, OptionRootClass)}>
96
- <ListItem focused={isFocused} selected={isSelected} hovered={false} done={isDisabled}>
97
- {isPureText ? (
98
- <OptionTextContentWithTooltip isFocused={isFocused}>{children as string}</OptionTextContentWithTooltip>
99
- ) : (
100
- children
101
- )}
102
- </ListItem>
52
+ {isPureText ? (
53
+ <ListRowSurface
54
+ focused={isFocused}
55
+ selected={isSelected}
56
+ hovered={rest.innerProps.onMouseOver ? undefined : false} //if onMouseOver is not passed (fast option used) then enabling natural hover by passing undefined so we not forcing non-hover state
57
+ done={isDisabled}
58
+ >
59
+ <TooltipIfOverflown title={children} side={"right"} sideOffset={space.s6} visible={isFocused}>
60
+ {({setNode}) => {
61
+ return (
62
+ <ListRowContent contentRef={setNode} contentClassName={OptionWithTextContent}>
63
+ {children}
64
+ </ListRowContent>
65
+ );
66
+ }}
67
+ </TooltipIfOverflown>
68
+ </ListRowSurface>
69
+ ) : (
70
+ <ListRowSurface
71
+ focused={isFocused}
72
+ selected={isSelected}
73
+ hovered={rest.innerProps.onMouseOver ? undefined : false} //if onMouseOver is not passed (fast option used) then enabling natural hover by passing undefined so we not forcing non-hover state
74
+ done={isDisabled}
75
+ >
76
+ <ListRowContent>{children}</ListRowContent>
77
+ </ListRowSurface>
78
+ )}
103
79
  </components.Option>
104
80
  );
105
81
  }
@@ -113,7 +89,8 @@ export function Option<Option, IsMulti extends boolean = false, Group extends Gr
113
89
  ...rest
114
90
  }: OptionProps<Option, IsMulti, Group>) {
115
91
  /**
116
- * This makes this option component fast
92
+ * This makes this option component fast.
93
+ * Excluding onMouseOver, onMouseMove makes ReactSelect not rerender the whole list on option hover
117
94
  */
118
95
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
119
96
  const {onMouseOver, onMouseMove, ...truncatedInnerProps} = rest.innerProps;
@@ -0,0 +1,26 @@
1
+ import {useResizeObserver} from "@fibery/react/src/use-resize-observer";
2
+ import {useState} from "react";
3
+ import {Tooltip, TooltipProps} from "./tooltip";
4
+
5
+ type SetNode = (element: HTMLElement | null) => void;
6
+
7
+ export const TooltipIfOverflown: React.FC<
8
+ Omit<TooltipProps, "children"> & {children: (args: {setNode: SetNode}) => JSX.Element | null}
9
+ > = ({children, ...tooltipProps}) => {
10
+ const [truncated, setTruncated] = useState(false);
11
+ const [node, setNode] = useState<HTMLElement | null>(null);
12
+
13
+ useResizeObserver(node, (entry) => {
14
+ const target = entry.target;
15
+ const hasLineClamp = getComputedStyle(target).webkitLineClamp !== "none";
16
+ const truncated = hasLineClamp
17
+ ? target.scrollHeight > target.clientHeight
18
+ : target.scrollWidth > target.clientWidth;
19
+ setTruncated(truncated);
20
+ });
21
+ return (
22
+ <Tooltip {...tooltipProps} disabled={!truncated}>
23
+ {children({setNode})}
24
+ </Tooltip>
25
+ );
26
+ };
@@ -1,8 +0,0 @@
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()});
@@ -1,139 +0,0 @@
1
- import {styled} from "@linaria/react";
2
- import {forwardRef, HTMLAttributes, PropsWithChildren, ReactNode} from "react";
3
- import {border, space, textStyles, themeVars} from "../design-system";
4
- import {css, cx} from "@linaria/core";
5
-
6
- export interface ListItemGeneralProps extends HTMLAttributes<HTMLDivElement> {
7
- className?: string;
8
- selected?: boolean;
9
- done?: boolean;
10
- focused?: boolean;
11
- hovered?: boolean;
12
- }
13
- const NATUAL_HOVER_CLASS = "natural-hover";
14
- const NATUAL_FOCUS_CLASS = "natural-focus";
15
- const NATURAL_HOVER = `&.${NATUAL_HOVER_CLASS}:hover`;
16
- const NATURAL_FOCUS = `&.${NATUAL_FOCUS_CLASS}:focus`;
17
-
18
- const ListItemGeneralRoot = styled.div`
19
- max-width: 100%;
20
- flex-basis: 100%;
21
- display: flex;
22
- cursor: default;
23
- align-items: center;
24
- align-content: center;
25
- padding: 0 ${space.s4}px;
26
- column-gap: ${space.s4}px;
27
- ${{...textStyles.regular}};
28
- color: ${themeVars.textColor};
29
- border-radius: ${border.radius6}px;
30
- background-color: ${themeVars.colorBgListItemGeneral};
31
- transition: background-color;
32
- ${NATURAL_HOVER},
33
- &.hover {
34
- cursor: pointer;
35
- background-color: ${themeVars.colorBgListItemGeneralHover};
36
- }
37
-
38
- ${NATURAL_FOCUS}, &.focus {
39
- background-color: ${themeVars.colorBgListItemGeneralFocus};
40
- ${NATURAL_HOVER},
41
- &.hover {
42
- background-color: ${themeVars.colorBgListItemGeneralFocus};
43
- }
44
- }
45
- &.done {
46
- cursor: not-allowed;
47
- color: ${themeVars.colorTextListItemGeneralDisabled};
48
- background-color: ${themeVars.colorBgListItemGeneralDisabled};
49
- ${NATURAL_HOVER},
50
- &.hover,
51
- ${NATURAL_FOCUS},
52
- &.focus {
53
- background-color: ${themeVars.colorBgListItemGeneralDisabled};
54
- }
55
- > * {
56
- opacity: ${themeVars.opacityListItemGeneralDisabled};
57
- }
58
- }
59
- &.selected {
60
- cursor: default;
61
- background-color: ${themeVars.colorBgListItemGeneralSelected};
62
- ${NATURAL_HOVER},
63
- &.hover {
64
- background-color: ${themeVars.colorBgListItemGeneralSelectedHover};
65
- }
66
- ${NATURAL_FOCUS},
67
- &.focus {
68
- background-color: ${themeVars.colorBgListItemGeneralSelectedFocus};
69
- }
70
- }
71
- `;
72
-
73
- /**
74
- * This should be responsible for all flat list items existing in UI in Fibery. Options, action menus and etc.
75
- * You can enforce it's state with props focused, hovered, selected, done (use it for disabled too)
76
- */
77
- export const ListItem = forwardRef<HTMLDivElement, ListItemGeneralProps>(
78
- ({selected, focused, hovered, done, className, children, ...rest}, ref) => {
79
- return (
80
- <ListItemGeneralRoot
81
- {...rest}
82
- className={cx(
83
- className,
84
- hovered === undefined && NATUAL_HOVER_CLASS,
85
- focused === undefined && NATUAL_FOCUS_CLASS,
86
- focused && "focus",
87
- hovered && "hover",
88
- selected && "selected",
89
- done && "done"
90
- )}
91
- ref={ref}
92
- >
93
- {children}
94
- </ListItemGeneralRoot>
95
- );
96
- }
97
- );
98
-
99
- export interface ListItemCompoundLayoutProps extends PropsWithChildren<HTMLAttributes<HTMLDivElement>> {
100
- className?: string;
101
- before?: ReactNode;
102
- after?: ReactNode;
103
- contentClassName?: string;
104
- beforeClassName?: string;
105
- afterClassName?: string;
106
- }
107
-
108
- const ListItemCompoundLayoutRoot = css`
109
- flex-basis: 100%;
110
- display: flex;
111
- justify-content: stretch;
112
- align-items: center;
113
- overflow: hidden;
114
- `;
115
- const ListItemCompoundLayoutContainerStyle = css`
116
- display: flex; // never new line
117
- align-items: center;
118
- `;
119
- const ListItemCompoundLayoutContentStyle = css`
120
- ${{...textStyles.regular}}
121
- overflow: hidden;
122
- text-overflow: ellipsis;
123
- padding: 0 0 0 ${space.s4}px;
124
- white-space: nowrap;
125
- min-width: 0; // allows ellipsis in flex contexts :)
126
- max-width: 100%;
127
- width: 100%;
128
- `;
129
- export const ListItemLayout = forwardRef<HTMLDivElement, ListItemCompoundLayoutProps>(
130
- ({before, after, className, afterClassName, beforeClassName, contentClassName, children, ...rest}, ref) => {
131
- return (
132
- <div {...rest} ref={ref} className={cx(className, ListItemCompoundLayoutRoot)}>
133
- {before ? <div className={cx(ListItemCompoundLayoutContainerStyle, beforeClassName)}>{before}</div> : null}
134
- <div className={cx(ListItemCompoundLayoutContentStyle, contentClassName)}>{children}</div>
135
- {after ? <div className={cx(ListItemCompoundLayoutContainerStyle, afterClassName)}>{after}</div> : null}
136
- </div>
137
- );
138
- }
139
- );