@workday/canvas-kit-preview-react 15.0.0-alpha.0075-next.0 → 15.0.0-alpha.0077-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commonjs/index.d.ts +1 -2
- package/dist/commonjs/index.d.ts.map +1 -1
- package/dist/commonjs/index.js +1 -2
- package/dist/es6/index.d.ts +1 -2
- package/dist/es6/index.d.ts.map +1 -1
- package/dist/es6/index.js +1 -2
- package/index.ts +1 -2
- package/package.json +4 -4
- package/avatar/index.ts +0 -5
- package/avatar/lib/Avatar.tsx +0 -156
- package/avatar/lib/AvatarImage.tsx +0 -20
- package/avatar/lib/AvatarName.tsx +0 -32
- package/avatar/lib/BaseAvatar.tsx +0 -127
- package/avatar/lib/getInitialsFromName.ts +0 -11
- package/avatar/package.json +0 -6
- package/dist/commonjs/avatar/index.d.ts +0 -6
- package/dist/commonjs/avatar/index.d.ts.map +0 -1
- package/dist/commonjs/avatar/index.js +0 -21
- package/dist/commonjs/avatar/lib/Avatar.d.ts +0 -281
- package/dist/commonjs/avatar/lib/Avatar.d.ts.map +0 -1
- package/dist/commonjs/avatar/lib/Avatar.js +0 -53
- package/dist/commonjs/avatar/lib/AvatarImage.d.ts +0 -6
- package/dist/commonjs/avatar/lib/AvatarImage.d.ts.map +0 -1
- package/dist/commonjs/avatar/lib/AvatarImage.js +0 -15
- package/dist/commonjs/avatar/lib/AvatarName.d.ts +0 -14
- package/dist/commonjs/avatar/lib/AvatarName.d.ts.map +0 -1
- package/dist/commonjs/avatar/lib/AvatarName.js +0 -16
- package/dist/commonjs/avatar/lib/BaseAvatar.d.ts +0 -148
- package/dist/commonjs/avatar/lib/BaseAvatar.d.ts.map +0 -1
- package/dist/commonjs/avatar/lib/BaseAvatar.js +0 -47
- package/dist/commonjs/avatar/lib/getInitialsFromName.d.ts +0 -2
- package/dist/commonjs/avatar/lib/getInitialsFromName.d.ts.map +0 -1
- package/dist/commonjs/avatar/lib/getInitialsFromName.js +0 -13
- package/dist/commonjs/select/index.d.ts +0 -3
- package/dist/commonjs/select/index.d.ts.map +0 -1
- package/dist/commonjs/select/index.js +0 -17
- package/dist/commonjs/select/lib/Select.d.ts +0 -31
- package/dist/commonjs/select/lib/Select.d.ts.map +0 -1
- package/dist/commonjs/select/lib/Select.js +0 -486
- package/dist/commonjs/select/lib/SelectBase.d.ts +0 -165
- package/dist/commonjs/select/lib/SelectBase.d.ts.map +0 -1
- package/dist/commonjs/select/lib/SelectBase.js +0 -235
- package/dist/commonjs/select/lib/SelectMenu.d.ts +0 -45
- package/dist/commonjs/select/lib/SelectMenu.d.ts.map +0 -1
- package/dist/commonjs/select/lib/SelectMenu.js +0 -195
- package/dist/commonjs/select/lib/SelectOption.d.ts +0 -38
- package/dist/commonjs/select/lib/SelectOption.d.ts.map +0 -1
- package/dist/commonjs/select/lib/SelectOption.js +0 -73
- package/dist/commonjs/select/lib/scrolling.d.ts +0 -5
- package/dist/commonjs/select/lib/scrolling.d.ts.map +0 -1
- package/dist/commonjs/select/lib/scrolling.js +0 -36
- package/dist/commonjs/select/lib/types.d.ts +0 -37
- package/dist/commonjs/select/lib/types.d.ts.map +0 -1
- package/dist/commonjs/select/lib/types.js +0 -2
- package/dist/commonjs/select/lib/utils.d.ts +0 -10
- package/dist/commonjs/select/lib/utils.d.ts.map +0 -1
- package/dist/commonjs/select/lib/utils.js +0 -27
- package/dist/es6/avatar/index.d.ts +0 -6
- package/dist/es6/avatar/index.d.ts.map +0 -1
- package/dist/es6/avatar/index.js +0 -5
- package/dist/es6/avatar/lib/Avatar.d.ts +0 -281
- package/dist/es6/avatar/lib/Avatar.d.ts.map +0 -1
- package/dist/es6/avatar/lib/Avatar.js +0 -47
- package/dist/es6/avatar/lib/AvatarImage.d.ts +0 -6
- package/dist/es6/avatar/lib/AvatarImage.d.ts.map +0 -1
- package/dist/es6/avatar/lib/AvatarImage.js +0 -12
- package/dist/es6/avatar/lib/AvatarName.d.ts +0 -14
- package/dist/es6/avatar/lib/AvatarName.d.ts.map +0 -1
- package/dist/es6/avatar/lib/AvatarName.js +0 -13
- package/dist/es6/avatar/lib/BaseAvatar.d.ts +0 -148
- package/dist/es6/avatar/lib/BaseAvatar.d.ts.map +0 -1
- package/dist/es6/avatar/lib/BaseAvatar.js +0 -44
- package/dist/es6/avatar/lib/getInitialsFromName.d.ts +0 -2
- package/dist/es6/avatar/lib/getInitialsFromName.d.ts.map +0 -1
- package/dist/es6/avatar/lib/getInitialsFromName.js +0 -9
- package/dist/es6/select/index.d.ts +0 -3
- package/dist/es6/select/index.d.ts.map +0 -1
- package/dist/es6/select/index.js +0 -1
- package/dist/es6/select/lib/Select.d.ts +0 -31
- package/dist/es6/select/lib/Select.d.ts.map +0 -1
- package/dist/es6/select/lib/Select.js +0 -460
- package/dist/es6/select/lib/SelectBase.d.ts +0 -165
- package/dist/es6/select/lib/SelectBase.d.ts.map +0 -1
- package/dist/es6/select/lib/SelectBase.js +0 -208
- package/dist/es6/select/lib/SelectMenu.d.ts +0 -45
- package/dist/es6/select/lib/SelectMenu.d.ts.map +0 -1
- package/dist/es6/select/lib/SelectMenu.js +0 -191
- package/dist/es6/select/lib/SelectOption.d.ts +0 -38
- package/dist/es6/select/lib/SelectOption.d.ts.map +0 -1
- package/dist/es6/select/lib/SelectOption.js +0 -69
- package/dist/es6/select/lib/scrolling.d.ts +0 -5
- package/dist/es6/select/lib/scrolling.d.ts.map +0 -1
- package/dist/es6/select/lib/scrolling.js +0 -32
- package/dist/es6/select/lib/types.d.ts +0 -37
- package/dist/es6/select/lib/types.d.ts.map +0 -1
- package/dist/es6/select/lib/types.js +0 -1
- package/dist/es6/select/lib/utils.d.ts +0 -10
- package/dist/es6/select/lib/utils.d.ts.map +0 -1
- package/dist/es6/select/lib/utils.js +0 -22
- package/select/index.ts +0 -8
- package/select/lib/Select.tsx +0 -595
- package/select/lib/SelectBase.tsx +0 -493
- package/select/lib/SelectMenu.tsx +0 -304
- package/select/lib/SelectOption.tsx +0 -133
- package/select/lib/scrolling.ts +0 -42
- package/select/lib/types.ts +0 -37
- package/select/lib/utils.ts +0 -30
- package/select/package.json +0 -6
|
@@ -1,493 +0,0 @@
|
|
|
1
|
-
import React, {useLayoutEffect} from 'react';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
GrowthBehavior,
|
|
5
|
-
ErrorType,
|
|
6
|
-
StyledType,
|
|
7
|
-
Themeable,
|
|
8
|
-
errorRing,
|
|
9
|
-
styled,
|
|
10
|
-
useUniqueId,
|
|
11
|
-
} from '@workday/canvas-kit-react/common';
|
|
12
|
-
import {colors, borderRadius, inputColors, type, space} from '@workday/canvas-kit-react/tokens';
|
|
13
|
-
import {caretDownSmallIcon} from '@workday/canvas-system-icons-web';
|
|
14
|
-
import {SystemIcon} from '@workday/canvas-kit-react/icon';
|
|
15
|
-
|
|
16
|
-
import {SelectMenu} from './SelectMenu';
|
|
17
|
-
import {SelectOption} from './SelectOption';
|
|
18
|
-
import {scrollIntoViewIfNeeded} from './scrolling';
|
|
19
|
-
import {MenuPlacement, MenuVisibility} from './types';
|
|
20
|
-
import {getCorrectedIndexByValue} from './utils';
|
|
21
|
-
|
|
22
|
-
interface OptionData {
|
|
23
|
-
// This allows developers to include arbitrary keys in their
|
|
24
|
-
// Options data and to utilize those keys in their renderOption
|
|
25
|
-
// function without encountering TypeScript errors
|
|
26
|
-
[key: string]: any;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* @deprecated ⚠️ `Option` in Preview has been deprecated and will be removed in a future major version. Please use [`Select` in Main](https://workday.github.io/canvas-kit/?path=/docs/components-inputs-select--docs) instead.
|
|
30
|
-
*/
|
|
31
|
-
export interface Option {
|
|
32
|
-
data?: OptionData;
|
|
33
|
-
disabled?: boolean;
|
|
34
|
-
id?: string;
|
|
35
|
-
label?: string;
|
|
36
|
-
value: string;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* @deprecated ⚠️ `NormalizedOption` in Preview has been deprecated and will be removed in a future major version. Please use [`Select` in Main](https://workday.github.io/canvas-kit/?path=/docs/components-inputs-select--docs) instead.
|
|
40
|
-
*/
|
|
41
|
-
export interface NormalizedOption extends Option {
|
|
42
|
-
// Optional keys in Option are required in NormalizedOption
|
|
43
|
-
data: OptionData;
|
|
44
|
-
disabled: boolean;
|
|
45
|
-
id: string;
|
|
46
|
-
label: string;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* @deprecated ⚠️ `RenderSelectedFunction` in Preview has been deprecated and will be removed in a future major version. Please use [`Select` in Main](https://workday.github.io/canvas-kit/?path=/docs/components-inputs-select--docs) instead.
|
|
50
|
-
*/
|
|
51
|
-
export interface RenderSelectedFunction {
|
|
52
|
-
(option: NormalizedOption): React.ReactNode;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* @deprecated ⚠️ `RenderableOption` in Preview has been deprecated and will be removed in a future major version. Please use [`Select` in Main](https://workday.github.io/canvas-kit/?path=/docs/components-inputs-select--docs) instead.
|
|
56
|
-
*/
|
|
57
|
-
export interface RenderableOption extends NormalizedOption {
|
|
58
|
-
focused: boolean;
|
|
59
|
-
selected: boolean;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* @deprecated ⚠️ `RenderOptionFunction` in Preview has been deprecated and will be removed in a future major version. Please use [`Select` in Main](https://workday.github.io/canvas-kit/?path=/docs/components-inputs-select--docs) instead.
|
|
63
|
-
*/
|
|
64
|
-
export interface RenderOptionFunction {
|
|
65
|
-
(option: RenderableOption): React.ReactNode;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* @deprecated ⚠️ `CoreSelectBaseProps` in Preview has been deprecated and will be removed in a future major version. Please use [`Select` in Main](https://workday.github.io/canvas-kit/?path=/docs/components-inputs-select--docs) instead.
|
|
69
|
-
*/
|
|
70
|
-
export interface CoreSelectBaseProps
|
|
71
|
-
extends Themeable,
|
|
72
|
-
GrowthBehavior,
|
|
73
|
-
Pick<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'>,
|
|
74
|
-
Pick<React.SelectHTMLAttributes<HTMLSelectElement>, 'required'>,
|
|
75
|
-
Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onChange'> {
|
|
76
|
-
/**
|
|
77
|
-
* The type of error associated with the Select (if applicable).
|
|
78
|
-
*/
|
|
79
|
-
error?: ErrorType;
|
|
80
|
-
/**
|
|
81
|
-
* The function called to render the content of each option.
|
|
82
|
-
*
|
|
83
|
-
* The `option` argument passed to the function is an object which contains the following:
|
|
84
|
-
*
|
|
85
|
-
* * `data: object` (data object carried over from the corresponding option originally passed into the component)
|
|
86
|
-
* * `disabled: boolean`
|
|
87
|
-
* * `focused: boolean` (set to `true` if the option has keyboard focus)
|
|
88
|
-
* * `id: string`
|
|
89
|
-
* * `label: string`
|
|
90
|
-
* * `selected: boolean` (set to `true` if the option is selected)
|
|
91
|
-
* * `value: string`
|
|
92
|
-
*
|
|
93
|
-
* If you omit the `renderOption` prop, each option will be rendered using a `defaultRenderOption` function provided by the component.
|
|
94
|
-
*
|
|
95
|
-
* @default defaultRenderOption
|
|
96
|
-
*/
|
|
97
|
-
renderOption?: RenderOptionFunction;
|
|
98
|
-
/**
|
|
99
|
-
* The function called to render the selected option.
|
|
100
|
-
*
|
|
101
|
-
* The `option` argument passed to the function is an object which contains the following:
|
|
102
|
-
*
|
|
103
|
-
* * `data: object` (data object carried over from the corresponding option originally passed into the component)
|
|
104
|
-
* * `disabled: boolean`
|
|
105
|
-
* * `id: string`
|
|
106
|
-
* * `label: string`
|
|
107
|
-
* * `value: string`
|
|
108
|
-
*
|
|
109
|
-
* If you omit the `renderSelected` prop, each option will be rendered using a `defaultRenderSelected` function provided by the component.
|
|
110
|
-
*
|
|
111
|
-
* @default defaultRenderSelected
|
|
112
|
-
*/
|
|
113
|
-
renderSelected?: RenderSelectedFunction;
|
|
114
|
-
/**
|
|
115
|
-
* The value of the Select.
|
|
116
|
-
*/
|
|
117
|
-
value?: string;
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* @deprecated ⚠️ `SelectBaseProps` in Preview has been deprecated and will be removed in a future major version. Please use [`Select` in Main](https://workday.github.io/canvas-kit/?path=/docs/components-inputs-select--docs) instead.
|
|
121
|
-
*/
|
|
122
|
-
export interface SelectBaseProps extends CoreSelectBaseProps, StyledType {
|
|
123
|
-
/**
|
|
124
|
-
* The ref to be forwarded to the underlying button element. Use this to imperatively manipulate the button.
|
|
125
|
-
*/
|
|
126
|
-
forwardedButtonRef?: React.Ref<HTMLButtonElement>;
|
|
127
|
-
/**
|
|
128
|
-
* The local ref to the underlying button element. Can be used in situations where RefObject is required (such as the Popper Menu).
|
|
129
|
-
*/
|
|
130
|
-
localButtonRef?: React.RefObject<HTMLButtonElement>;
|
|
131
|
-
/**
|
|
132
|
-
* The index of the focused option in the SelectBase.
|
|
133
|
-
* @default 0
|
|
134
|
-
*/
|
|
135
|
-
focusedOptionIndex?: number;
|
|
136
|
-
/**
|
|
137
|
-
* The ref to the underlying (hidden) text input element. Use this to imperatively manipulate the input.
|
|
138
|
-
*/
|
|
139
|
-
inputRef?: React.Ref<HTMLInputElement>;
|
|
140
|
-
/**
|
|
141
|
-
* The placement of the SelectBase menu relative to its corresponding SelectButton.
|
|
142
|
-
* @default 'bottom'
|
|
143
|
-
*/
|
|
144
|
-
menuPlacement?: MenuPlacement;
|
|
145
|
-
/**
|
|
146
|
-
* The ref to the underlying menu element. Use this to imperatively manipulate the menu.
|
|
147
|
-
*/
|
|
148
|
-
menuRef?: React.RefObject<HTMLUListElement>;
|
|
149
|
-
/**
|
|
150
|
-
* The visibility state of the SelectBase menu.
|
|
151
|
-
* @default 'closed'
|
|
152
|
-
*/
|
|
153
|
-
menuVisibility?: MenuVisibility;
|
|
154
|
-
/**
|
|
155
|
-
* The function called when a key is pressed down while the SelectBase button or menu has focus.
|
|
156
|
-
*/
|
|
157
|
-
onKeyDown?: (event: React.KeyboardEvent<HTMLElement>) => void;
|
|
158
|
-
/**
|
|
159
|
-
* The function called when the menu is closed.
|
|
160
|
-
*/
|
|
161
|
-
onClose?: () => void;
|
|
162
|
-
/**
|
|
163
|
-
* The function called when an option in the SelectBase is selected via a click or a keyboard shortcut. The `index` passed to the callback function represents the index of the option which was selected.
|
|
164
|
-
*/
|
|
165
|
-
onOptionSelection?: (index: number) => void;
|
|
166
|
-
/**
|
|
167
|
-
* The options of the SelectBase. `options` is an array of objects, each object adhering to the `NormalizedOption` interface:
|
|
168
|
-
*
|
|
169
|
-
* * `value: string` (required, analagous to the `value` attribute of an `<option>`)
|
|
170
|
-
* * `disabled: boolean` (required)
|
|
171
|
-
* * `id: string` (required)
|
|
172
|
-
* * `label: string` (required, analagous to the text content of an `<option>`)
|
|
173
|
-
*/
|
|
174
|
-
options: NormalizedOption[];
|
|
175
|
-
/**
|
|
176
|
-
* If true, automatically flip the SelectBase menu to keep it visible if necessary (e.g., if the the menu would otherwise display below the visible area of the viewport).
|
|
177
|
-
* @default true
|
|
178
|
-
*/
|
|
179
|
-
shouldMenuAutoFlip?: boolean;
|
|
180
|
-
/**
|
|
181
|
-
* If true, focus the SelectBase menu when it's shown. Set to false if you don't want to focus the menu automatically (for visual testing purposes, for example).
|
|
182
|
-
* @default true
|
|
183
|
-
*/
|
|
184
|
-
shouldMenuAutoFocus?: boolean;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export const buttonBorderWidth = 1;
|
|
188
|
-
|
|
189
|
-
const menuIconSize = space.m;
|
|
190
|
-
|
|
191
|
-
const SelectButton = styled('button')<
|
|
192
|
-
Pick<SelectBaseProps, 'error' | 'grow' | 'menuVisibility' | 'theme'> & StyledType
|
|
193
|
-
>(
|
|
194
|
-
{
|
|
195
|
-
...type.levels.subtext.large,
|
|
196
|
-
border: `${buttonBorderWidth}px solid ${inputColors.border}`,
|
|
197
|
-
cursor: 'default',
|
|
198
|
-
display: 'block',
|
|
199
|
-
backgroundColor: inputColors.background,
|
|
200
|
-
borderRadius: borderRadius.m,
|
|
201
|
-
boxSizing: 'border-box',
|
|
202
|
-
height: space.xl,
|
|
203
|
-
outline: 'none',
|
|
204
|
-
overflow: 'hidden',
|
|
205
|
-
padding: `calc(${space.xxs} - ${buttonBorderWidth}px)`,
|
|
206
|
-
paddingRight: `calc(${space.xxs} + ${space.m} + (${space.xxs} + ${buttonBorderWidth}px))`,
|
|
207
|
-
textAlign: 'left',
|
|
208
|
-
textOverflow: 'ellipsis',
|
|
209
|
-
transition: '0.2s box-shadow, 0.2s border-color',
|
|
210
|
-
whiteSpace: 'nowrap',
|
|
211
|
-
// width is required (instead of minWidth) in order for the button to
|
|
212
|
-
// be sized properly for lengthy options
|
|
213
|
-
width: `calc((${space.xxxl} * 7) / 2)`,
|
|
214
|
-
'&::placeholder': {
|
|
215
|
-
color: inputColors.placeholder,
|
|
216
|
-
},
|
|
217
|
-
'&:disabled': {
|
|
218
|
-
backgroundColor: inputColors.disabled.background,
|
|
219
|
-
borderColor: inputColors.disabled.border,
|
|
220
|
-
color: inputColors.disabled.text,
|
|
221
|
-
'&::placeholder': {
|
|
222
|
-
color: inputColors.disabled.text,
|
|
223
|
-
},
|
|
224
|
-
},
|
|
225
|
-
},
|
|
226
|
-
({error, menuVisibility, theme}) => {
|
|
227
|
-
const themedFocusOutlineColor = theme.canvas.palette.common.focusOutline;
|
|
228
|
-
const buttonFocusStyles = {
|
|
229
|
-
borderColor: themedFocusOutlineColor,
|
|
230
|
-
boxShadow: `inset 0 0 0 1px ${themedFocusOutlineColor}`,
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
if (error === undefined) {
|
|
234
|
-
// If there isn't an error, apply focus and hover styles if the menu is
|
|
235
|
-
// closed or in the process of closing (otherwise, the menu is opened
|
|
236
|
-
// or in the process of opening: style the button as if it had focus)
|
|
237
|
-
return menuVisibility === 'closed' || menuVisibility === 'closing'
|
|
238
|
-
? {
|
|
239
|
-
'&:focus:not([disabled])': {
|
|
240
|
-
...buttonFocusStyles,
|
|
241
|
-
},
|
|
242
|
-
'&:hover:not([disabled]):not(:focus)': {
|
|
243
|
-
borderColor: inputColors.hoverBorder,
|
|
244
|
-
},
|
|
245
|
-
}
|
|
246
|
-
: {...buttonFocusStyles};
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
return {
|
|
250
|
-
...errorRing(error, theme),
|
|
251
|
-
};
|
|
252
|
-
},
|
|
253
|
-
({grow}) =>
|
|
254
|
-
grow && {
|
|
255
|
-
width: '100%',
|
|
256
|
-
}
|
|
257
|
-
);
|
|
258
|
-
|
|
259
|
-
const SelectMenuIcon = styled(SystemIcon)({
|
|
260
|
-
position: 'absolute',
|
|
261
|
-
top: space.xxxs,
|
|
262
|
-
right: space.xxxs,
|
|
263
|
-
padding: space.xxxs,
|
|
264
|
-
pointerEvents: 'none',
|
|
265
|
-
'& path': {
|
|
266
|
-
transition: '100ms fill',
|
|
267
|
-
},
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
const SelectInput = styled('input')({
|
|
271
|
-
display: 'none',
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
const SelectWrapper = styled('div')<Pick<SelectBaseProps, 'grow' | 'disabled'>>(
|
|
275
|
-
{
|
|
276
|
-
position: 'relative',
|
|
277
|
-
},
|
|
278
|
-
({grow}) => ({
|
|
279
|
-
display: grow ? 'block' : 'inline-block',
|
|
280
|
-
}),
|
|
281
|
-
({disabled}) => ({
|
|
282
|
-
'&:hover .menu-icon path': {
|
|
283
|
-
fill: disabled ? undefined : colors.licorice500,
|
|
284
|
-
},
|
|
285
|
-
})
|
|
286
|
-
);
|
|
287
|
-
|
|
288
|
-
const defaultRenderOption: RenderOptionFunction = option => {
|
|
289
|
-
return <div>{defaultRenderSelected(option)}</div>;
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
const defaultRenderSelected: RenderSelectedFunction = option => {
|
|
293
|
-
return option.label;
|
|
294
|
-
};
|
|
295
|
-
/**
|
|
296
|
-
* @deprecated ⚠️ `SelectBase` in Preview has been deprecated and will be removed in a future major version. Please use [`Select` in Main](https://workday.github.io/canvas-kit/?path=/docs/components-inputs-select--docs) instead.
|
|
297
|
-
*/
|
|
298
|
-
export const SelectBase = ({
|
|
299
|
-
'aria-labelledby': ariaLabelledBy,
|
|
300
|
-
'aria-required': ariaRequired,
|
|
301
|
-
as,
|
|
302
|
-
forwardedButtonRef,
|
|
303
|
-
localButtonRef,
|
|
304
|
-
disabled,
|
|
305
|
-
error,
|
|
306
|
-
focusedOptionIndex = 0,
|
|
307
|
-
grow,
|
|
308
|
-
inputRef,
|
|
309
|
-
menuPlacement = 'bottom',
|
|
310
|
-
menuRef,
|
|
311
|
-
menuVisibility = 'closed',
|
|
312
|
-
onChange,
|
|
313
|
-
onKeyDown,
|
|
314
|
-
onBlur,
|
|
315
|
-
onClose,
|
|
316
|
-
onOptionSelection,
|
|
317
|
-
options,
|
|
318
|
-
renderOption = defaultRenderOption,
|
|
319
|
-
renderSelected = defaultRenderSelected,
|
|
320
|
-
required,
|
|
321
|
-
shouldMenuAutoFlip = true,
|
|
322
|
-
shouldMenuAutoFocus = true,
|
|
323
|
-
value,
|
|
324
|
-
...elemProps
|
|
325
|
-
}: SelectBaseProps) => {
|
|
326
|
-
const focusedOptionRef = React.useRef<HTMLLIElement>(null);
|
|
327
|
-
|
|
328
|
-
const menuId = useUniqueId();
|
|
329
|
-
|
|
330
|
-
const renderOptions = (renderOption: RenderOptionFunction) => {
|
|
331
|
-
const selectedOptionIndex = getCorrectedIndexByValue(options, value);
|
|
332
|
-
|
|
333
|
-
return options.map((option, index) => {
|
|
334
|
-
const optionProps = {
|
|
335
|
-
'aria-disabled': option.disabled ? true : undefined,
|
|
336
|
-
'aria-selected': selectedOptionIndex === index ? true : undefined,
|
|
337
|
-
error,
|
|
338
|
-
focused: focusedOptionIndex === index,
|
|
339
|
-
id: option.id,
|
|
340
|
-
interactive: menuVisibility === 'opening' || menuVisibility === 'opened',
|
|
341
|
-
key: option.id,
|
|
342
|
-
optionRef: focusedOptionIndex === index ? focusedOptionRef : undefined,
|
|
343
|
-
value: option.value,
|
|
344
|
-
...(onOptionSelection
|
|
345
|
-
? {
|
|
346
|
-
onClick: (event: React.MouseEvent) => {
|
|
347
|
-
// TODO: Figure out why this preventDefault call exists.
|
|
348
|
-
// Removing it doesn't have any obvious consequences,
|
|
349
|
-
// but need to do more careful testing.
|
|
350
|
-
event.preventDefault();
|
|
351
|
-
onOptionSelection(index);
|
|
352
|
-
},
|
|
353
|
-
}
|
|
354
|
-
: {}),
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
// Pass in additional information about the option state: focused, selected
|
|
358
|
-
const normalizedOption = {
|
|
359
|
-
...option,
|
|
360
|
-
focused: optionProps.focused,
|
|
361
|
-
selected: !!optionProps['aria-selected'],
|
|
362
|
-
};
|
|
363
|
-
|
|
364
|
-
return <SelectOption {...optionProps}>{renderOption(normalizedOption)}</SelectOption>;
|
|
365
|
-
});
|
|
366
|
-
};
|
|
367
|
-
|
|
368
|
-
// If the focused option changed, scroll the newly focused option into view (if
|
|
369
|
-
// necessary) but do NOT center it
|
|
370
|
-
useLayoutEffect(() => {
|
|
371
|
-
const focusedOption = focusedOptionRef.current;
|
|
372
|
-
|
|
373
|
-
if (focusedOption) {
|
|
374
|
-
// TODO: Figure out if rAF is the best approach here. I initially added
|
|
375
|
-
// rAF to get the Select States Menu On story to render correctly in IE.
|
|
376
|
-
// Without rAF, the menu is scrolled slightly further down than it should
|
|
377
|
-
// be (only in IE, and only in the Menu On visual testing stories), which
|
|
378
|
-
// triggers a visual regression. We're inside useLayoutEffect here so I
|
|
379
|
-
// didn't expect to need rAF in order to make proper measurements, but it
|
|
380
|
-
// seems to be necessary in IE.
|
|
381
|
-
//
|
|
382
|
-
// This rAF call also has the additional benefit of fixing a jarring menu
|
|
383
|
-
// placement issue in IE (https://github.com/Workday/canvas-kit/issues/791),
|
|
384
|
-
// so I'm leaving it in for now.
|
|
385
|
-
const animateId = requestAnimationFrame(() => {
|
|
386
|
-
// We cannot use the native Element.scrollIntoView() here because it
|
|
387
|
-
// doesn't work properly with the portalled menu: when using the keyboard
|
|
388
|
-
// to advance focus through the options, using scrollIntoView to keep the
|
|
389
|
-
// newly focused option in view also scrolls the ENTIRE page. Instead, we
|
|
390
|
-
// call our own scrollIntoViewIfNeeded function.
|
|
391
|
-
scrollIntoViewIfNeeded(focusedOption, false);
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
return () => {
|
|
395
|
-
cancelAnimationFrame(animateId);
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
return undefined;
|
|
400
|
-
}, [focusedOptionIndex]);
|
|
401
|
-
|
|
402
|
-
// If the menu was just opened, scroll the focused option into view (if
|
|
403
|
-
// necessary) and center it
|
|
404
|
-
useLayoutEffect(() => {
|
|
405
|
-
const focusedOption = focusedOptionRef.current;
|
|
406
|
-
|
|
407
|
-
// We need to scroll if the menu is either opening or opened in case we decide to
|
|
408
|
-
// bypass the opening state and jump straight to opened (like in visual testing,
|
|
409
|
-
// for instance)
|
|
410
|
-
if (focusedOption && (menuVisibility === 'opening' || menuVisibility === 'opened')) {
|
|
411
|
-
// TODO: This rAF call is also necessary for the Menu On visual testing
|
|
412
|
-
// stories to render properly in IE (see above, both rAF calls need to be
|
|
413
|
-
// present). It has no bearing on the menu placement issue.
|
|
414
|
-
const animateId = requestAnimationFrame(() => {
|
|
415
|
-
scrollIntoViewIfNeeded(focusedOption, true);
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
return () => {
|
|
419
|
-
cancelAnimationFrame(animateId);
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
return undefined;
|
|
424
|
-
}, [menuVisibility]);
|
|
425
|
-
|
|
426
|
-
// Do a bit of error-checking in case options weren't provided
|
|
427
|
-
const hasOptions = options.length > 0;
|
|
428
|
-
const selectedOption = hasOptions ? options[getCorrectedIndexByValue(options, value)] : null;
|
|
429
|
-
const selectedOptionValue = selectedOption ? selectedOption.value : '';
|
|
430
|
-
|
|
431
|
-
return (
|
|
432
|
-
<SelectWrapper grow={grow} disabled={disabled}>
|
|
433
|
-
<SelectButton
|
|
434
|
-
aria-expanded={menuVisibility !== 'closed' ? 'true' : undefined}
|
|
435
|
-
aria-haspopup="listbox"
|
|
436
|
-
aria-controls={menuVisibility !== 'closed' ? menuId : undefined}
|
|
437
|
-
as={as}
|
|
438
|
-
disabled={disabled}
|
|
439
|
-
error={error}
|
|
440
|
-
grow={grow}
|
|
441
|
-
menuVisibility={menuVisibility}
|
|
442
|
-
onKeyDown={onKeyDown}
|
|
443
|
-
onBlur={event => {
|
|
444
|
-
if (menuVisibility === 'closed') {
|
|
445
|
-
onBlur?.(event);
|
|
446
|
-
} else {
|
|
447
|
-
event.preventDefault();
|
|
448
|
-
event.stopPropagation();
|
|
449
|
-
}
|
|
450
|
-
}}
|
|
451
|
-
// Prevent Firefox from triggering click handler on spacebar during
|
|
452
|
-
// type-ahead when the menu is closed (and, thus, incorrectly displaying
|
|
453
|
-
// the menu)
|
|
454
|
-
onKeyUp={e => {
|
|
455
|
-
e.preventDefault();
|
|
456
|
-
}}
|
|
457
|
-
ref={forwardedButtonRef}
|
|
458
|
-
type="button"
|
|
459
|
-
value={selectedOptionValue}
|
|
460
|
-
{...elemProps}
|
|
461
|
-
>
|
|
462
|
-
{!!selectedOption && renderSelected(selectedOption)}
|
|
463
|
-
</SelectButton>
|
|
464
|
-
<SelectInput onChange={onChange} ref={inputRef} type="text" value={selectedOptionValue} />
|
|
465
|
-
{hasOptions && menuVisibility !== 'closed' && (
|
|
466
|
-
<SelectMenu
|
|
467
|
-
aria-activedescendant={options[focusedOptionIndex].id}
|
|
468
|
-
aria-labelledby={ariaLabelledBy}
|
|
469
|
-
aria-required={ariaRequired || required ? true : undefined}
|
|
470
|
-
buttonRef={localButtonRef}
|
|
471
|
-
id={menuId}
|
|
472
|
-
error={error}
|
|
473
|
-
menuRef={menuRef}
|
|
474
|
-
onKeyDown={onKeyDown}
|
|
475
|
-
onClose={onClose}
|
|
476
|
-
placement={menuPlacement}
|
|
477
|
-
shouldAutoFlip={shouldMenuAutoFlip}
|
|
478
|
-
shouldAutoFocus={shouldMenuAutoFocus}
|
|
479
|
-
visibility={menuVisibility}
|
|
480
|
-
>
|
|
481
|
-
{renderOptions(renderOption)}
|
|
482
|
-
</SelectMenu>
|
|
483
|
-
)}
|
|
484
|
-
<SelectMenuIcon
|
|
485
|
-
className="menu-icon"
|
|
486
|
-
icon={caretDownSmallIcon}
|
|
487
|
-
color={disabled ? colors.licorice100 : colors.licorice200}
|
|
488
|
-
colorHover={disabled ? colors.licorice100 : colors.licorice500}
|
|
489
|
-
size={`${menuIconSize}rem`}
|
|
490
|
-
/>
|
|
491
|
-
</SelectWrapper>
|
|
492
|
-
);
|
|
493
|
-
};
|