@os-design/core 1.0.199 → 1.0.200
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 +21 -13
- package/src/@types/emotion.d.ts +7 -0
- package/src/Alert/index.tsx +112 -0
- package/src/Avatar/index.tsx +173 -0
- package/src/Avatar/utils/nameToInitials.ts +12 -0
- package/src/Avatar/utils/strToHue.ts +13 -0
- package/src/AvatarSkeleton/index.tsx +29 -0
- package/src/Breadcrumb/index.tsx +93 -0
- package/src/BreadcrumbItem/index.tsx +83 -0
- package/src/Button/ButtonContent.tsx +91 -0
- package/src/Button/index.tsx +225 -0
- package/src/Button/utils/useButtonColors.ts +84 -0
- package/src/Checkbox/index.tsx +225 -0
- package/src/CheckboxSkeleton/index.tsx +50 -0
- package/src/DatePicker/DatePickerCalendar.tsx +220 -0
- package/src/DatePicker/index.tsx +568 -0
- package/src/Drawer/index.tsx +212 -0
- package/src/Form/FormConfigContext.ts +16 -0
- package/src/Form/index.tsx +49 -0
- package/src/FormDivider/index.tsx +74 -0
- package/src/FormItem/index.tsx +118 -0
- package/src/Gallery/Status.tsx +62 -0
- package/src/Gallery/index.tsx +290 -0
- package/src/GlobalStyles/index.tsx +17 -0
- package/src/GlobalStyles/resetStyles.ts +17 -0
- package/src/GlobalStyles/typographyStyles.ts +78 -0
- package/src/HeaderSkeleton/index.tsx +64 -0
- package/src/Image/index.tsx +104 -0
- package/src/ImageSkeleton/index.tsx +22 -0
- package/src/Input/index.tsx +330 -0
- package/src/Input/utils/getFocusableElements.ts +8 -0
- package/src/InputNumber/index.tsx +208 -0
- package/src/InputNumber/utils/defaultLocale.ts +9 -0
- package/src/InputPassword/index.tsx +201 -0
- package/src/InputPassword/utils/defaultLocale.ts +11 -0
- package/src/InputSearch/index.tsx +111 -0
- package/src/InputSearch/utils/defaultLocale.ts +9 -0
- package/src/InputSkeleton/index.tsx +28 -0
- package/src/Layout/LayoutContext.ts +21 -0
- package/src/Layout/index.tsx +44 -0
- package/src/Link/index.tsx +129 -0
- package/src/LinkButton/index.tsx +100 -0
- package/src/List/WindowScroller.tsx +53 -0
- package/src/List/index.tsx +255 -0
- package/src/List/utils/bodyPointerEvents.ts +24 -0
- package/src/List/utils/frameTimeout.ts +36 -0
- package/src/List/utils/useRWLoadNext.ts +38 -0
- package/src/ListItem/index.tsx +92 -0
- package/src/ListItemActions/index.tsx +207 -0
- package/src/ListItemLink/index.tsx +63 -0
- package/src/ListSkeleton/index.tsx +115 -0
- package/src/LogoLink/index.tsx +93 -0
- package/src/LogoLink/logo.example.svg +18 -0
- package/src/Menu/index.tsx +128 -0
- package/src/Menu/utils/useFocusWithArrows.ts +50 -0
- package/src/MenuDivider/index.tsx +22 -0
- package/src/MenuGroup/index.tsx +190 -0
- package/src/MenuItem/index.tsx +108 -0
- package/src/Modal/index.tsx +411 -0
- package/src/Modal/utils/defaultLocale.ts +9 -0
- package/src/Navigation/index.tsx +214 -0
- package/src/Navigation/utils/useScrollFlags.ts +39 -0
- package/src/NavigationItem/index.tsx +136 -0
- package/src/PageContent/index.tsx +99 -0
- package/src/PageHeader/index.tsx +246 -0
- package/src/PageHeader/utils/defaultLocale.ts +9 -0
- package/src/PageHeaderInputSearch/index.tsx +145 -0
- package/src/PageHeaderInputSearch/utils/defaultLocale.ts +16 -0
- package/src/PageHeaderSkeleton/index.tsx +33 -0
- package/src/ParagraphSkeleton/index.tsx +65 -0
- package/src/Popover/index.tsx +243 -0
- package/src/Popover/utils/usePopoverPosition.ts +216 -0
- package/src/Progress/index.tsx +100 -0
- package/src/RadioGroup/index.tsx +165 -0
- package/src/RadioGroupSkeleton/index.tsx +36 -0
- package/src/Result/index.tsx +109 -0
- package/src/ScrollButton/index.tsx +159 -0
- package/src/ScrollButton/utils/useContainerPosition.ts +41 -0
- package/src/ScrollButton/utils/useVisibility.ts +56 -0
- package/src/Select/index.tsx +970 -0
- package/src/Select/utils/defaultLocale.ts +11 -0
- package/src/Skeleton/index.tsx +52 -0
- package/src/Switch/index.tsx +217 -0
- package/src/SwitchSkeleton/index.tsx +30 -0
- package/src/Tag/index.tsx +75 -0
- package/src/TagLink/index.tsx +53 -0
- package/src/TagList/index.tsx +95 -0
- package/src/TagListSkeleton/index.tsx +38 -0
- package/src/TagSkeleton/index.tsx +40 -0
- package/src/TextArea/index.tsx +231 -0
- package/src/TextAreaSkeleton/index.tsx +20 -0
- package/src/ThemeSwitcher/index.tsx +39 -0
- package/src/TimePicker/index.tsx +142 -0
- package/src/Video/index.tsx +41 -0
- package/src/index.ts +125 -0
- package/src/message/AlertIcon.tsx +50 -0
- package/src/message/Message.tsx +108 -0
- package/src/message/index.tsx +64 -0
- package/src/message/styles.ts +25 -0
|
@@ -0,0 +1,970 @@
|
|
|
1
|
+
import { css } from '@emotion/react';
|
|
2
|
+
import styled from '@emotion/styled';
|
|
3
|
+
import { Close, CloseCircle, Down, Loading, Up } from '@os-design/icons';
|
|
4
|
+
import { m, useIsMinWidth } from '@os-design/media';
|
|
5
|
+
import {
|
|
6
|
+
WithSize,
|
|
7
|
+
ellipsisStyles,
|
|
8
|
+
resetButtonStyles,
|
|
9
|
+
transitionStyles,
|
|
10
|
+
} from '@os-design/styles';
|
|
11
|
+
import { ThemeOverrider, clr, useTheme } from '@os-design/theming';
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
omitEmotionProps,
|
|
15
|
+
useBrowserLayoutEffect,
|
|
16
|
+
useEvent,
|
|
17
|
+
useFontSize,
|
|
18
|
+
useForwardedRef,
|
|
19
|
+
useForwardedState,
|
|
20
|
+
useResizeObserver,
|
|
21
|
+
useSize,
|
|
22
|
+
} from '@os-design/utils';
|
|
23
|
+
import React, {
|
|
24
|
+
forwardRef,
|
|
25
|
+
useCallback,
|
|
26
|
+
useEffect,
|
|
27
|
+
useMemo,
|
|
28
|
+
useRef,
|
|
29
|
+
useState,
|
|
30
|
+
} from 'react';
|
|
31
|
+
import { FixedSizeList } from 'react-window';
|
|
32
|
+
import Button from '../Button';
|
|
33
|
+
import { InputContainer } from '../Input';
|
|
34
|
+
import InputSearch, { InputSearchProps } from '../InputSearch';
|
|
35
|
+
import useRWLoadNext from '../List/utils/useRWLoadNext';
|
|
36
|
+
import Menu from '../Menu';
|
|
37
|
+
import MenuItem, { MenuItemProps } from '../MenuItem';
|
|
38
|
+
import { PopoverProps } from '../Popover';
|
|
39
|
+
import Tag from '../Tag';
|
|
40
|
+
import defaultLocale, { SelectLocale } from './utils/defaultLocale';
|
|
41
|
+
|
|
42
|
+
export interface Option extends MenuItemProps {
|
|
43
|
+
title?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
type JsxDivProps = Omit<
|
|
47
|
+
JSX.IntrinsicElements['div'],
|
|
48
|
+
'value' | 'defaultValue' | 'onChange' | 'ref'
|
|
49
|
+
>;
|
|
50
|
+
interface BaseSelectProps<T>
|
|
51
|
+
extends JsxDivProps,
|
|
52
|
+
WithSize,
|
|
53
|
+
Pick<PopoverProps, 'placement'> {
|
|
54
|
+
/**
|
|
55
|
+
* Options of the select.
|
|
56
|
+
* @default undefined
|
|
57
|
+
*/
|
|
58
|
+
options?: Option[];
|
|
59
|
+
/**
|
|
60
|
+
* The component located on the left side.
|
|
61
|
+
* @default undefined
|
|
62
|
+
*/
|
|
63
|
+
left?: React.ReactNode;
|
|
64
|
+
/**
|
|
65
|
+
* Adds padding to the left component.
|
|
66
|
+
* It can be useful when passing an icon or text in the left component.
|
|
67
|
+
* @default false
|
|
68
|
+
*/
|
|
69
|
+
leftHasPadding?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* The component located on the right side.
|
|
72
|
+
* @default undefined
|
|
73
|
+
*/
|
|
74
|
+
right?: React.ReactNode;
|
|
75
|
+
/**
|
|
76
|
+
* Adds padding to the right component.
|
|
77
|
+
* It can be useful when passing an icon or text in the right component.
|
|
78
|
+
* @default false
|
|
79
|
+
*/
|
|
80
|
+
rightHasPadding?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* The placeholder of the select.
|
|
83
|
+
* @default undefined
|
|
84
|
+
*/
|
|
85
|
+
placeholder?: string;
|
|
86
|
+
/**
|
|
87
|
+
* Whether the search input visible.
|
|
88
|
+
* @default false
|
|
89
|
+
*/
|
|
90
|
+
searchVisible?: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Props of the search input.
|
|
93
|
+
* @default undefined
|
|
94
|
+
*/
|
|
95
|
+
searchProps?: InputSearchProps;
|
|
96
|
+
/**
|
|
97
|
+
* Text displayed when there are no list items.
|
|
98
|
+
* @default Not found
|
|
99
|
+
*/
|
|
100
|
+
notFoundText?: string;
|
|
101
|
+
/**
|
|
102
|
+
* Whether the border is hidden.
|
|
103
|
+
* @default false
|
|
104
|
+
*/
|
|
105
|
+
unbordered?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Shows the loading status.
|
|
108
|
+
* @default false
|
|
109
|
+
*/
|
|
110
|
+
loading?: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Whether the select is disabled.
|
|
113
|
+
* @default false
|
|
114
|
+
*/
|
|
115
|
+
disabled?: boolean;
|
|
116
|
+
/**
|
|
117
|
+
* Whether the component have a focus.
|
|
118
|
+
* @default false
|
|
119
|
+
*/
|
|
120
|
+
autoFocus?: boolean;
|
|
121
|
+
/**
|
|
122
|
+
* Whether the component opens the popup list.
|
|
123
|
+
* @default false
|
|
124
|
+
*/
|
|
125
|
+
autoOpen?: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Whether the select has the clear button.
|
|
128
|
+
* @default false
|
|
129
|
+
*/
|
|
130
|
+
clearVisible?: boolean;
|
|
131
|
+
/**
|
|
132
|
+
* A threshold N means that the onLoadNext function calls when a user scrolls all items except N.
|
|
133
|
+
* @default 10
|
|
134
|
+
*/
|
|
135
|
+
threshold?: number;
|
|
136
|
+
/**
|
|
137
|
+
* Defines how many items inside of the visible "window" to render.
|
|
138
|
+
* @default 6
|
|
139
|
+
*/
|
|
140
|
+
visibleCount?: number;
|
|
141
|
+
/**
|
|
142
|
+
* Defines how many items outside of the visible "window" to render at all times.
|
|
143
|
+
* @default 10
|
|
144
|
+
*/
|
|
145
|
+
overscanCount?: number;
|
|
146
|
+
/**
|
|
147
|
+
* The max number of options that the user can select. Zero means unlimited.
|
|
148
|
+
* Works only when multiple is true.
|
|
149
|
+
* @default 0
|
|
150
|
+
*/
|
|
151
|
+
maxSelectedItems?: number;
|
|
152
|
+
/**
|
|
153
|
+
* The locale.
|
|
154
|
+
* @default undefined
|
|
155
|
+
*/
|
|
156
|
+
locale?: SelectLocale;
|
|
157
|
+
/**
|
|
158
|
+
* Selected options.
|
|
159
|
+
* @default undefined
|
|
160
|
+
*/
|
|
161
|
+
value?: T;
|
|
162
|
+
/**
|
|
163
|
+
* The default value.
|
|
164
|
+
* @default undefined
|
|
165
|
+
*/
|
|
166
|
+
defaultValue?: T;
|
|
167
|
+
/**
|
|
168
|
+
* The change event handler.
|
|
169
|
+
* @default undefined
|
|
170
|
+
*/
|
|
171
|
+
onChange?: (value: T) => void;
|
|
172
|
+
/**
|
|
173
|
+
* The callback to load more items.
|
|
174
|
+
* @default undefined
|
|
175
|
+
*/
|
|
176
|
+
onLoadNext?: () => void;
|
|
177
|
+
/**
|
|
178
|
+
* The event handler that is called whenever a popup closes.
|
|
179
|
+
* @default undefined
|
|
180
|
+
*/
|
|
181
|
+
onClose?: () => void;
|
|
182
|
+
}
|
|
183
|
+
export interface SelectNotMultipleProps extends BaseSelectProps<string | null> {
|
|
184
|
+
/**
|
|
185
|
+
* Is it possible to select multiple values.
|
|
186
|
+
* @default false
|
|
187
|
+
*/
|
|
188
|
+
multiple?: false;
|
|
189
|
+
}
|
|
190
|
+
export interface SelectMultipleProps extends BaseSelectProps<string[]> {
|
|
191
|
+
/**
|
|
192
|
+
* Is it possible to select multiple values.
|
|
193
|
+
* @default false
|
|
194
|
+
*/
|
|
195
|
+
multiple: true;
|
|
196
|
+
}
|
|
197
|
+
export type SelectProps = SelectNotMultipleProps | SelectMultipleProps;
|
|
198
|
+
|
|
199
|
+
const selectContainerPaddingStyles = (p) => {
|
|
200
|
+
const paddingVertical =
|
|
201
|
+
(p.theme.baseHeight - p.theme.selectToggleListItemHeight) / 2;
|
|
202
|
+
|
|
203
|
+
return css`
|
|
204
|
+
padding: calc(${paddingVertical}em - 1px) 0;
|
|
205
|
+
`;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const selectContainerOpenedStyles = (p) =>
|
|
209
|
+
p.opened &&
|
|
210
|
+
!p.unbordered &&
|
|
211
|
+
css`
|
|
212
|
+
border-color: ${clr(p.theme.inputFocusColorBorder)};
|
|
213
|
+
box-shadow: 0 0 0 0.15em ${clr(p.theme.inputFocusColorShadow)};
|
|
214
|
+
`;
|
|
215
|
+
|
|
216
|
+
const selectContainerUnborderedStyles = (p) =>
|
|
217
|
+
p.unbordered &&
|
|
218
|
+
css`
|
|
219
|
+
border: 0;
|
|
220
|
+
box-shadow: none !important;
|
|
221
|
+
${transitionStyles('background-color')(p)};
|
|
222
|
+
`;
|
|
223
|
+
|
|
224
|
+
const selectContainerUnborderedHoverStyles = (p) =>
|
|
225
|
+
p.unbordered &&
|
|
226
|
+
!p.disabled &&
|
|
227
|
+
css`
|
|
228
|
+
@media (hover: hover) {
|
|
229
|
+
&:hover,
|
|
230
|
+
&:focus {
|
|
231
|
+
background-color: ${clr(p.theme.buttonGhostColorBgHover)};
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
`;
|
|
235
|
+
|
|
236
|
+
interface SelectContainerProps {
|
|
237
|
+
opened: boolean;
|
|
238
|
+
unbordered?: boolean;
|
|
239
|
+
disabled?: boolean;
|
|
240
|
+
}
|
|
241
|
+
export const SelectContainer = styled(
|
|
242
|
+
InputContainer,
|
|
243
|
+
omitEmotionProps('opened', 'unbordered', 'disabled')
|
|
244
|
+
)<SelectContainerProps>`
|
|
245
|
+
cursor: ${(p) => (!p.disabled ? 'pointer' : 'not-allowed')};
|
|
246
|
+
user-select: none;
|
|
247
|
+
position: relative;
|
|
248
|
+
display: flex;
|
|
249
|
+
align-items: center;
|
|
250
|
+
|
|
251
|
+
height: unset;
|
|
252
|
+
min-height: ${(p) => p.theme.baseHeight}em;
|
|
253
|
+
|
|
254
|
+
${selectContainerPaddingStyles};
|
|
255
|
+
${selectContainerOpenedStyles};
|
|
256
|
+
${selectContainerUnborderedStyles};
|
|
257
|
+
${selectContainerUnborderedHoverStyles};
|
|
258
|
+
`;
|
|
259
|
+
|
|
260
|
+
interface SelectMenuProps {
|
|
261
|
+
width: number;
|
|
262
|
+
}
|
|
263
|
+
const SelectMenu = styled(Menu, omitEmotionProps('width'))<SelectMenuProps>`
|
|
264
|
+
padding-top: 0;
|
|
265
|
+
padding-bottom: 0;
|
|
266
|
+
max-height: unset;
|
|
267
|
+
|
|
268
|
+
${m.min.xs} {
|
|
269
|
+
width: ${(p) => p.width}px;
|
|
270
|
+
}
|
|
271
|
+
`;
|
|
272
|
+
|
|
273
|
+
const NotFound = styled.div`
|
|
274
|
+
height: ${(p) => p.theme.menuItemHeight}em;
|
|
275
|
+
display: flex;
|
|
276
|
+
align-items: center;
|
|
277
|
+
|
|
278
|
+
padding: 0 ${(p) => p.theme.inputPaddingHorizontal}em;
|
|
279
|
+
color: ${(p) => clr(p.theme.selectNotFoundColorText)};
|
|
280
|
+
`;
|
|
281
|
+
|
|
282
|
+
const InputSearchContainer = styled.div`
|
|
283
|
+
padding: ${(p) => p.theme.menuPaddingVertical}em
|
|
284
|
+
${(p) => p.theme.inputPaddingHorizontal}em 0;
|
|
285
|
+
`;
|
|
286
|
+
|
|
287
|
+
export const ToggleContainer = styled.div`
|
|
288
|
+
flex: 1;
|
|
289
|
+
display: flex;
|
|
290
|
+
align-items: center;
|
|
291
|
+
overflow: hidden;
|
|
292
|
+
`;
|
|
293
|
+
|
|
294
|
+
const toggleContentNotHasLeftStyles = (p) =>
|
|
295
|
+
!p.hasLeft &&
|
|
296
|
+
css`
|
|
297
|
+
padding-left: ${p.theme.inputPaddingHorizontal}em;
|
|
298
|
+
`;
|
|
299
|
+
|
|
300
|
+
const toggleContentNotHasRightStyles = (p) =>
|
|
301
|
+
!p.hasRight &&
|
|
302
|
+
!p.unbordered &&
|
|
303
|
+
css`
|
|
304
|
+
padding-right: ${p.theme.inputPaddingHorizontal}em;
|
|
305
|
+
`;
|
|
306
|
+
|
|
307
|
+
interface ToggleContentProps {
|
|
308
|
+
hasLeft?: boolean;
|
|
309
|
+
hasRight?: boolean;
|
|
310
|
+
unbordered?: boolean;
|
|
311
|
+
}
|
|
312
|
+
export const ToggleContent = styled(
|
|
313
|
+
'div',
|
|
314
|
+
omitEmotionProps('hasLeft', 'hasRight', 'unbordered')
|
|
315
|
+
)<ToggleContentProps>`
|
|
316
|
+
flex: 1;
|
|
317
|
+
${toggleContentNotHasLeftStyles};
|
|
318
|
+
${toggleContentNotHasRightStyles};
|
|
319
|
+
${ellipsisStyles};
|
|
320
|
+
`;
|
|
321
|
+
|
|
322
|
+
export const Placeholder = styled.span`
|
|
323
|
+
color: ${(p) => clr(p.theme.inputColorPlaceholder)};
|
|
324
|
+
${ellipsisStyles};
|
|
325
|
+
`;
|
|
326
|
+
|
|
327
|
+
const titleUnborderedTitleStyles = (p) =>
|
|
328
|
+
p.unbordered &&
|
|
329
|
+
css`
|
|
330
|
+
font-weight: 500;
|
|
331
|
+
${!p.disabled && `color: ${clr(p.theme.colorPrimary)};`}
|
|
332
|
+
`;
|
|
333
|
+
|
|
334
|
+
const titleDisabledStyles = (p) =>
|
|
335
|
+
p.disabled &&
|
|
336
|
+
css`
|
|
337
|
+
color: ${clr(p.theme.inputDisabledColorText)};
|
|
338
|
+
`;
|
|
339
|
+
|
|
340
|
+
type TitleProps = Pick<SelectProps, 'disabled' | 'unbordered'>;
|
|
341
|
+
export const Title = styled(
|
|
342
|
+
'span',
|
|
343
|
+
omitEmotionProps('disabled', 'unbordered')
|
|
344
|
+
)<TitleProps>`
|
|
345
|
+
color: ${(p) => clr(p.theme.colorText)};
|
|
346
|
+
${titleUnborderedTitleStyles};
|
|
347
|
+
${titleDisabledStyles};
|
|
348
|
+
${ellipsisStyles};
|
|
349
|
+
`;
|
|
350
|
+
|
|
351
|
+
type ToggleListItemProps = Pick<SelectProps, 'disabled'>;
|
|
352
|
+
const ToggleListItem = styled(
|
|
353
|
+
Tag,
|
|
354
|
+
omitEmotionProps('disabled')
|
|
355
|
+
)<ToggleListItemProps>`
|
|
356
|
+
// Reset tag styles
|
|
357
|
+
padding-top: 0;
|
|
358
|
+
padding-bottom: 0;
|
|
359
|
+
|
|
360
|
+
height: ${(p) => p.theme.selectToggleListItemHeight}em;
|
|
361
|
+
${titleDisabledStyles};
|
|
362
|
+
`;
|
|
363
|
+
|
|
364
|
+
const ToggleList = styled.div`
|
|
365
|
+
display: flex;
|
|
366
|
+
flex-wrap: wrap;
|
|
367
|
+
overflow: hidden; // For ellipsis styles
|
|
368
|
+
|
|
369
|
+
margin: 0 ${(p) => p.theme.selectToggleListItemGap}em
|
|
370
|
+
${(p) => -p.theme.selectToggleListItemGap}em 0;
|
|
371
|
+
|
|
372
|
+
& > div {
|
|
373
|
+
margin: 0 ${(p) => p.theme.selectToggleListItemGap}em
|
|
374
|
+
${(p) => p.theme.selectToggleListItemGap}em 0;
|
|
375
|
+
}
|
|
376
|
+
`;
|
|
377
|
+
|
|
378
|
+
const DeleteButton = styled.button`
|
|
379
|
+
${resetButtonStyles};
|
|
380
|
+
cursor: pointer;
|
|
381
|
+
display: inherit;
|
|
382
|
+
font-size: 1em;
|
|
383
|
+
margin-left: 0.2em;
|
|
384
|
+
|
|
385
|
+
background-color: transparent;
|
|
386
|
+
color: ${(p) => clr(p.theme.selectToggleDeleteButtonColorIcon)};
|
|
387
|
+
|
|
388
|
+
@media (hover: hover) {
|
|
389
|
+
&:hover,
|
|
390
|
+
&:focus {
|
|
391
|
+
color: ${(p) => clr(p.theme.selectToggleDeleteButtonColorIconHover)};
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
${transitionStyles('color')};
|
|
396
|
+
`;
|
|
397
|
+
|
|
398
|
+
const toggleIconUnborderedStyles = (p) =>
|
|
399
|
+
p.unbordered &&
|
|
400
|
+
css`
|
|
401
|
+
padding-top: 0.2em;
|
|
402
|
+
font-size: 0.8em;
|
|
403
|
+
${!p.disabled && `color: ${clr(p.theme.colorPrimary)};`}
|
|
404
|
+
`;
|
|
405
|
+
|
|
406
|
+
interface ToggleIconContainerProps {
|
|
407
|
+
unbordered?: boolean;
|
|
408
|
+
disabled?: boolean;
|
|
409
|
+
}
|
|
410
|
+
export const ToggleIconContainer = styled(
|
|
411
|
+
'span',
|
|
412
|
+
omitEmotionProps('unbordered', 'disabled')
|
|
413
|
+
)<ToggleIconContainerProps>`
|
|
414
|
+
color: ${(p) => clr(p.theme.selectColorIcon)};
|
|
415
|
+
line-height: 1;
|
|
416
|
+
${toggleIconUnborderedStyles};
|
|
417
|
+
`;
|
|
418
|
+
|
|
419
|
+
export const ClearIcon = styled(CloseCircle)`
|
|
420
|
+
transform: scale(1.2) !important;
|
|
421
|
+
`;
|
|
422
|
+
|
|
423
|
+
interface AddonProps {
|
|
424
|
+
hasPadding: boolean;
|
|
425
|
+
}
|
|
426
|
+
const Addon = styled('span', omitEmotionProps('hasPadding'))<AddonProps>`
|
|
427
|
+
display: flex;
|
|
428
|
+
align-items: center;
|
|
429
|
+
user-select: none;
|
|
430
|
+
color: ${(p) => clr(p.theme.inputColorPlaceholder)};
|
|
431
|
+
|
|
432
|
+
svg {
|
|
433
|
+
transform: scale(1.2);
|
|
434
|
+
}
|
|
435
|
+
`;
|
|
436
|
+
|
|
437
|
+
export const ToggleLeftAddon = styled(Addon)`
|
|
438
|
+
padding-right: ${(p) => p.theme.inputAddonPaddingHorizontal}em;
|
|
439
|
+
${(p) =>
|
|
440
|
+
p.hasPadding &&
|
|
441
|
+
css`
|
|
442
|
+
padding-left: ${p.theme.inputPaddingHorizontal}em;
|
|
443
|
+
`}
|
|
444
|
+
`;
|
|
445
|
+
|
|
446
|
+
export const ToggleRightAddon = styled(Addon)`
|
|
447
|
+
padding-left: ${(p) => p.theme.inputAddonPaddingHorizontal}em;
|
|
448
|
+
${(p) =>
|
|
449
|
+
p.hasPadding &&
|
|
450
|
+
css`
|
|
451
|
+
padding-right: ${p.theme.inputPaddingHorizontal}em;
|
|
452
|
+
`}
|
|
453
|
+
`;
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* The component that allows to pick a value from predefined options.
|
|
457
|
+
*/
|
|
458
|
+
const Select = forwardRef<HTMLDivElement, SelectProps>(
|
|
459
|
+
(
|
|
460
|
+
{
|
|
461
|
+
options = [],
|
|
462
|
+
left,
|
|
463
|
+
leftHasPadding = false,
|
|
464
|
+
right,
|
|
465
|
+
rightHasPadding = false,
|
|
466
|
+
placeholder,
|
|
467
|
+
searchVisible = false,
|
|
468
|
+
searchProps = {},
|
|
469
|
+
notFoundText = 'Not found',
|
|
470
|
+
unbordered = false,
|
|
471
|
+
loading = false,
|
|
472
|
+
disabled = false,
|
|
473
|
+
autoFocus = false,
|
|
474
|
+
autoOpen = false,
|
|
475
|
+
clearVisible = false,
|
|
476
|
+
threshold = 10,
|
|
477
|
+
visibleCount = 6,
|
|
478
|
+
overscanCount = 10,
|
|
479
|
+
multiple = false,
|
|
480
|
+
maxSelectedItems = 0,
|
|
481
|
+
locale = defaultLocale,
|
|
482
|
+
value,
|
|
483
|
+
defaultValue,
|
|
484
|
+
onChange,
|
|
485
|
+
onLoadNext = () => {},
|
|
486
|
+
onClose = () => {},
|
|
487
|
+
onBlur = () => {},
|
|
488
|
+
size,
|
|
489
|
+
placement,
|
|
490
|
+
...rest
|
|
491
|
+
},
|
|
492
|
+
ref
|
|
493
|
+
) => {
|
|
494
|
+
const [containerRef, mergedContainerRef] = useForwardedRef(ref);
|
|
495
|
+
const [width, setWidth] = useState(0);
|
|
496
|
+
const inputSearchContainerRef = useRef<HTMLDivElement>(null);
|
|
497
|
+
const [opened, setOpened] = useState(autoOpen);
|
|
498
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
499
|
+
const [forwardedValue, setForwardedValue] = useForwardedState<any>({
|
|
500
|
+
value,
|
|
501
|
+
defaultValue,
|
|
502
|
+
onChange,
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
useEffect(() => {
|
|
506
|
+
if (autoFocus) containerRef.current?.focus();
|
|
507
|
+
}, [autoFocus, containerRef]);
|
|
508
|
+
|
|
509
|
+
const onCloseRef = useRef(onClose);
|
|
510
|
+
useEffect(() => {
|
|
511
|
+
onCloseRef.current = onClose;
|
|
512
|
+
}, [onClose]);
|
|
513
|
+
|
|
514
|
+
useEffect(() => {
|
|
515
|
+
if (!opened) onCloseRef.current();
|
|
516
|
+
}, [opened]);
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Detect the width of the container when the select was opened and update
|
|
520
|
+
* it when either the container size or the window size has been changed.
|
|
521
|
+
*/
|
|
522
|
+
const resizeHandler = useCallback(() => {
|
|
523
|
+
window.requestAnimationFrame(() => {
|
|
524
|
+
if (!opened || !containerRef.current) return;
|
|
525
|
+
const nextWidth = containerRef.current.getBoundingClientRect().width;
|
|
526
|
+
if (width === nextWidth) return;
|
|
527
|
+
setWidth(nextWidth);
|
|
528
|
+
});
|
|
529
|
+
}, [opened, containerRef, width]);
|
|
530
|
+
useBrowserLayoutEffect(() => resizeHandler(), [resizeHandler]);
|
|
531
|
+
useResizeObserver(containerRef, resizeHandler);
|
|
532
|
+
useEvent(
|
|
533
|
+
(typeof window !== 'undefined' ? window : undefined) as EventTarget,
|
|
534
|
+
'resize',
|
|
535
|
+
resizeHandler
|
|
536
|
+
);
|
|
537
|
+
|
|
538
|
+
// Replace the aria-haspopup attribute from menu to listbox
|
|
539
|
+
useBrowserLayoutEffect(() => {
|
|
540
|
+
if (!containerRef.current) return;
|
|
541
|
+
containerRef.current.setAttribute('aria-haspopup', 'listbox');
|
|
542
|
+
}, []);
|
|
543
|
+
|
|
544
|
+
const listBoxId = useMemo(
|
|
545
|
+
() => `listbox-${Math.random().toString(36).slice(2, 11)}`,
|
|
546
|
+
[]
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
const blurHandler = useCallback(
|
|
550
|
+
(e) => {
|
|
551
|
+
if (!opened) onBlur(e);
|
|
552
|
+
},
|
|
553
|
+
[onBlur, opened]
|
|
554
|
+
);
|
|
555
|
+
|
|
556
|
+
const onDelete = useCallback(
|
|
557
|
+
(v: string) => {
|
|
558
|
+
if (!multiple) return;
|
|
559
|
+
setForwardedValue((forwardedValue || []).filter((item) => item !== v));
|
|
560
|
+
},
|
|
561
|
+
[forwardedValue, multiple, setForwardedValue]
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
const toggleContent = useMemo(() => {
|
|
565
|
+
if (multiple) {
|
|
566
|
+
if (!forwardedValue || forwardedValue.length === 0) {
|
|
567
|
+
return <Placeholder>{placeholder}</Placeholder>;
|
|
568
|
+
}
|
|
569
|
+
const items = forwardedValue.map((v) => {
|
|
570
|
+
const option = (options || []).find((item) => item.value === v);
|
|
571
|
+
return { title: option ? option.title || '' : '', value: v };
|
|
572
|
+
});
|
|
573
|
+
return (
|
|
574
|
+
<ToggleList>
|
|
575
|
+
{items.map(({ title, value: v }) => (
|
|
576
|
+
<ToggleListItem
|
|
577
|
+
key={v}
|
|
578
|
+
disabled={disabled}
|
|
579
|
+
right={
|
|
580
|
+
!disabled ? (
|
|
581
|
+
<DeleteButton
|
|
582
|
+
onClick={(e) => {
|
|
583
|
+
onDelete(v);
|
|
584
|
+
e.stopPropagation();
|
|
585
|
+
}}
|
|
586
|
+
onKeyDown={(e) => e.stopPropagation()}
|
|
587
|
+
aria-label={`${locale.deleteLabel} ${title}`}
|
|
588
|
+
aria-hidden
|
|
589
|
+
>
|
|
590
|
+
<Close />
|
|
591
|
+
</DeleteButton>
|
|
592
|
+
) : undefined
|
|
593
|
+
}
|
|
594
|
+
aria-hidden
|
|
595
|
+
>
|
|
596
|
+
{title}
|
|
597
|
+
</ToggleListItem>
|
|
598
|
+
))}
|
|
599
|
+
</ToggleList>
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
if (!forwardedValue) {
|
|
603
|
+
return <Placeholder>{placeholder}</Placeholder>;
|
|
604
|
+
}
|
|
605
|
+
const option = (options || []).find(
|
|
606
|
+
(item) => item.value === forwardedValue
|
|
607
|
+
);
|
|
608
|
+
return (
|
|
609
|
+
<Title disabled={disabled} unbordered={unbordered}>
|
|
610
|
+
{option ? option.title : ''}
|
|
611
|
+
</Title>
|
|
612
|
+
);
|
|
613
|
+
}, [
|
|
614
|
+
disabled,
|
|
615
|
+
forwardedValue,
|
|
616
|
+
locale.deleteLabel,
|
|
617
|
+
multiple,
|
|
618
|
+
onDelete,
|
|
619
|
+
options,
|
|
620
|
+
placeholder,
|
|
621
|
+
unbordered,
|
|
622
|
+
]);
|
|
623
|
+
|
|
624
|
+
const toggleShowClearButton = useMemo(() => {
|
|
625
|
+
if (!clearVisible) return false;
|
|
626
|
+
if (multiple) return forwardedValue && forwardedValue.length > 0;
|
|
627
|
+
return !!forwardedValue;
|
|
628
|
+
}, [clearVisible, forwardedValue, multiple]);
|
|
629
|
+
|
|
630
|
+
const toggleOnClear = useCallback(() => {
|
|
631
|
+
setForwardedValue(multiple ? [] : null);
|
|
632
|
+
if (!containerRef.current) return;
|
|
633
|
+
containerRef.current.focus();
|
|
634
|
+
}, [containerRef, multiple, setForwardedValue]);
|
|
635
|
+
|
|
636
|
+
const toggleRightValue = useMemo(() => {
|
|
637
|
+
if (loading) return <Loading />;
|
|
638
|
+
if (toggleShowClearButton) {
|
|
639
|
+
return (
|
|
640
|
+
<Button
|
|
641
|
+
type='ghost'
|
|
642
|
+
wide='never'
|
|
643
|
+
size='small'
|
|
644
|
+
disabled={disabled}
|
|
645
|
+
onClick={(e) => {
|
|
646
|
+
toggleOnClear();
|
|
647
|
+
e.stopPropagation();
|
|
648
|
+
}}
|
|
649
|
+
onKeyDown={(e) => {
|
|
650
|
+
if (e.key === 'Enter') toggleOnClear();
|
|
651
|
+
e.stopPropagation();
|
|
652
|
+
}}
|
|
653
|
+
aria-label={locale.clearLabel}
|
|
654
|
+
>
|
|
655
|
+
<ClearIcon />
|
|
656
|
+
</Button>
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
return (
|
|
660
|
+
right || (
|
|
661
|
+
<ToggleIconContainer unbordered={unbordered} disabled={disabled}>
|
|
662
|
+
{opened ? <Up /> : <Down />}
|
|
663
|
+
</ToggleIconContainer>
|
|
664
|
+
)
|
|
665
|
+
);
|
|
666
|
+
}, [
|
|
667
|
+
disabled,
|
|
668
|
+
loading,
|
|
669
|
+
locale.clearLabel,
|
|
670
|
+
opened,
|
|
671
|
+
right,
|
|
672
|
+
toggleOnClear,
|
|
673
|
+
toggleShowClearButton,
|
|
674
|
+
unbordered,
|
|
675
|
+
]);
|
|
676
|
+
|
|
677
|
+
const toggleRightHasPaddingValue = useMemo(() => {
|
|
678
|
+
if (loading) return true;
|
|
679
|
+
if (toggleShowClearButton) return false;
|
|
680
|
+
return right ? rightHasPadding : true;
|
|
681
|
+
}, [loading, right, rightHasPadding, toggleShowClearButton]);
|
|
682
|
+
|
|
683
|
+
const onSelect = useCallback(
|
|
684
|
+
(v: string) => {
|
|
685
|
+
if (multiple) {
|
|
686
|
+
// Delete the value because it was already selected
|
|
687
|
+
if ((forwardedValue || []).includes(v)) {
|
|
688
|
+
setForwardedValue(
|
|
689
|
+
(forwardedValue || []).filter((item) => item !== v)
|
|
690
|
+
);
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// Add a new value if the number of selected items is less than max
|
|
695
|
+
if (
|
|
696
|
+
maxSelectedItems === 0 ||
|
|
697
|
+
(forwardedValue || []).length < maxSelectedItems
|
|
698
|
+
) {
|
|
699
|
+
setForwardedValue([...(forwardedValue || []), v]);
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
setForwardedValue(v);
|
|
705
|
+
},
|
|
706
|
+
[forwardedValue, maxSelectedItems, multiple, setForwardedValue]
|
|
707
|
+
);
|
|
708
|
+
|
|
709
|
+
const windowSize = useSize();
|
|
710
|
+
const isMinXs = useIsMinWidth('xs');
|
|
711
|
+
const fontSize = useFontSize(document.body);
|
|
712
|
+
const { theme } = useTheme();
|
|
713
|
+
|
|
714
|
+
const scaleFactor = useMemo(() => {
|
|
715
|
+
const s = size || 'medium';
|
|
716
|
+
return ['small', 'medium', 'large'].includes(s)
|
|
717
|
+
? theme.sizes[s]
|
|
718
|
+
: Number(s.replace(/^([0-9]+(\.[0-9]+)?).*/, '$1')) || 1; // Extract the number
|
|
719
|
+
}, [size, theme.sizes]);
|
|
720
|
+
|
|
721
|
+
const paddingBottom = useMemo(() => {
|
|
722
|
+
const paddingEm = isMinXs
|
|
723
|
+
? theme.menuPaddingVertical
|
|
724
|
+
: theme.modalBodyPaddingVertical[0];
|
|
725
|
+
return paddingEm * fontSize * scaleFactor;
|
|
726
|
+
}, [
|
|
727
|
+
isMinXs,
|
|
728
|
+
theme.menuPaddingVertical,
|
|
729
|
+
theme.modalBodyPaddingVertical,
|
|
730
|
+
fontSize,
|
|
731
|
+
scaleFactor,
|
|
732
|
+
]);
|
|
733
|
+
|
|
734
|
+
const paddingTop = useMemo(
|
|
735
|
+
() => (searchVisible ? 5 * scaleFactor : paddingBottom),
|
|
736
|
+
[searchVisible, scaleFactor, paddingBottom]
|
|
737
|
+
);
|
|
738
|
+
|
|
739
|
+
const searchInputHeight = useMemo(
|
|
740
|
+
() =>
|
|
741
|
+
searchVisible
|
|
742
|
+
? (theme.baseHeight + theme.menuPaddingVertical) *
|
|
743
|
+
fontSize *
|
|
744
|
+
scaleFactor
|
|
745
|
+
: 0,
|
|
746
|
+
[
|
|
747
|
+
searchVisible,
|
|
748
|
+
theme.baseHeight,
|
|
749
|
+
theme.menuPaddingVertical,
|
|
750
|
+
fontSize,
|
|
751
|
+
scaleFactor,
|
|
752
|
+
]
|
|
753
|
+
);
|
|
754
|
+
|
|
755
|
+
const itemSize = useMemo(
|
|
756
|
+
() => theme.menuItemHeight * fontSize * scaleFactor,
|
|
757
|
+
[theme.menuItemHeight, fontSize, scaleFactor]
|
|
758
|
+
);
|
|
759
|
+
|
|
760
|
+
const height = useMemo(() => {
|
|
761
|
+
// Modal
|
|
762
|
+
if (!isMinXs) {
|
|
763
|
+
const maxHeight =
|
|
764
|
+
windowSize.height -
|
|
765
|
+
theme.modalHeaderHeight * fontSize * scaleFactor -
|
|
766
|
+
searchInputHeight;
|
|
767
|
+
const curHeight =
|
|
768
|
+
options.length * itemSize + paddingTop + paddingBottom;
|
|
769
|
+
return curHeight < maxHeight ? curHeight : maxHeight;
|
|
770
|
+
}
|
|
771
|
+
// Popover
|
|
772
|
+
const count =
|
|
773
|
+
options.length < visibleCount ? options.length : visibleCount;
|
|
774
|
+
return count * itemSize + paddingTop + paddingBottom;
|
|
775
|
+
}, [
|
|
776
|
+
isMinXs,
|
|
777
|
+
options.length,
|
|
778
|
+
visibleCount,
|
|
779
|
+
itemSize,
|
|
780
|
+
windowSize.height,
|
|
781
|
+
theme.modalHeaderHeight,
|
|
782
|
+
fontSize,
|
|
783
|
+
scaleFactor,
|
|
784
|
+
searchInputHeight,
|
|
785
|
+
paddingTop,
|
|
786
|
+
paddingBottom,
|
|
787
|
+
]);
|
|
788
|
+
|
|
789
|
+
const scrollHandler = useRWLoadNext({
|
|
790
|
+
itemCount: options.length,
|
|
791
|
+
threshold,
|
|
792
|
+
itemSize,
|
|
793
|
+
paddingTop,
|
|
794
|
+
height,
|
|
795
|
+
onLoadNext,
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
799
|
+
const InnerElement = useCallback(
|
|
800
|
+
({ style, ...innerElementRest }) => (
|
|
801
|
+
<div
|
|
802
|
+
style={{
|
|
803
|
+
...style,
|
|
804
|
+
height: `${
|
|
805
|
+
parseFloat(style.height) + paddingTop + paddingBottom
|
|
806
|
+
}px`,
|
|
807
|
+
}}
|
|
808
|
+
role='listbox'
|
|
809
|
+
id={listBoxId}
|
|
810
|
+
{...innerElementRest}
|
|
811
|
+
/>
|
|
812
|
+
),
|
|
813
|
+
[listBoxId, paddingBottom, paddingTop]
|
|
814
|
+
);
|
|
815
|
+
|
|
816
|
+
const listItemFn = useCallback(
|
|
817
|
+
({ index, style }) => {
|
|
818
|
+
const {
|
|
819
|
+
title: optionTitle,
|
|
820
|
+
value: optionValue,
|
|
821
|
+
onClick: optionOnClick,
|
|
822
|
+
...restOption
|
|
823
|
+
} = options[index];
|
|
824
|
+
return (
|
|
825
|
+
<MenuItem
|
|
826
|
+
style={{
|
|
827
|
+
...style,
|
|
828
|
+
top: `${
|
|
829
|
+
parseFloat(style.top ? style.top.toString() : '0') + paddingTop
|
|
830
|
+
}px`,
|
|
831
|
+
}}
|
|
832
|
+
selected={
|
|
833
|
+
(multiple &&
|
|
834
|
+
(forwardedValue || []).includes(optionValue || '')) ||
|
|
835
|
+
(!multiple && forwardedValue === optionValue)
|
|
836
|
+
}
|
|
837
|
+
onClick={(e) => {
|
|
838
|
+
if (!optionValue) return;
|
|
839
|
+
onSelect(optionValue);
|
|
840
|
+
|
|
841
|
+
// Focus the input.
|
|
842
|
+
// Otherwise, if multiple is false and the user presses enter to select an item,
|
|
843
|
+
// the input will lose focus.
|
|
844
|
+
if (containerRef.current && !multiple) {
|
|
845
|
+
containerRef.current.focus();
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
if (optionOnClick) optionOnClick(e);
|
|
849
|
+
}}
|
|
850
|
+
role='option'
|
|
851
|
+
aria-selected={
|
|
852
|
+
(multiple &&
|
|
853
|
+
(forwardedValue || []).includes(optionValue || '')) ||
|
|
854
|
+
(!multiple && forwardedValue === optionValue)
|
|
855
|
+
}
|
|
856
|
+
{...restOption}
|
|
857
|
+
>
|
|
858
|
+
{optionTitle}
|
|
859
|
+
</MenuItem>
|
|
860
|
+
);
|
|
861
|
+
},
|
|
862
|
+
[containerRef, forwardedValue, multiple, onSelect, options, paddingTop]
|
|
863
|
+
);
|
|
864
|
+
|
|
865
|
+
return (
|
|
866
|
+
<>
|
|
867
|
+
<SelectContainer
|
|
868
|
+
opened={opened}
|
|
869
|
+
unbordered={unbordered}
|
|
870
|
+
disabled={disabled}
|
|
871
|
+
size={size}
|
|
872
|
+
tabIndex={!disabled ? 0 : -1}
|
|
873
|
+
onClick={() => {
|
|
874
|
+
if (disabled) return;
|
|
875
|
+
setOpened(!opened);
|
|
876
|
+
}}
|
|
877
|
+
onKeyDown={(e) => {
|
|
878
|
+
if (disabled) return;
|
|
879
|
+
if (['Enter', ' '].includes(e.key)) {
|
|
880
|
+
setOpened(!opened);
|
|
881
|
+
e.preventDefault();
|
|
882
|
+
}
|
|
883
|
+
}}
|
|
884
|
+
onMouseDown={(e) => e.preventDefault()}
|
|
885
|
+
onBlur={blurHandler}
|
|
886
|
+
role='combobox'
|
|
887
|
+
aria-disabled={disabled}
|
|
888
|
+
aria-busy={loading}
|
|
889
|
+
aria-haspopup='listbox'
|
|
890
|
+
aria-owns={listBoxId}
|
|
891
|
+
{...rest}
|
|
892
|
+
ref={mergedContainerRef}
|
|
893
|
+
>
|
|
894
|
+
<ToggleContainer>
|
|
895
|
+
{left && (
|
|
896
|
+
<ThemeOverrider
|
|
897
|
+
overrides={(t) => ({
|
|
898
|
+
buttonPaddingHorizontal: 0.8,
|
|
899
|
+
baseHeight: t.selectToggleListItemHeight / t.sizes.small,
|
|
900
|
+
})}
|
|
901
|
+
>
|
|
902
|
+
<ToggleLeftAddon hasPadding={leftHasPadding}>
|
|
903
|
+
{left}
|
|
904
|
+
</ToggleLeftAddon>
|
|
905
|
+
</ThemeOverrider>
|
|
906
|
+
)}
|
|
907
|
+
|
|
908
|
+
<ToggleContent
|
|
909
|
+
hasLeft={!!left}
|
|
910
|
+
hasRight={!!right}
|
|
911
|
+
unbordered={unbordered}
|
|
912
|
+
>
|
|
913
|
+
{toggleContent}
|
|
914
|
+
</ToggleContent>
|
|
915
|
+
|
|
916
|
+
{toggleRightValue && (
|
|
917
|
+
<ThemeOverrider
|
|
918
|
+
overrides={(t) => ({
|
|
919
|
+
buttonPaddingHorizontal: 0.8,
|
|
920
|
+
baseHeight: t.selectToggleListItemHeight / t.sizes.small,
|
|
921
|
+
})}
|
|
922
|
+
>
|
|
923
|
+
<ToggleRightAddon hasPadding={toggleRightHasPaddingValue}>
|
|
924
|
+
{toggleRightValue}
|
|
925
|
+
</ToggleRightAddon>
|
|
926
|
+
</ThemeOverrider>
|
|
927
|
+
)}
|
|
928
|
+
</ToggleContainer>
|
|
929
|
+
</SelectContainer>
|
|
930
|
+
|
|
931
|
+
<SelectMenu
|
|
932
|
+
trigger={containerRef}
|
|
933
|
+
visible={opened}
|
|
934
|
+
onClose={() => setOpened(false)}
|
|
935
|
+
size={size}
|
|
936
|
+
width={width}
|
|
937
|
+
closeOnSelect={!multiple}
|
|
938
|
+
modalTitle={placeholder}
|
|
939
|
+
placement={placement}
|
|
940
|
+
>
|
|
941
|
+
{searchVisible && (
|
|
942
|
+
<InputSearchContainer ref={inputSearchContainerRef}>
|
|
943
|
+
<InputSearch {...searchProps} />
|
|
944
|
+
</InputSearchContainer>
|
|
945
|
+
)}
|
|
946
|
+
|
|
947
|
+
{options.length > 0 ? (
|
|
948
|
+
<FixedSizeList
|
|
949
|
+
width='100%'
|
|
950
|
+
height={height}
|
|
951
|
+
itemSize={itemSize}
|
|
952
|
+
itemCount={options.length}
|
|
953
|
+
overscanCount={overscanCount}
|
|
954
|
+
onScroll={({ scrollOffset }) => scrollHandler(scrollOffset)}
|
|
955
|
+
innerElementType={InnerElement}
|
|
956
|
+
>
|
|
957
|
+
{listItemFn}
|
|
958
|
+
</FixedSizeList>
|
|
959
|
+
) : (
|
|
960
|
+
<NotFound>{notFoundText}</NotFound>
|
|
961
|
+
)}
|
|
962
|
+
</SelectMenu>
|
|
963
|
+
</>
|
|
964
|
+
);
|
|
965
|
+
}
|
|
966
|
+
);
|
|
967
|
+
|
|
968
|
+
Select.displayName = 'Select';
|
|
969
|
+
|
|
970
|
+
export default Select;
|