@fibery/ui-kit 1.4.1 → 1.7.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 (71) hide show
  1. package/index.ts +0 -1
  2. package/package.json +39 -23
  3. package/src/Item.tsx +8 -2
  4. package/src/Pallete.ts +191 -191
  5. package/src/Select/components.tsx +22 -21
  6. package/src/Select/index.tsx +98 -30
  7. package/src/Select/select-control-settings-context.tsx +20 -0
  8. package/src/Select/select-in-popover.tsx +333 -0
  9. package/src/Select/styles.ts +27 -1
  10. package/src/ThemeProvider.tsx +1 -1
  11. package/src/antd/styles.ts +5 -7
  12. package/src/designSystem.ts +129 -91
  13. package/src/error-alert.tsx +2 -2
  14. package/src/icons/Icon.tsx +17 -12
  15. package/src/icons/ast/AddGroup.ts +8 -0
  16. package/src/icons/ast/AiAssistant.ts +8 -0
  17. package/src/icons/ast/AiAvatar.ts +8 -0
  18. package/src/icons/ast/ArrowBottom.ts +1 -1
  19. package/src/icons/ast/ArrowCollapse.ts +1 -1
  20. package/src/icons/ast/ArrowCollapseVertical.ts +1 -1
  21. package/src/icons/ast/ArrowLeft.ts +1 -1
  22. package/src/icons/ast/ArrowRight.ts +1 -1
  23. package/src/icons/ast/ArrowTop.ts +1 -1
  24. package/src/icons/ast/Atom.ts +8 -0
  25. package/src/icons/ast/Copy.ts +8 -0
  26. package/src/icons/ast/DynamicFilterValue.ts +8 -0
  27. package/src/icons/ast/ExtensionAssignments.ts +1 -1
  28. package/src/icons/ast/FileUpload.ts +8 -0
  29. package/src/icons/ast/FormWithCover.ts +8 -0
  30. package/src/icons/ast/FormWithoutCover.ts +8 -0
  31. package/src/icons/ast/Github.ts +3 -27
  32. package/src/icons/ast/Gitlab.ts +3 -25
  33. package/src/icons/ast/Hint.ts +1 -1
  34. package/src/icons/ast/Jira.ts +8 -0
  35. package/src/icons/ast/Markdown.ts +8 -0
  36. package/src/icons/ast/Minus.ts +8 -0
  37. package/src/icons/ast/MoreCompact.ts +1 -1
  38. package/src/icons/ast/RicheditorBlockFile.ts +8 -0
  39. package/src/icons/ast/RicheditorBlockH4.ts +1 -1
  40. package/src/icons/ast/SimpleCompass.ts +8 -0
  41. package/src/icons/ast/Templates.ts +8 -0
  42. package/src/icons/ast/TypeLocation.ts +8 -0
  43. package/src/icons/ast/ViewForm.ts +8 -0
  44. package/src/icons/ast/ViewMap.ts +8 -0
  45. package/src/icons/ast/index.tsx +199 -181
  46. package/src/icons/react/AddGroup.tsx +12 -0
  47. package/src/icons/react/AiAssistant.tsx +12 -0
  48. package/src/icons/react/AiAvatar.tsx +12 -0
  49. package/src/icons/react/Atom.tsx +12 -0
  50. package/src/icons/react/Copy.tsx +12 -0
  51. package/src/icons/react/DynamicFilterValue.tsx +12 -0
  52. package/src/icons/react/FileUpload.tsx +12 -0
  53. package/src/icons/react/FormWithCover.tsx +12 -0
  54. package/src/icons/react/FormWithoutCover.tsx +12 -0
  55. package/src/icons/react/Github.tsx +8 -6
  56. package/src/icons/react/Gitlab.tsx +8 -6
  57. package/src/icons/react/Jira.tsx +12 -0
  58. package/src/icons/react/Markdown.tsx +12 -0
  59. package/src/icons/react/Minus.tsx +12 -0
  60. package/src/icons/react/RicheditorBlockFile.tsx +12 -0
  61. package/src/icons/react/SimpleCompass.tsx +12 -0
  62. package/src/icons/react/Templates.tsx +12 -0
  63. package/src/icons/react/TypeLocation.tsx +12 -0
  64. package/src/icons/react/ViewForm.tsx +12 -0
  65. package/src/icons/react/ViewMap.tsx +12 -0
  66. package/src/icons/react/index.tsx +199 -181
  67. package/src/tooltip.tsx +132 -152
  68. package/src/Button.d.ts +0 -26
  69. package/src/Button.js +0 -360
  70. package/src/antd/ant-tooltip.tsx +0 -5
  71. package/src/icons/IconAsBackground.tsx +0 -15
@@ -1,12 +1,12 @@
1
- import {useCallback, ReactNode} from "react";
1
+ import React, {ReactNode} from "react";
2
2
  import {css} from "@linaria/core";
3
3
  import {components} from "react-windowed-select";
4
4
  import RemoveIcon from "../icons/react/Remove";
5
- import {OptionProps, DropdownIndicatorProps, ClearIndicatorProps, MultiValueRemoveProps, GroupBase} from "react-select";
6
- import {Button} from "../Button";
7
- import {themeVars, space} from "../designSystem";
5
+ import {ClearIndicatorProps, DropdownIndicatorProps, GroupBase, MultiValueRemoveProps, OptionProps} from "react-select";
6
+ import {Button} from "../Button/button";
7
+ import {space, themeVars} from "../designSystem";
8
8
  import ArrowCollapseVertical from "../icons/react/ArrowCollapseVertical";
9
- import {expanderStyle, expanderExpandedStyle} from "./styles";
9
+ import {expanderExpandedStyle, expanderStyle} from "./styles";
10
10
  import cn from "classnames";
11
11
 
12
12
  export const Option = <
@@ -43,24 +43,9 @@ export function ClearIndicator<
43
43
  innerProps,
44
44
  selectProps,
45
45
  }: ClearIndicatorProps<TOption, IsMulti, Group> | MultiValueRemoveProps<TOption, IsMulti, Group>) {
46
- const onMouseDown = innerProps.onMouseDown;
47
- const wrappedOnMouseDown = useCallback(
48
- (e) => {
49
- onMouseDown?.(e);
50
- // This prevents false positive triggering in rc-trigger
51
- e.nativeEvent.stopImmediatePropagation();
52
- },
53
- [onMouseDown]
54
- );
55
46
  return (
56
47
  <div {...innerProps}>
57
- <Button
58
- borderless
59
- Icon={RemoveIcon}
60
- disabled={selectProps.isDisabled}
61
- size={":button-size/super-small"}
62
- onMouseDown={wrappedOnMouseDown}
63
- />
48
+ <Button borderless Icon={RemoveIcon} disabled={selectProps.isDisabled} size={":button-size/super-small"} />
64
49
  </div>
65
50
  );
66
51
  }
@@ -81,3 +66,19 @@ export const NoOptionsMessage = ({children}: {children: ReactNode}) => {
81
66
  </div>
82
67
  );
83
68
  };
69
+
70
+ const groupDividerContainerStyle = css`
71
+ padding: ${space.l}px 0;
72
+ box-sizing: content-box;
73
+ `;
74
+
75
+ const groupDividerStyle = css`
76
+ width: 100%;
77
+ border-top: 1px solid #eeeff1;
78
+ `;
79
+
80
+ export const GroupDivider = () => (
81
+ <div className={groupDividerContainerStyle}>
82
+ <div className={groupDividerStyle}></div>
83
+ </div>
84
+ );
@@ -1,46 +1,67 @@
1
+ /* eslint-disable */
1
2
  import cn from "classnames";
2
- import {useCallback, forwardRef} from "react";
3
+ import {forwardRef, useCallback, useMemo, useState} from "react";
3
4
  import BaseSelect, {
5
+ createFilter,
6
+ GroupBase as ReactSelectGroupBase,
4
7
  GroupHeadingProps,
8
+ MenuListProps,
5
9
  MenuProps,
6
- StylesConfig,
7
- Props as BaseSelectProps,
8
- GroupBase,
9
- OptionsOrGroups,
10
- OptionProps,
11
- SingleValueProps,
10
+ mergeStyles,
11
+ MultiValue,
12
12
  MultiValueProps,
13
+ OptionProps,
14
+ OptionsOrGroups,
15
+ Props as BaseSelectProps,
13
16
  SingleValue,
14
- MultiValue,
15
- mergeStyles,
17
+ SingleValueProps,
18
+ StylesConfig,
19
+ ActionMeta,
16
20
  } from "react-select";
17
- import BaseCreatableSelect from "react-select/creatable";
21
+ import BaseCreatableSelect, {CreatableProps} from "react-select/creatable";
18
22
  import WindowedSelect, {components, WindowedMenuList} from "react-windowed-select";
19
23
  import {createInlineTheme} from "../designSystem";
20
24
  import {useTheme} from "../ThemeProvider";
21
- import {ClearIndicator, DropdownIndicator, NoOptionsMessage, Option as OptionComponent} from "./components";
22
- import {componentsStyles, singleLineComponentsStyle} from "./styles";
25
+ import {
26
+ ClearIndicator,
27
+ DropdownIndicator,
28
+ GroupDivider,
29
+ NoOptionsMessage,
30
+ Option as OptionComponent,
31
+ } from "./components";
32
+ import {componentsStyles, singleLineComponentsStyle, virtualizedStyles, zIndexStyles} from "./styles";
33
+ import {useSelectControlSettings} from "./select-control-settings-context";
34
+
35
+ type GroupBase<T> = ReactSelectGroupBase<T> & {separator?: boolean};
23
36
 
24
37
  export type {
25
38
  OptionsOrGroups,
26
39
  GroupBase,
27
40
  StylesConfig,
41
+ MenuProps,
42
+ MenuListProps,
28
43
  OptionProps,
29
44
  SingleValueProps,
30
45
  MultiValueProps,
31
46
  SingleValue,
32
47
  MultiValue,
48
+ ActionMeta,
33
49
  };
34
50
 
35
51
  function GroupHeading<
36
52
  Option = unknown,
37
53
  IsMulti extends boolean = boolean,
38
54
  Group extends GroupBase<Option> = GroupBase<Option>
39
- >(props: GroupHeadingProps<Option, IsMulti, Group>) {
40
- if (!props.children) {
55
+ >({children, ...props}: GroupHeadingProps<Option, IsMulti, Group>) {
56
+ if (props.data.separator) {
57
+ return <GroupDivider />;
58
+ }
59
+
60
+ if (!children) {
41
61
  return null;
42
62
  }
43
- return <components.GroupHeading {...props} />;
63
+
64
+ return <components.GroupHeading {...props}>{children}</components.GroupHeading>;
44
65
  }
45
66
 
46
67
  function Menu<Option = unknown, IsMulti extends boolean = boolean, Group extends GroupBase<Option> = GroupBase<Option>>(
@@ -52,6 +73,8 @@ function Menu<Option = unknown, IsMulti extends boolean = boolean, Group extends
52
73
  return <components.Menu {...props} innerProps={overridedInnerProps} className={cn(className, "react-select-menu")} />;
53
74
  }
54
75
 
76
+ export {WindowedMenuList};
77
+
55
78
  export {components};
56
79
 
57
80
  export function combineStyles<
@@ -62,6 +85,8 @@ export function combineStyles<
62
85
  return stylesConfigs.reduce(mergeStyles, {});
63
86
  }
64
87
 
88
+ export {createFilter};
89
+
65
90
  export type SelectProps<
66
91
  Option = unknown,
67
92
  IsMulti extends boolean = boolean,
@@ -100,34 +125,48 @@ export const Select = forwardRef(function Select<
100
125
  onKeyDown,
101
126
  styles = {},
102
127
  virtualized = true,
128
+ onMenuOpen,
129
+ onMenuClose,
130
+ onChange,
103
131
  ...rest
104
132
  }: SelectProps<Option, IsMulti, Group>,
105
133
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
106
134
  forwardedRef: any
107
135
  ) {
108
- const handleKeyDown = useCallback(
136
+ const {getPopupContainerElement, zIndex} = useSelectControlSettings();
137
+ const [menuOpenState, setMenuOpenState] = useState(rest.defaultMenuIsOpen);
138
+ const onMenuOpenWrapped = useCallback(() => {
139
+ setMenuOpenState(true);
140
+ onMenuOpen && onMenuOpen();
141
+ }, [onMenuOpen]);
142
+ const onMenuCloseWrapped = useCallback(() => {
143
+ setMenuOpenState(false);
144
+ onMenuClose && onMenuClose();
145
+ }, [onMenuClose]);
146
+ const handleKeyDown: React.KeyboardEventHandler<HTMLInputElement> = useCallback(
109
147
  (e) => {
110
148
  switch (e.key) {
111
149
  case "Escape":
112
- if (rest.menuIsOpen) {
150
+ // we cannot rely on "menuIsOpen" prop here, as user may not pass this prop and rely on internal component behavior instead.
151
+ if (menuOpenState) {
113
152
  e.stopPropagation();
114
153
  }
115
154
  break;
116
155
  case "Home":
117
156
  e.preventDefault();
118
157
  if (e.shiftKey) {
119
- e.target.selectionStart = 0;
158
+ (e.target as HTMLInputElement).selectionStart = 0;
120
159
  } else {
121
- e.target.setSelectionRange(0, 0);
160
+ (e.target as HTMLInputElement).setSelectionRange(0, 0);
122
161
  }
123
162
  break;
124
163
  case "End": {
125
164
  e.preventDefault();
126
- const len = e.target.value.length;
165
+ const len = (e.target as HTMLInputElement).value.length;
127
166
  if (e.shiftKey) {
128
- e.target.selectionEnd = len;
167
+ (e.target as HTMLInputElement).selectionEnd = len;
129
168
  } else {
130
- e.target.setSelectionRange(len, len);
169
+ (e.target as HTMLInputElement).setSelectionRange(len, len);
131
170
  }
132
171
  break;
133
172
  }
@@ -135,20 +174,28 @@ export const Select = forwardRef(function Select<
135
174
  }
136
175
  onKeyDown && onKeyDown(e);
137
176
  },
138
- [onKeyDown, rest.menuIsOpen]
177
+ [onKeyDown, menuOpenState]
139
178
  );
140
179
 
141
180
  const SelectComponent = (virtualized ? WindowedSelect : BaseSelect) as unknown as typeof BaseSelect;
142
181
 
182
+ const combinedStyles = useMemo(() => {
183
+ return combineStyles<Option, IsMulti, Group>([
184
+ componentsStyles,
185
+ virtualized ? virtualizedStyles : {},
186
+ zIndex ? zIndexStyles(zIndex) : {},
187
+ styles,
188
+ ] as StylesConfig<Option, IsMulti, Group>[]);
189
+ }, [styles, virtualized]);
190
+
143
191
  return (
144
192
  <SelectComponent
145
193
  ref={forwardedRef}
146
- menuPortalTarget={menuPortalTarget}
194
+ // There are places, where it is useful to override menuPortalTarget globally for all selects.
195
+ // When you are already in some popup. e.g. user input in buttons, relation filter popover.
196
+ menuPortalTarget={(getPopupContainerElement && getPopupContainerElement()) || menuPortalTarget}
147
197
  menuPlacement={"auto"}
148
- styles={combineStyles<Option, IsMulti, Group>([
149
- componentsStyles as unknown as StylesConfig<Option, IsMulti, Group>,
150
- styles,
151
- ])}
198
+ styles={combinedStyles}
152
199
  isMulti={isCollectionMode}
153
200
  backspaceRemovesValue={isCollectionMode}
154
201
  tabSelectsValue={false}
@@ -164,6 +211,9 @@ export const Select = forwardRef(function Select<
164
211
  }}
165
212
  {...rest}
166
213
  onKeyDown={handleKeyDown}
214
+ onMenuOpen={onMenuOpenWrapped}
215
+ onMenuClose={onMenuCloseWrapped}
216
+ onChange={rest.disabled ? undefined : onChange}
167
217
  isDisabled={rest.disabled}
168
218
  />
169
219
  );
@@ -173,13 +223,31 @@ export const Select = forwardRef(function Select<
173
223
  props: SelectProps<Option, IsMulti, Group> & {ref?: any}
174
224
  ) => JSX.Element;
175
225
 
176
- export function CreatableSelect<
226
+ export const CreatableSelect = forwardRef(CreatableSelectInner) as <
227
+ Option = unknown,
228
+ IsMulti extends boolean = boolean,
229
+ Group extends GroupBase<Option> = GroupBase<Option>
230
+ >(
231
+ props: CreatableProps<Option, IsMulti, Group> & SelectProps<Option, IsMulti, Group>
232
+ ) => ReturnType<typeof CreatableSelectInner>;
233
+
234
+ export function CreatableSelectInner<
177
235
  Option = unknown,
178
236
  IsMulti extends boolean = boolean,
179
237
  Group extends GroupBase<Option> = GroupBase<Option>
180
- >({components, isCollectionMode, menuPortalTarget, styles = {}, ...rest}: SelectProps<Option, IsMulti, Group>) {
238
+ >(
239
+ {
240
+ components,
241
+ isCollectionMode,
242
+ menuPortalTarget,
243
+ styles = {},
244
+ ...rest
245
+ }: CreatableProps<Option, IsMulti, Group> & SelectProps<Option, IsMulti, Group>,
246
+ ref: any
247
+ ) {
181
248
  return (
182
249
  <BaseCreatableSelect
250
+ ref={ref}
183
251
  menuPortalTarget={menuPortalTarget}
184
252
  menuPlacement={"auto"}
185
253
  styles={combineStyles<Option, IsMulti, Group>([
@@ -0,0 +1,20 @@
1
+ import {createContext, ReactNode, useContext, useMemo} from "react";
2
+
3
+ const SelectControlSettings = createContext<{getPopupContainerElement?: () => HTMLElement; zIndex?: number}>({});
4
+
5
+ // Overrides zIndex and getPopupContainerElement for all selects, except select-in-popover.
6
+ // It has more priority than "menuPortalTarget" prop. Use at your own risk!
7
+ export function SelectControlSettingsProvider({
8
+ children,
9
+ getPopupContainerElement,
10
+ zIndex,
11
+ }: {
12
+ children: ReactNode;
13
+ getPopupContainerElement?: () => HTMLElement;
14
+ zIndex?: number;
15
+ }) {
16
+ const settings = useMemo(() => ({getPopupContainerElement, zIndex}), [getPopupContainerElement, zIndex]);
17
+ return <SelectControlSettings.Provider value={settings}>{children}</SelectControlSettings.Provider>;
18
+ }
19
+
20
+ export const useSelectControlSettings = () => useContext(SelectControlSettings);
@@ -0,0 +1,333 @@
1
+ import {css, cx} from "@linaria/core";
2
+ import {border, radius, space, textStyles} from "../designSystem";
3
+ import {inputOverrides} from "../antd/styles";
4
+ import React, {
5
+ ComponentType,
6
+ forwardRef,
7
+ ReactNode,
8
+ useCallback,
9
+ useEffect,
10
+ useImperativeHandle,
11
+ useMemo,
12
+ useRef,
13
+ useState,
14
+ } from "react";
15
+ import {Popup} from "../Popup";
16
+ import {$TSFixMe} from "../tsfixme";
17
+ import {combineStyles, GroupBase, OptionsOrGroups, Select as ReactSelect, SelectProps, StylesConfig} from "./index";
18
+ import {SelectControlSettingsProvider} from "./select-control-settings-context";
19
+
20
+ const offset = [0, space.xs] as [number, number];
21
+ const popupClassName = css`
22
+ z-index: 1050;
23
+ max-width: 360px;
24
+ padding-top: ${space.m}px;
25
+ padding-bottom: ${space.xs}px;
26
+ padding-left: ${space.m}px;
27
+ padding-right: ${space.m}px;
28
+ `;
29
+
30
+ const valueClassName = css`
31
+ border-radius: ${radius}px;
32
+
33
+ &:focus {
34
+ outline: none;
35
+ ${inputOverrides.focus}
36
+ }
37
+
38
+ &:focus:not(:focus-visible) {
39
+ outline: none;
40
+ box-shadow: none !important;
41
+ }
42
+
43
+ &:focus-visible {
44
+ outline: none;
45
+ ${inputOverrides.focus}
46
+ }
47
+ `;
48
+
49
+ const titleClassName = css`
50
+ ${textStyles.heading6}
51
+ padding-top: ${space.s}px;
52
+ padding-left: ${space.m}px;
53
+ padding-right: ${space.m}px;
54
+ padding-bottom: ${space.l}px;
55
+ `;
56
+
57
+ export const selectInPopupStyles = {
58
+ container: (props: $TSFixMe) => ({
59
+ ...props,
60
+ minWidth: 180,
61
+ width: "100%",
62
+ }),
63
+ option: (props: $TSFixMe) => ({
64
+ ...props,
65
+ borderRadius: border.radius3,
66
+ }),
67
+ menu: (props: $TSFixMe) => ({
68
+ ...props,
69
+ minWidth: 100,
70
+ position: "static",
71
+ marginTop: space.s,
72
+ marginBottom: 0,
73
+ boxShadow: "none",
74
+ borderRadius: radius,
75
+ }),
76
+ };
77
+
78
+ export type TriggerProps = {
79
+ innerRef: React.Ref<HTMLDivElement>;
80
+ children: ReactNode;
81
+ onClick: React.MouseEventHandler;
82
+ onKeyDown: React.KeyboardEventHandler;
83
+ };
84
+
85
+ const Trigger = ({innerRef, children, onClick, onKeyDown}: TriggerProps) => {
86
+ return (
87
+ <div ref={innerRef} onClick={onClick} onKeyDown={onKeyDown} tabIndex={0} className={valueClassName}>
88
+ {children}
89
+ </div>
90
+ );
91
+ };
92
+
93
+ type Components = {Trigger: ComponentType<TriggerProps>};
94
+
95
+ export type ComponentsConfig = Partial<Components>;
96
+
97
+ const defaultComponents = {Trigger};
98
+
99
+ type Ref = {
100
+ valueRef: HTMLDivElement | null;
101
+ selectRef: $TSFixMe | null;
102
+ };
103
+
104
+ type Props<Option, IsMulti extends boolean, Group extends GroupBase<Option>> = {
105
+ value?: Option;
106
+ menuIsOpen?: boolean;
107
+ autoFocus?: boolean;
108
+ defaultMenuIsOpen?: boolean;
109
+ placeholder?: string;
110
+ onInputChange?: (s: string, actionType: {action: string}) => void;
111
+ noOptionsMessage: () => string | JSX.Element | null;
112
+ options: OptionsOrGroups<Option, Group>;
113
+ filterOption?: SelectProps<Option>["filterOption"];
114
+ inputValue?: string;
115
+ disabled?: boolean;
116
+ onFocus?: () => void;
117
+ isCollectionMode?: IsMulti;
118
+ isClearable?: boolean;
119
+ onChange: (...arg: $TSFixMe) => void;
120
+ onMenuOpen: () => void;
121
+ onMenuClose: () => void | boolean;
122
+ renderValue?: () => ReactNode;
123
+ title?: string;
124
+ maxMenuHeight?: number;
125
+ hideOnChange?: boolean;
126
+ renderInPortal?: boolean;
127
+ popupStyles?: string;
128
+ selectStyles?: StylesConfig<Option, IsMulti, Group>;
129
+ formatGroupLabel?: (group: Group) => ReactNode;
130
+ formatOptionLabel?: (option: Option) => ReactNode;
131
+ components?: ComponentsConfig;
132
+ };
133
+
134
+ function SelectInPopoverInner<Option, IsMulti extends boolean, Group extends GroupBase<Option>>(
135
+ props: Props<Option, IsMulti, Group>,
136
+ forwardedRef: React.ForwardedRef<Ref>
137
+ ) {
138
+ const {
139
+ value,
140
+ menuIsOpen = false,
141
+ autoFocus,
142
+ defaultMenuIsOpen,
143
+ placeholder,
144
+ onInputChange,
145
+ noOptionsMessage,
146
+ options,
147
+ filterOption,
148
+ inputValue,
149
+ disabled,
150
+ onFocus,
151
+ isCollectionMode,
152
+ isClearable = true,
153
+ onChange,
154
+ onMenuOpen,
155
+ onMenuClose,
156
+ renderValue,
157
+ title,
158
+ maxMenuHeight,
159
+ hideOnChange = true,
160
+ renderInPortal = true,
161
+ popupStyles,
162
+ selectStyles = {},
163
+ formatGroupLabel,
164
+ formatOptionLabel,
165
+ components = {},
166
+ } = props;
167
+ const [triggerElement, setTriggerElement] = useState<HTMLDivElement | null>(null);
168
+ const [visible, setVisible] = useState(menuIsOpen);
169
+ const blurTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
170
+ const selectRef = useRef<{focus: () => void}>(null);
171
+ const ref = useRef<HTMLDivElement | null>(null);
172
+ useImperativeHandle(
173
+ forwardedRef,
174
+ () =>
175
+ ({
176
+ valueRef: ref.current,
177
+ selectRef: selectRef.current,
178
+ } as {valueRef: HTMLDivElement; selectRef: $TSFixMe | null})
179
+ );
180
+ useEffect(() => {
181
+ setTriggerElement(ref.current);
182
+ }, []);
183
+ useEffect(() => {
184
+ if (blurTimeoutRef.current) {
185
+ clearTimeout(blurTimeoutRef.current);
186
+ }
187
+ }, []);
188
+
189
+ const onHide = useCallback(
190
+ (restoreFocus = true) => {
191
+ const wasClosed = onMenuClose();
192
+ if (wasClosed === false) {
193
+ return;
194
+ }
195
+ setVisible(false);
196
+
197
+ if (restoreFocus) {
198
+ triggerElement?.focus();
199
+ }
200
+ },
201
+ [onMenuClose, triggerElement]
202
+ );
203
+
204
+ useEffect(() => {
205
+ const onVisibleChange = function () {
206
+ if (document.visibilityState === "visible" && blurTimeoutRef.current && visible) {
207
+ clearTimeout(blurTimeoutRef.current);
208
+ selectRef.current?.focus();
209
+ } else if (document.visibilityState === "hidden" && blurTimeoutRef.current) {
210
+ clearTimeout(blurTimeoutRef.current);
211
+ }
212
+ };
213
+ document.addEventListener("visibilitychange", onVisibleChange);
214
+ return () => {
215
+ document.removeEventListener("visibilitychange", onVisibleChange);
216
+ };
217
+ }, [visible]);
218
+
219
+ const onShow = useCallback(() => {
220
+ if (blurTimeoutRef.current) {
221
+ clearTimeout(blurTimeoutRef.current);
222
+ }
223
+ setVisible(true);
224
+ onMenuOpen();
225
+ }, [onMenuOpen]);
226
+
227
+ const styles = useMemo(() => {
228
+ return combineStyles<Option, IsMulti, Group>([selectInPopupStyles, selectStyles]);
229
+ }, [selectStyles]);
230
+
231
+ const onTriggerClick: React.MouseEventHandler = useCallback(
232
+ (event) => {
233
+ if (visible) {
234
+ onHide();
235
+ } else {
236
+ onShow();
237
+ }
238
+ event.stopPropagation();
239
+ },
240
+ [onHide, onShow, visible]
241
+ );
242
+
243
+ const onTriggerKeyDown: React.KeyboardEventHandler = useCallback(
244
+ (event) => {
245
+ if ((event.key === "Enter" || event.key === " ") && !visible) {
246
+ event.preventDefault();
247
+ onShow();
248
+ }
249
+ },
250
+ [onShow, visible]
251
+ );
252
+
253
+ const mergedComponents = {...defaultComponents, ...components};
254
+
255
+ return (
256
+ <>
257
+ <mergedComponents.Trigger innerRef={ref} onClick={onTriggerClick} onKeyDown={onTriggerKeyDown}>
258
+ {renderValue ? renderValue() : null}
259
+ </mergedComponents.Trigger>
260
+ {Boolean(triggerElement) && (
261
+ <Popup
262
+ offset={offset}
263
+ popupClassName={cx(popupClassName, popupStyles)}
264
+ placement="bottom-start"
265
+ renderInPortal={renderInPortal}
266
+ open={visible}
267
+ onOpen={() => onShow()}
268
+ onClose={() => onHide()}
269
+ trigger={<></>}
270
+ triggerDomElement={triggerElement}
271
+ >
272
+ {title && <div className={titleClassName}>{title}</div>}
273
+ {/*Usually we set popupContainerElement for react-select via context (SelectControlSettingsProvider). But here we render react-select in popover.*/}
274
+ {/*Render target in controlled by Popover here. So we just override SelectControlContext here and set popupContainerElement to undefined. */}
275
+ {/*So in dom react-select will be a child of popover. */}
276
+ <SelectControlSettingsProvider getPopupContainerElement={undefined}>
277
+ <ReactSelect
278
+ ref={selectRef}
279
+ styles={styles}
280
+ isSearchable={true}
281
+ onKeyDown={(event) => {
282
+ if ((event.key === "Escape" || event.key === "Tab") && visible) {
283
+ onHide();
284
+ event.preventDefault();
285
+ event.stopPropagation();
286
+ }
287
+ }}
288
+ autoFocus={autoFocus}
289
+ onBlur={() => {
290
+ blurTimeoutRef.current = setTimeout(() => {
291
+ onHide(false);
292
+ }, 250);
293
+ }}
294
+ value={value}
295
+ defaultMenuIsOpen={defaultMenuIsOpen}
296
+ controlShouldRenderValue
297
+ closeMenuOnScroll={false}
298
+ closeMenuOnSelect={false}
299
+ onChange={(...arg) => {
300
+ if (hideOnChange) {
301
+ onHide();
302
+ }
303
+ onChange(...arg);
304
+ }}
305
+ placeholder={placeholder}
306
+ onInputChange={onInputChange}
307
+ noOptionsMessage={noOptionsMessage}
308
+ options={options || []}
309
+ filterOption={filterOption}
310
+ inputValue={inputValue}
311
+ menuIsOpen
312
+ maxMenuHeight={maxMenuHeight}
313
+ disabled={disabled}
314
+ onFocus={onFocus}
315
+ isCollectionMode={isCollectionMode}
316
+ isClearable={!isCollectionMode && isClearable}
317
+ formatGroupLabel={formatGroupLabel}
318
+ formatOptionLabel={formatOptionLabel}
319
+ components={{DropdownIndicator: null, IndicatorSeparator: null}}
320
+ />
321
+ </SelectControlSettingsProvider>
322
+ </Popup>
323
+ )}
324
+ </>
325
+ );
326
+ }
327
+
328
+ type SelectInPopoverType = <Option, IsMulti extends boolean, Group extends GroupBase<Option>>(
329
+ props: Props<Option, IsMulti, Group> & {ref?: React.ForwardedRef<Ref>}
330
+ ) => ReturnType<typeof SelectInPopoverInner>;
331
+
332
+ // Explicit type assertion to specify that component accepts generic props because forwardRef breaks it
333
+ export const SelectInPopover = forwardRef(SelectInPopoverInner) as SelectInPopoverType;
@@ -44,6 +44,31 @@ function createControlStyle({
44
44
  } as unknown as CSSObjectWithLabel);
45
45
  }
46
46
 
47
+ export const virtualizedStyles: StylesConfig = {
48
+ option: (provided) => ({
49
+ ...provided,
50
+ height: layout.menuItemHeight,
51
+ }),
52
+ groupHeading: (provided) => ({
53
+ ...provided,
54
+ height: layout.menuItemHeight,
55
+ whiteSpace: "nowrap",
56
+ overflow: "hidden",
57
+ textOverflow: "ellipsis",
58
+ }),
59
+ };
60
+
61
+ export const zIndexStyles = (zIndex: number): StylesConfig => ({
62
+ menuPortal: (provided) => ({
63
+ ...provided,
64
+ zIndex: zIndex,
65
+ }),
66
+ menu: (provided) => ({
67
+ ...provided,
68
+ zIndex,
69
+ }),
70
+ });
71
+
47
72
  export const componentsStyles: StylesConfig = {
48
73
  option: (provided, state) => {
49
74
  const disabledStyles = {
@@ -116,7 +141,8 @@ export const componentsStyles: StylesConfig = {
116
141
  groupHeading: (provided) => ({
117
142
  ...provided,
118
143
  paddingLeft: space.l,
119
- paddingTop: space.l,
144
+ paddingRight: space.m,
145
+ paddingTop: space.m + space.xs,
120
146
  margin: 0,
121
147
  ...textStyles.small,
122
148
  color: themeVars.disabledTextColor,
@@ -1,5 +1,5 @@
1
1
  import invariant from "invariant";
2
- import {themeStyles} from "@fibery/ui-kit/src/theme-styles";
2
+ import {themeStyles} from "./theme-styles";
3
3
  import {createContext, ReactNode, useContext, useEffect, useState} from "react";
4
4
  import {colors, getThemeColors, ThemeColors} from "./designSystem";
5
5
  import {