@fibery/ui-kit 1.18.0 → 1.20.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 (81) hide show
  1. package/package.json +9 -7
  2. package/src/a11y-color.ts +9 -6
  3. package/src/antd/styles.ts +6 -6
  4. package/src/back-button.tsx +1 -1
  5. package/src/{Button → button}/button.tsx +5 -5
  6. package/src/button.tsx +1 -1
  7. package/src/design-system.ts +83 -34
  8. package/src/emoji-picker/emoji-picker-content-with-color.tsx +4 -4
  9. package/src/emoji-picker/emoji-picker.tsx +2 -2
  10. package/src/emoji-picker/icon-emoji-picker.tsx +1 -1
  11. package/src/emoji-picker/primitives/category.tsx +3 -3
  12. package/src/emoji-picker/primitives/emoji.tsx +26 -3
  13. package/src/emoji-picker/primitives/footer.tsx +1 -1
  14. package/src/emoji-picker/primitives/header.tsx +1 -1
  15. package/src/emoji-picker/primitives/layout.ts +2 -2
  16. package/src/emoji-picker/primitives/search.tsx +6 -6
  17. package/src/emoji-picker/primitives/skin-tone.tsx +1 -1
  18. package/src/error-alert.tsx +4 -4
  19. package/src/form-field-loader.tsx +3 -3
  20. package/src/icons/Icon.tsx +2 -2
  21. package/src/icons/ast/AlertTriangle.ts +8 -0
  22. package/src/icons/ast/BellRinging.ts +1 -1
  23. package/src/icons/ast/Clock.ts +8 -0
  24. package/src/icons/ast/ClockAlarm.ts +8 -0
  25. package/src/icons/ast/Copy.ts +1 -1
  26. package/src/icons/ast/DragBlockHandleSingle.ts +8 -0
  27. package/src/icons/ast/ExtensionComments.ts +1 -1
  28. package/src/icons/ast/Eye.ts +8 -0
  29. package/src/icons/ast/Monitor.ts +8 -0
  30. package/src/icons/ast/NetworkAdd.ts +8 -0
  31. package/src/icons/ast/RemovePeople.ts +8 -0
  32. package/src/icons/ast/RicheditorCommentCreate.ts +1 -1
  33. package/src/icons/ast/RicheditorMarkTextBackgroundColor.ts +8 -0
  34. package/src/icons/ast/Success.ts +8 -0
  35. package/src/icons/ast/WarningTriangle.ts +8 -0
  36. package/src/icons/ast/index.tsx +11 -1
  37. package/src/icons/react/AlertTriangle.tsx +12 -0
  38. package/src/icons/react/Clock.tsx +12 -0
  39. package/src/icons/react/ClockAlarm.tsx +12 -0
  40. package/src/icons/react/DragBlockHandleSingle.tsx +12 -0
  41. package/src/icons/react/Eye.tsx +12 -0
  42. package/src/icons/react/Monitor.tsx +12 -0
  43. package/src/icons/react/NetworkAdd.tsx +12 -0
  44. package/src/icons/react/RemovePeople.tsx +12 -0
  45. package/src/icons/react/RicheditorMarkTextBackgroundColor.tsx +12 -0
  46. package/src/icons/react/Success.tsx +12 -0
  47. package/src/icons/react/WarningTriangle.tsx +12 -0
  48. package/src/icons/react/index.tsx +11 -1
  49. package/src/loaders.tsx +10 -10
  50. package/src/loading-sausage.tsx +2 -2
  51. package/src/media-query-utils.ts +1 -1
  52. package/src/{Select → select}/custom-select-partials/clear-indicator.tsx +1 -1
  53. package/src/{Select → select}/custom-select-partials/group-heading.tsx +2 -2
  54. package/src/{Select → select}/custom-select-partials/menu.tsx +9 -1
  55. package/src/{Select → select}/custom-select-partials/no-option-message.tsx +1 -1
  56. package/src/select/custom-select-partials/option.tsx +122 -0
  57. package/src/{Select → select}/index.tsx +40 -5
  58. package/src/{Select → select}/select-in-popover.tsx +14 -14
  59. package/src/{Select → select}/styles.ts +15 -9
  60. package/src/toast/primitives.tsx +46 -28
  61. package/src/toast/toast-action.tsx +28 -0
  62. package/src/toast/toast-provider.tsx +15 -0
  63. package/src/toast/toast-queue.ts +117 -0
  64. package/src/toast/toast.tsx +121 -0
  65. package/src/toast/toaster.tsx +35 -0
  66. package/src/toast/utils/toastify-item-name.ts +6 -0
  67. package/src/tooltip.tsx +2 -2
  68. package/src/Select/custom-select-partials/option.tsx +0 -92
  69. package/src/icons/ast/RicheditorMarkHighlight.ts +0 -8
  70. package/src/icons/react/RicheditorMarkHighlight.tsx +0 -12
  71. package/src/toast/index.tsx +0 -47
  72. /package/src/{Button → button}/actions-button-compact.tsx +0 -0
  73. /package/src/{Button → button}/actions-button.tsx +0 -0
  74. /package/src/{Button → button}/add-button.tsx +0 -0
  75. /package/src/{Button → button}/back-button.tsx +0 -0
  76. /package/src/{Button → button}/button-base.tsx +0 -0
  77. /package/src/{Button → button}/button-group.tsx +0 -0
  78. /package/src/{Button → button}/icon-button.tsx +0 -0
  79. /package/src/{Select → select}/custom-select-partials/drop-down-indicator.tsx +0 -0
  80. /package/src/{Select → select}/select-control-settings-context.tsx +0 -0
  81. /package/src/{Select → select}/select-loader.tsx +0 -0
@@ -3,12 +3,18 @@ import {CSSObjectWithLabel, MenuProps} from "react-select";
3
3
  import cn from "classnames";
4
4
  import {components} from "react-windowed-select";
5
5
  import {css} from "@linaria/core";
6
- import {border, themeVars} from "../../design-system";
6
+ import {border, space, themeVars} from "../../design-system";
7
7
 
8
+ export const MenuListNulledStyles: CSSObjectWithLabel = {
9
+ paddingTop: "",
10
+ paddingBottom: "",
11
+ };
8
12
  export const MenuNulledStyles: CSSObjectWithLabel = {
9
13
  backgroundColor: "",
10
14
  borderRadius: "",
11
15
  boxShadow: "",
16
+ paddingTop: "",
17
+ paddingBottom: "",
12
18
  };
13
19
  const MenuRootClass = css`
14
20
  color: ${themeVars.disabledTextColor};
@@ -16,6 +22,8 @@ const MenuRootClass = css`
16
22
  background-color: ${themeVars.colorBgSelectMenu};
17
23
  box-shadow: ${themeVars.shadowSelectMenu};
18
24
  border-radius: ${border.radius6}px;
25
+ padding-top: ${space.s6}px;
26
+ padding-bottom: ${space.s6}px;
19
27
  overflow: hidden;
20
28
  `;
21
29
  export function Menu<Option, IsMulti extends boolean = false, Group extends GroupBase<Option> = GroupBase<Option>>(
@@ -3,7 +3,7 @@ import {css} from "@linaria/core";
3
3
  import {space} from "../../design-system";
4
4
 
5
5
  const NopOptionMessageClass = css`
6
- padding: ${space.l + 2}px ${space.m}px;
6
+ padding: ${space.s12 + 2}px ${space.s8}px;
7
7
  `;
8
8
  export const NoOptionsMessage = ({children}: {children: ReactNode}) => {
9
9
  return <div className={NopOptionMessageClass}>{children}</div>;
@@ -0,0 +1,122 @@
1
+ import {OptionProps} from "react-select";
2
+ import {components} from "react-windowed-select";
3
+ import {GroupBase} from "../index";
4
+ import {css} from "@linaria/core";
5
+ import {layout, space} from "../../design-system";
6
+ import {ListItem} from "../../lists/list-item";
7
+ import cn from "classnames";
8
+ import {Children, ReactNode, useCallback, useMemo, useRef, useState} from "react";
9
+ import {Tooltip} from "antd";
10
+
11
+ export const OptionNulledVitualizedStyles = {
12
+ height: layout.menuItemHeight,
13
+ };
14
+ export const OptionNulledStyles = {
15
+ minHeight: layout.menuItemHeight,
16
+ paddingLeft: "",
17
+ paddingRight: "",
18
+ paddingTop: "",
19
+ opacity: "",
20
+ fontFamily: "",
21
+ fontSize: "",
22
+ lineHeight: "",
23
+ color: "",
24
+ };
25
+ const OptionRootClass = css`
26
+ padding: 0 ${space.s6}px;
27
+ display: flex;
28
+ align-items: stretch;
29
+ justify-content: stretch;
30
+ cursor: pointer;
31
+ max-width: 100%;
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;
46
+ `;
47
+
48
+ function isOptionContentIsPureText(children: ReactNode) {
49
+ return !Children.toArray(children).find((child) => !(typeof child === "string" || typeof child === "number"));
50
+ }
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
+ /**
84
+ * Attention! Do not use this option for potentially long lists inside React Select
85
+ * Why is this option slow? Read here https://the.fibery.io/SoftDev/bug/Select-option-behaviour-is-incorrect-9413
86
+ * */
87
+ export function OptionSlow<
88
+ Option,
89
+ IsMulti extends boolean = false,
90
+ Group extends GroupBase<Option> = GroupBase<Option>
91
+ >({children, ...rest}: OptionProps<Option, IsMulti, Group>) {
92
+ const {isDisabled, isSelected, isFocused} = rest;
93
+ const isPureText = useMemo(() => isOptionContentIsPureText(children), [children]);
94
+ return (
95
+ <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>
103
+ </components.Option>
104
+ );
105
+ }
106
+
107
+ /**
108
+ * This option is fast when there is 100+ items but breaks natural focus on hover React Select behaviour. Do not use it for short lists
109
+ * The issue is here https://the.fibery.io/SoftDev/bug/Select-option-behaviour-is-incorrect-9413
110
+ */
111
+ export function Option<Option, IsMulti extends boolean = false, Group extends GroupBase<Option> = GroupBase<Option>>({
112
+ children,
113
+ ...rest
114
+ }: OptionProps<Option, IsMulti, Group>) {
115
+ /**
116
+ * This makes this option component fast
117
+ */
118
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
119
+ const {onMouseOver, onMouseMove, ...truncatedInnerProps} = rest.innerProps;
120
+ const props = Object.assign(rest, {innerProps: truncatedInnerProps});
121
+ return <OptionSlow {...props}>{children}</OptionSlow>;
122
+ }
@@ -22,12 +22,13 @@ import BaseCreatableSelect, {CreatableProps} from "react-select/creatable";
22
22
  import WindowedSelect, {components, WindowedMenuList} from "react-windowed-select";
23
23
  import {componentsStyles, singleLineComponentsStyle, virtualizedStyles, zIndexStyles} from "./styles";
24
24
  import {useSelectControlSettings} from "./select-control-settings-context";
25
- import {Option} from "./custom-select-partials/option";
25
+ import {Option, OptionSlow} from "./custom-select-partials/option";
26
26
  import {Menu} from "./custom-select-partials/menu";
27
27
  import {GroupHeading} from "./custom-select-partials/group-heading";
28
28
  import {DropdownIndicator} from "./custom-select-partials/drop-down-indicator";
29
29
  import {NoOptionsMessage} from "./custom-select-partials/no-option-message";
30
30
  import {ClearIndicator} from "./custom-select-partials/clear-indicator";
31
+ import {isObject} from "lodash";
31
32
 
32
33
  type GroupBase<T> = ReactSelectGroupBase<T> & {separator?: boolean};
33
34
 
@@ -69,7 +70,6 @@ export type SelectProps<
69
70
  isCollectionMode?: IsMulti;
70
71
  disabled?: boolean;
71
72
  };
72
-
73
73
  export function SingleRowSelect<
74
74
  Option = unknown,
75
75
  IsMulti extends boolean = boolean,
@@ -86,6 +86,23 @@ export function SingleRowSelect<
86
86
  );
87
87
  }
88
88
 
89
+ const FAST_OPTION_USAGE_THRESHOLD = 200;
90
+ function isGroup<Option, Group extends GroupBase<Option>>(optionOrGroup: Option | Group): optionOrGroup is Group {
91
+ return isObject(optionOrGroup) && "options" in optionOrGroup;
92
+ }
93
+ function countOptions<Option, Group extends GroupBase<Option>>(categorizedOptions?: OptionsOrGroups<Option, Group>) {
94
+ if (!categorizedOptions) {
95
+ return 0;
96
+ }
97
+ return categorizedOptions.reduce<number>((optionsAccumulator, categorizedOption) => {
98
+ if (isGroup<Option, Group>(categorizedOption)) {
99
+ return optionsAccumulator + categorizedOption.options.length;
100
+ } else {
101
+ return optionsAccumulator + 1;
102
+ }
103
+ }, 0);
104
+ }
105
+
89
106
  export const Select = forwardRef(function Select<
90
107
  Option = unknown,
91
108
  IsMulti extends boolean = boolean,
@@ -108,6 +125,9 @@ export const Select = forwardRef(function Select<
108
125
  ) {
109
126
  const {getPopupContainerElement, zIndex} = useSelectControlSettings();
110
127
  const [menuOpenState, setMenuOpenState] = useState(rest.defaultMenuIsOpen);
128
+ const optionsCount = useMemo(() => {
129
+ return countOptions<Option, Group>(rest.options);
130
+ }, [rest.options]);
111
131
  const onMenuOpenWrapped = useCallback(() => {
112
132
  setMenuOpenState(true);
113
133
  onMenuOpen && onMenuOpen();
@@ -120,7 +140,7 @@ export const Select = forwardRef(function Select<
120
140
  (e) => {
121
141
  switch (e.key) {
122
142
  case "Escape":
123
- // we cannot rely on "menuIsOpen" prop here, as user may not pass this prop and rely on internal component behavior instead.
143
+ // we can't rely on "menuIsOpen" prop here, as user may not pass this prop and rely on internal component behavior instead.
124
144
  if (menuOpenState) {
125
145
  e.stopPropagation();
126
146
  }
@@ -176,7 +196,13 @@ export const Select = forwardRef(function Select<
176
196
  Menu: Menu,
177
197
  DropdownIndicator,
178
198
  ClearIndicator,
179
- Option,
199
+ /*
200
+ Fast Options break option focus on hover behaviour but help with slow React Select performance with large options list.
201
+ This isolates bug for big lists only
202
+ Read Option definition for more info
203
+ bug here: https://the.fibery.io/SoftDev/bug/Select-option-behaviour-is-incorrect-9413
204
+ */
205
+ Option: optionsCount > FAST_OPTION_USAGE_THRESHOLD ? Option : OptionSlow,
180
206
  MultiValueRemove: ClearIndicator,
181
207
  NoOptionsMessage,
182
208
  GroupHeading,
@@ -222,6 +248,9 @@ export function CreatableSelectInner<
222
248
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
223
249
  ref: any
224
250
  ) {
251
+ const optionsCount = useMemo(() => {
252
+ return countOptions<Option, Group>(rest.options);
253
+ }, [rest.options]);
225
254
  return (
226
255
  <BaseCreatableSelect
227
256
  ref={ref}
@@ -239,7 +268,13 @@ export function CreatableSelectInner<
239
268
  MenuList: virtualized ? WindowedMenuList : reactSelectComponents.MenuList,
240
269
  DropdownIndicator,
241
270
  ClearIndicator,
242
- Option,
271
+ /*
272
+ Fast Options break option focus on hover behaviour but help with slow React Select performance with large options list.
273
+ This isolates bug for big lists only
274
+ Read Option definition for more info
275
+ bug here: https://the.fibery.io/SoftDev/bug/Select-option-behaviour-is-incorrect-9413
276
+ */
277
+ Option: optionsCount > FAST_OPTION_USAGE_THRESHOLD ? Option : OptionSlow,
243
278
  MultiValueRemove: ClearIndicator,
244
279
  NoOptionsMessage,
245
280
  GroupHeading,
@@ -1,5 +1,5 @@
1
1
  import {css, cx} from "@linaria/core";
2
- import {border, radius, space, textStyles} from "../design-system";
2
+ import {border, space, textStyles} from "../design-system";
3
3
  import {inputOverrides} from "../antd/styles";
4
4
  import React, {
5
5
  ComponentType,
@@ -12,7 +12,7 @@ import React, {
12
12
  useRef,
13
13
  useState,
14
14
  } from "react";
15
- import {Popup} from "../Popup";
15
+ import {Popup} from "../popup";
16
16
  import {$TSFixMe} from "../tsfixme";
17
17
  import {
18
18
  combineStyles,
@@ -27,18 +27,18 @@ import {
27
27
  } from "./index";
28
28
  import {SelectControlSettingsProvider} from "./select-control-settings-context";
29
29
 
30
- const offset = [0, space.xs] as [number, number];
30
+ const offset = [0, space.s4] as [number, number];
31
31
  const popupClassName = css`
32
32
  z-index: 1050;
33
33
  max-width: 360px;
34
- padding-top: ${space.m}px;
35
- padding-bottom: ${space.xs}px;
36
- padding-left: ${space.m}px;
37
- padding-right: ${space.m}px;
34
+ padding-top: ${space.s8}px;
35
+ padding-bottom: ${space.s4}px;
36
+ padding-left: ${space.s8}px;
37
+ padding-right: ${space.s8}px;
38
38
  `;
39
39
 
40
40
  const valueClassName = css`
41
- border-radius: ${radius}px;
41
+ border-radius: ${border.radius4}px;
42
42
 
43
43
  &:focus {
44
44
  outline: none;
@@ -58,10 +58,10 @@ const valueClassName = css`
58
58
 
59
59
  const titleClassName = css`
60
60
  ${textStyles.heading6}
61
- padding-top: ${space.s}px;
62
- padding-left: ${space.m}px;
63
- padding-right: ${space.m}px;
64
- padding-bottom: ${space.l}px;
61
+ padding-top: ${space.s6}px;
62
+ padding-left: ${space.s8}px;
63
+ padding-right: ${space.s8}px;
64
+ padding-bottom: ${space.s12}px;
65
65
  `;
66
66
 
67
67
  export const selectInPopupStyles = {
@@ -78,10 +78,10 @@ export const selectInPopupStyles = {
78
78
  ...props,
79
79
  minWidth: 100,
80
80
  position: "static",
81
- marginTop: space.s,
81
+ marginTop: space.s6,
82
82
  marginBottom: 0,
83
83
  boxShadow: "none",
84
- borderRadius: radius,
84
+ borderRadius: border.radius4,
85
85
  }),
86
86
  };
87
87
 
@@ -2,8 +2,8 @@ import {css} from "@linaria/core";
2
2
  import type {ControlProps, CSSObjectWithLabel, StylesConfig} from "react-select";
3
3
  import {inputOverrides} from "../antd/styles";
4
4
  import {layout, space, themeVars} from "../design-system";
5
- import {OptionNulledStyles} from "./custom-select-partials/option";
6
- import {MenuNulledStyles} from "./custom-select-partials/menu";
5
+ import {OptionNulledStyles, OptionNulledVitualizedStyles} from "./custom-select-partials/option";
6
+ import {MenuListNulledStyles, MenuNulledStyles} from "./custom-select-partials/menu";
7
7
  import {groupHeadingNulledStyles} from "./custom-select-partials/group-heading";
8
8
 
9
9
  export const expanderExpandedStyle = css`
@@ -35,14 +35,14 @@ function createControlStyle({
35
35
  ...(state.isFocused && inputOverrides.focus),
36
36
  ...(state.isDisabled && inputOverrides.disabled),
37
37
  ":hover": !state.isFocused && !state.isDisabled ? inputOverrides.hover : {},
38
- paddingLeft: space.l,
39
- paddingRight: space.l,
38
+ paddingLeft: space.s10,
39
+ paddingRight: space.s12,
40
40
  "& > div": childDivStyle,
41
41
  } as unknown as CSSObjectWithLabel);
42
42
  }
43
43
 
44
44
  export const virtualizedStyles: StylesConfig = {
45
- option: () => OptionNulledStyles,
45
+ option: () => ({...OptionNulledVitualizedStyles, ...OptionNulledStyles}),
46
46
  groupHeading: (provided) => ({
47
47
  ...provided,
48
48
  height: layout.menuItemHeight,
@@ -64,9 +64,7 @@ export const zIndexStyles = (zIndex: number): StylesConfig => ({
64
64
  });
65
65
 
66
66
  export const componentsStyles: StylesConfig = {
67
- option: () => ({
68
- minHeight: layout.menuItemHeight,
69
- }),
67
+ option: () => OptionNulledStyles,
70
68
  placeholder: (provided) => ({
71
69
  ...provided,
72
70
  color: themeVars.disabledTextColor,
@@ -80,11 +78,15 @@ export const componentsStyles: StylesConfig = {
80
78
  ...provided,
81
79
  overflow: "visible",
82
80
  color: themeVars.textColor,
81
+ marginLeft: "",
82
+ marginRight: "",
83
+ margin: "",
83
84
  }),
84
85
  valueContainer(provided) {
85
86
  return {
86
87
  ...provided,
87
88
  minWidth: 10,
89
+ gridTemplateColumns: "1fr",
88
90
  };
89
91
  },
90
92
  multiValue: (provided) => ({
@@ -94,7 +96,7 @@ export const componentsStyles: StylesConfig = {
94
96
  marginTop: 0,
95
97
  marginBottom: 0,
96
98
  marginLeft: 0,
97
- marginRight: space.l,
99
+ marginRight: space.s12,
98
100
  alignItems: "center",
99
101
  height: layout.inputHeight - 2,
100
102
  }),
@@ -117,6 +119,10 @@ export const componentsStyles: StylesConfig = {
117
119
  ...provided,
118
120
  ...MenuNulledStyles,
119
121
  }),
122
+ menuList: (provided) => ({
123
+ ...provided,
124
+ ...MenuListNulledStyles,
125
+ }),
120
126
  container: (provided, state) => ({
121
127
  ...provided,
122
128
  pointerEvents: "auto",
@@ -1,24 +1,29 @@
1
+ import {preventDefault} from "@fibery/react/src/prevent-default";
1
2
  import {css, cx} from "@linaria/core";
2
3
  import * as ToastPrimitives from "@radix-ui/react-toast";
3
4
  import {ElementRef, forwardRef} from "react";
5
+ import {IconButton} from "../button/icon-button";
4
6
  import {border, space, textStyles, themeVars} from "../design-system";
5
7
  import CloseIcon from "../icons/react/Close";
6
- import {IconButton} from "../Button/icon-button";
7
- import {Button} from "../button";
8
- import {ButtonProps} from "../Button/button";
9
- import {useTheme} from "../theme-provider";
10
-
11
- export const ToastProvider = ToastPrimitives.Provider;
12
8
 
13
9
  const viewportCss = css`
14
10
  position: fixed;
15
11
  left: 50%;
16
- transform: translateX(-50%);
17
12
  bottom: 0;
13
+ transform: translateX(-50%);
14
+
15
+ display: flex;
16
+ flex-direction: column-reverse;
17
+ align-items: center;
18
+ gap: ${space.s16}px;
18
19
  margin: 0;
19
20
  z-index: 1010; // inherited from old toast implementation
20
- padding: ${space.xl}px;
21
+ padding: 0;
21
22
  list-style: none;
23
+
24
+ &:not(:empty) {
25
+ padding: ${space.s24}px; // increased mouse hover area to pause timers of toast
26
+ }
22
27
  `;
23
28
 
24
29
  export const ToastViewport = forwardRef<
@@ -29,12 +34,25 @@ export const ToastViewport = forwardRef<
29
34
  });
30
35
 
31
36
  const rootCss = css`
37
+ user-select: auto !important;
38
+
32
39
  background-color: ${themeVars.colorBgToastDefault};
33
- padding: ${space.m}px ${space.m * 2}px;
40
+ padding: ${space.s12}px ${space.s8 * 2}px;
34
41
  border-radius: ${border.radius6}px;
35
42
  box-shadow: ${themeVars.shadowModal};
36
43
 
37
- @keyframes hide {
44
+ will-change: opacity;
45
+
46
+ @keyframes fadeIn {
47
+ from {
48
+ opacity: 0;
49
+ }
50
+ to {
51
+ opacity: 1;
52
+ }
53
+ }
54
+
55
+ @keyframes fadeOut {
38
56
  from {
39
57
  opacity: 1;
40
58
  }
@@ -71,15 +89,19 @@ const rootCss = css`
71
89
  transition: transform 200ms ease-out;
72
90
  }
73
91
 
92
+ &[data-state="open"] {
93
+ animation: fadeIn 200ms ease-out;
94
+ }
95
+ &[data-state="closed"] {
96
+ animation: fadeOut 200ms ease-out;
97
+ }
98
+
74
99
  @media (prefers-reduced-motion: no-preference) {
75
100
  will-change: transform, opacity;
76
101
 
77
102
  &[data-state="open"] {
78
103
  animation: slideIn 200ms ease-out;
79
104
  }
80
- &[data-state="closed"] {
81
- animation: hide 200ms ease-out;
82
- }
83
105
 
84
106
  &[data-swipe="end"] {
85
107
  animation: swipeOut 200ms ease-out;
@@ -89,7 +111,17 @@ const rootCss = css`
89
111
 
90
112
  export const ToastRoot = forwardRef<ElementRef<typeof ToastPrimitives.Root>, ToastPrimitives.ToastProps>(
91
113
  ({className, ...props}, ref) => {
92
- return <ToastPrimitives.Root ref={ref} className={cx(rootCss, className)} {...props}></ToastPrimitives.Root>;
114
+ return (
115
+ <ToastPrimitives.Root
116
+ ref={ref}
117
+ className={cx(rootCss, className)}
118
+ onSwipeStart={preventDefault}
119
+ onSwipeMove={preventDefault}
120
+ onSwipeCancel={preventDefault}
121
+ onSwipeEnd={preventDefault}
122
+ {...props}
123
+ />
124
+ );
93
125
  }
94
126
  );
95
127
 
@@ -115,20 +147,6 @@ export const ToastSubtitle = forwardRef<
115
147
  return <ToastPrimitives.Description ref={ref} className={cx(className, descriptionCss)} {...props} />;
116
148
  });
117
149
 
118
- export const ToastAction = forwardRef<
119
- ElementRef<typeof ToastPrimitives.Action>,
120
- ButtonProps & {altText: ToastPrimitives.ToastActionProps["altText"]}
121
- >(({children, altText, ...props}, ref) => {
122
- const theme = useTheme();
123
- return (
124
- <ToastPrimitives.Action ref={ref} altText={altText} asChild>
125
- <Button color={theme.textColor} {...props}>
126
- {children}
127
- </Button>
128
- </ToastPrimitives.Action>
129
- );
130
- });
131
-
132
150
  export const ToastClose = () => {
133
151
  return (
134
152
  <ToastPrimitives.Close aria-label="Dismiss" asChild>
@@ -0,0 +1,28 @@
1
+ import {Action as RadixToastAction, type ToastActionProps} from "@radix-ui/react-toast";
2
+ import {ElementRef, forwardRef} from "react";
3
+ import {ButtonProps} from "../button/button";
4
+ import {Button} from "../button";
5
+ import {useTheme} from "../theme-provider";
6
+ import {css} from "@linaria/core";
7
+ import {space} from "../design-system";
8
+
9
+ const toastActionCss = css`
10
+ margin-top: -${space.s4}px;
11
+ margin-bottom: -${space.s4}px;
12
+ flex-shrink: 0;
13
+ `;
14
+
15
+ export const ToastAction = forwardRef<
16
+ ElementRef<typeof RadixToastAction>,
17
+ ButtonProps & {altText: ToastActionProps["altText"]}
18
+ >(({children, altText, ...props}, ref) => {
19
+ const theme = useTheme();
20
+ return (
21
+ <RadixToastAction className={toastActionCss} ref={ref} altText={altText} asChild>
22
+ <Button color={theme.textColor} {...props}>
23
+ {children}
24
+ </Button>
25
+ </RadixToastAction>
26
+ );
27
+ });
28
+ export type ToastActionElement = React.ReactElement<typeof ToastAction>;
@@ -0,0 +1,15 @@
1
+ import {createContext} from "@fibery/react/src/create-context";
2
+ import React from "react";
3
+ import type {ToastQueue} from "./toast-queue";
4
+ import {ToastProvider as PrimitiveToastProvider} from "@radix-ui/react-toast";
5
+
6
+ const [Provider, useCtx] = createContext<ToastQueue>("ToastQueue");
7
+
8
+ export const ToastProvider: React.FC<React.PropsWithChildren<{toastQueue: ToastQueue}>> = ({toastQueue, children}) => {
9
+ return (
10
+ <Provider value={toastQueue}>
11
+ <PrimitiveToastProvider>{children}</PrimitiveToastProvider>
12
+ </Provider>
13
+ );
14
+ };
15
+ export const useToast = useCtx;