@fibery/ui-kit 1.5.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +0 -1
- package/package.json +38 -24
- package/src/Button/AddButton.tsx +13 -0
- package/src/{BackButton.tsx → Button/BackButton.tsx} +3 -4
- package/src/Button/button-base.tsx +190 -0
- package/src/Button/button-group.tsx +76 -0
- package/src/Button/button.tsx +242 -0
- package/src/Button/icon-button-with-tooltip.tsx +15 -0
- package/src/Button/icon-button.tsx +42 -0
- package/src/Item.tsx +5 -2
- package/src/Pallete.ts +191 -191
- package/src/Select/components.tsx +22 -21
- package/src/Select/index.tsx +60 -25
- package/src/Select/select-control-settings-context.tsx +20 -0
- package/src/Select/select-in-popover.tsx +333 -0
- package/src/Select/styles.ts +27 -1
- package/src/ThemeProvider.tsx +1 -1
- package/src/antd/styles.ts +3 -3
- package/src/designSystem.ts +80 -63
- package/src/error-alert.tsx +2 -2
- package/src/icons/Icon.tsx +17 -12
- package/src/icons/ast/AddGroup.ts +8 -0
- package/src/icons/ast/AiAssistant.ts +8 -0
- package/src/icons/ast/AiAvatar.ts +8 -0
- package/src/icons/ast/ArrowBottom.ts +3 -27
- package/src/icons/ast/ArrowCollapse.ts +3 -27
- package/src/icons/ast/ArrowCollapseVertical.ts +3 -27
- package/src/icons/ast/ArrowLeft.ts +3 -27
- package/src/icons/ast/ArrowRight.ts +3 -27
- package/src/icons/ast/ArrowTop.ts +3 -27
- package/src/icons/ast/Atom.ts +8 -0
- package/src/icons/ast/Copy.ts +8 -0
- package/src/icons/ast/DynamicFilterValue.ts +8 -0
- package/src/icons/ast/ExtensionAssignments.ts +3 -35
- package/src/icons/ast/FileUpload.ts +8 -0
- package/src/icons/ast/FormWithCover.ts +8 -0
- package/src/icons/ast/FormWithoutCover.ts +8 -0
- package/src/icons/ast/Github.ts +3 -27
- package/src/icons/ast/Gitlab.ts +3 -25
- package/src/icons/ast/Hint.ts +1 -1
- package/src/icons/ast/Jira.ts +8 -0
- package/src/icons/ast/Markdown.ts +8 -0
- package/src/icons/ast/Minus.ts +3 -23
- package/src/icons/ast/MoreCompact.ts +1 -1
- package/src/icons/ast/RicheditorBlockFile.ts +8 -0
- package/src/icons/ast/RicheditorBlockH4.ts +1 -1
- package/src/icons/ast/SimpleCompass.ts +8 -0
- package/src/icons/ast/Templates.ts +8 -0
- package/src/icons/ast/TypeLocation.ts +8 -0
- package/src/icons/ast/ViewForm.ts +3 -25
- package/src/icons/ast/ViewMap.ts +8 -0
- package/src/icons/ast/index.tsx +199 -182
- package/src/icons/react/AddGroup.tsx +12 -0
- package/src/icons/react/AiAssistant.tsx +12 -0
- package/src/icons/react/AiAvatar.tsx +12 -0
- package/src/icons/react/Atom.tsx +12 -0
- package/src/icons/react/Copy.tsx +12 -0
- package/src/icons/react/DynamicFilterValue.tsx +12 -0
- package/src/icons/react/FileUpload.tsx +12 -0
- package/src/icons/react/FormWithCover.tsx +12 -0
- package/src/icons/react/FormWithoutCover.tsx +12 -0
- package/src/icons/react/Github.tsx +8 -6
- package/src/icons/react/Gitlab.tsx +8 -6
- package/src/icons/react/Jira.tsx +12 -0
- package/src/icons/react/Markdown.tsx +12 -0
- package/src/icons/react/Minus.tsx +8 -6
- package/src/icons/react/RicheditorBlockFile.tsx +12 -0
- package/src/icons/react/SimpleCompass.tsx +12 -0
- package/src/icons/react/Templates.tsx +12 -0
- package/src/icons/react/TypeLocation.tsx +12 -0
- package/src/icons/react/ViewForm.tsx +8 -6
- package/src/icons/react/ViewMap.tsx +12 -0
- package/src/icons/react/index.tsx +199 -182
- package/src/tooltip.tsx +132 -152
- package/src/Button.d.ts +0 -28
- package/src/Button.js +0 -360
- package/src/antd/ant-tooltip.tsx +0 -5
- package/src/icons/IconAsBackground.tsx +0 -15
package/src/Select/index.tsx
CHANGED
|
@@ -1,47 +1,67 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
2
|
import cn from "classnames";
|
|
3
|
-
import {useCallback,
|
|
3
|
+
import {forwardRef, useCallback, useMemo, useState} from "react";
|
|
4
4
|
import BaseSelect, {
|
|
5
|
+
createFilter,
|
|
6
|
+
GroupBase as ReactSelectGroupBase,
|
|
5
7
|
GroupHeadingProps,
|
|
8
|
+
MenuListProps,
|
|
6
9
|
MenuProps,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
GroupBase,
|
|
10
|
-
OptionsOrGroups,
|
|
11
|
-
OptionProps,
|
|
12
|
-
SingleValueProps,
|
|
10
|
+
mergeStyles,
|
|
11
|
+
MultiValue,
|
|
13
12
|
MultiValueProps,
|
|
13
|
+
OptionProps,
|
|
14
|
+
OptionsOrGroups,
|
|
15
|
+
Props as BaseSelectProps,
|
|
14
16
|
SingleValue,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
SingleValueProps,
|
|
18
|
+
StylesConfig,
|
|
19
|
+
ActionMeta,
|
|
17
20
|
} from "react-select";
|
|
18
21
|
import BaseCreatableSelect, {CreatableProps} from "react-select/creatable";
|
|
19
22
|
import WindowedSelect, {components, WindowedMenuList} from "react-windowed-select";
|
|
20
23
|
import {createInlineTheme} from "../designSystem";
|
|
21
24
|
import {useTheme} from "../ThemeProvider";
|
|
22
|
-
import {
|
|
23
|
-
|
|
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};
|
|
24
36
|
|
|
25
37
|
export type {
|
|
26
38
|
OptionsOrGroups,
|
|
27
39
|
GroupBase,
|
|
28
40
|
StylesConfig,
|
|
41
|
+
MenuProps,
|
|
42
|
+
MenuListProps,
|
|
29
43
|
OptionProps,
|
|
30
44
|
SingleValueProps,
|
|
31
45
|
MultiValueProps,
|
|
32
46
|
SingleValue,
|
|
33
47
|
MultiValue,
|
|
48
|
+
ActionMeta,
|
|
34
49
|
};
|
|
35
50
|
|
|
36
51
|
function GroupHeading<
|
|
37
52
|
Option = unknown,
|
|
38
53
|
IsMulti extends boolean = boolean,
|
|
39
54
|
Group extends GroupBase<Option> = GroupBase<Option>
|
|
40
|
-
>(props: GroupHeadingProps<Option, IsMulti, Group>) {
|
|
41
|
-
if (
|
|
55
|
+
>({children, ...props}: GroupHeadingProps<Option, IsMulti, Group>) {
|
|
56
|
+
if (props.data.separator) {
|
|
57
|
+
return <GroupDivider />;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!children) {
|
|
42
61
|
return null;
|
|
43
62
|
}
|
|
44
|
-
|
|
63
|
+
|
|
64
|
+
return <components.GroupHeading {...props}>{children}</components.GroupHeading>;
|
|
45
65
|
}
|
|
46
66
|
|
|
47
67
|
function Menu<Option = unknown, IsMulti extends boolean = boolean, Group extends GroupBase<Option> = GroupBase<Option>>(
|
|
@@ -53,6 +73,8 @@ function Menu<Option = unknown, IsMulti extends boolean = boolean, Group extends
|
|
|
53
73
|
return <components.Menu {...props} innerProps={overridedInnerProps} className={cn(className, "react-select-menu")} />;
|
|
54
74
|
}
|
|
55
75
|
|
|
76
|
+
export {WindowedMenuList};
|
|
77
|
+
|
|
56
78
|
export {components};
|
|
57
79
|
|
|
58
80
|
export function combineStyles<
|
|
@@ -63,6 +85,8 @@ export function combineStyles<
|
|
|
63
85
|
return stylesConfigs.reduce(mergeStyles, {});
|
|
64
86
|
}
|
|
65
87
|
|
|
88
|
+
export {createFilter};
|
|
89
|
+
|
|
66
90
|
export type SelectProps<
|
|
67
91
|
Option = unknown,
|
|
68
92
|
IsMulti extends boolean = boolean,
|
|
@@ -103,11 +127,13 @@ export const Select = forwardRef(function Select<
|
|
|
103
127
|
virtualized = true,
|
|
104
128
|
onMenuOpen,
|
|
105
129
|
onMenuClose,
|
|
130
|
+
onChange,
|
|
106
131
|
...rest
|
|
107
132
|
}: SelectProps<Option, IsMulti, Group>,
|
|
108
133
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
109
134
|
forwardedRef: any
|
|
110
135
|
) {
|
|
136
|
+
const {getPopupContainerElement, zIndex} = useSelectControlSettings();
|
|
111
137
|
const [menuOpenState, setMenuOpenState] = useState(rest.defaultMenuIsOpen);
|
|
112
138
|
const onMenuOpenWrapped = useCallback(() => {
|
|
113
139
|
setMenuOpenState(true);
|
|
@@ -117,7 +143,7 @@ export const Select = forwardRef(function Select<
|
|
|
117
143
|
setMenuOpenState(false);
|
|
118
144
|
onMenuClose && onMenuClose();
|
|
119
145
|
}, [onMenuClose]);
|
|
120
|
-
const handleKeyDown = useCallback(
|
|
146
|
+
const handleKeyDown: React.KeyboardEventHandler<HTMLInputElement> = useCallback(
|
|
121
147
|
(e) => {
|
|
122
148
|
switch (e.key) {
|
|
123
149
|
case "Escape":
|
|
@@ -129,18 +155,18 @@ export const Select = forwardRef(function Select<
|
|
|
129
155
|
case "Home":
|
|
130
156
|
e.preventDefault();
|
|
131
157
|
if (e.shiftKey) {
|
|
132
|
-
e.target.selectionStart = 0;
|
|
158
|
+
(e.target as HTMLInputElement).selectionStart = 0;
|
|
133
159
|
} else {
|
|
134
|
-
e.target.setSelectionRange(0, 0);
|
|
160
|
+
(e.target as HTMLInputElement).setSelectionRange(0, 0);
|
|
135
161
|
}
|
|
136
162
|
break;
|
|
137
163
|
case "End": {
|
|
138
164
|
e.preventDefault();
|
|
139
|
-
const len = e.target.value.length;
|
|
165
|
+
const len = (e.target as HTMLInputElement).value.length;
|
|
140
166
|
if (e.shiftKey) {
|
|
141
|
-
e.target.selectionEnd = len;
|
|
167
|
+
(e.target as HTMLInputElement).selectionEnd = len;
|
|
142
168
|
} else {
|
|
143
|
-
e.target.setSelectionRange(len, len);
|
|
169
|
+
(e.target as HTMLInputElement).setSelectionRange(len, len);
|
|
144
170
|
}
|
|
145
171
|
break;
|
|
146
172
|
}
|
|
@@ -153,15 +179,23 @@ export const Select = forwardRef(function Select<
|
|
|
153
179
|
|
|
154
180
|
const SelectComponent = (virtualized ? WindowedSelect : BaseSelect) as unknown as typeof BaseSelect;
|
|
155
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
|
+
|
|
156
191
|
return (
|
|
157
192
|
<SelectComponent
|
|
158
193
|
ref={forwardedRef}
|
|
159
|
-
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}
|
|
160
197
|
menuPlacement={"auto"}
|
|
161
|
-
styles={
|
|
162
|
-
componentsStyles as unknown as StylesConfig<Option, IsMulti, Group>,
|
|
163
|
-
styles,
|
|
164
|
-
])}
|
|
198
|
+
styles={combinedStyles}
|
|
165
199
|
isMulti={isCollectionMode}
|
|
166
200
|
backspaceRemovesValue={isCollectionMode}
|
|
167
201
|
tabSelectsValue={false}
|
|
@@ -179,6 +213,7 @@ export const Select = forwardRef(function Select<
|
|
|
179
213
|
onKeyDown={handleKeyDown}
|
|
180
214
|
onMenuOpen={onMenuOpenWrapped}
|
|
181
215
|
onMenuClose={onMenuCloseWrapped}
|
|
216
|
+
onChange={rest.disabled ? undefined : onChange}
|
|
182
217
|
isDisabled={rest.disabled}
|
|
183
218
|
/>
|
|
184
219
|
);
|
|
@@ -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;
|
package/src/Select/styles.ts
CHANGED
|
@@ -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
|
-
|
|
144
|
+
paddingRight: space.m,
|
|
145
|
+
paddingTop: space.m + space.xs,
|
|
120
146
|
margin: 0,
|
|
121
147
|
...textStyles.small,
|
|
122
148
|
color: themeVars.disabledTextColor,
|
package/src/ThemeProvider.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import invariant from "invariant";
|
|
2
|
-
import {themeStyles} from "
|
|
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 {
|
package/src/antd/styles.ts
CHANGED
|
@@ -23,15 +23,15 @@ export const inputOverrides = {
|
|
|
23
23
|
},
|
|
24
24
|
},
|
|
25
25
|
hover: {
|
|
26
|
-
boxShadow: `0 0 0 1px ${themeVars.opacity.
|
|
26
|
+
boxShadow: `0 0 0 1px ${themeVars.opacity.opacity40} !important`,
|
|
27
27
|
transition: `box-shadow ${transition}`,
|
|
28
28
|
},
|
|
29
29
|
focus: {
|
|
30
|
-
boxShadow: `0 0 0 1px ${themeVars.opacity.
|
|
30
|
+
boxShadow: `0 0 0 1px ${themeVars.opacity.opacity25}, 0 0 0 3px ${themeVars.focus} !important`,
|
|
31
31
|
transition: `box-shadow ${transition}`,
|
|
32
32
|
},
|
|
33
33
|
disabled: {
|
|
34
|
-
color: `${themeVars.accentTextColor}
|
|
34
|
+
color: `${themeVars.accentTextColor}`,
|
|
35
35
|
backgroundColor: `${themeVars.inputDisabledBgColor} !important`,
|
|
36
36
|
borderColor: colors.transparent,
|
|
37
37
|
boxShadow: `${themeVars.inputDisabledBorderColor} !important`,
|