@itwin/itwinui-react 3.5.0 → 3.6.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/CHANGELOG.md +34 -0
- package/cjs/core/Carousel/Carousel.d.ts +4 -4
- package/cjs/core/Carousel/CarouselNavigation.d.ts +4 -4
- package/cjs/core/Dialog/Dialog.js +19 -17
- package/cjs/core/Dialog/DialogContext.d.ts +4 -0
- package/cjs/core/Dialog/DialogMain.js +1 -1
- package/cjs/core/Dialog/DialogTitleBar.js +1 -1
- package/cjs/core/InputGroup/InputGroup.d.ts +4 -0
- package/cjs/core/InputGroup/InputGroup.js +20 -4
- package/cjs/core/InputWithDecorations/InputWithDecorations.d.ts +18 -62
- package/cjs/core/InputWithDecorations/InputWithDecorations.js +21 -6
- package/cjs/core/LabeledSelect/LabeledSelect.d.ts +376 -4
- package/cjs/core/LabeledSelect/LabeledSelect.js +1 -1
- package/cjs/core/Popover/Popover.d.ts +6 -3
- package/cjs/core/SearchBox/SearchBox.js +4 -6
- package/cjs/core/Select/Select.d.ts +194 -101
- package/cjs/core/Select/Select.js +71 -18
- package/cjs/core/Table/Table.js +2 -1
- package/cjs/core/Tabs/Tabs.js +19 -3
- package/cjs/core/Tile/Tile.d.ts +2 -2
- package/cjs/core/utils/components/InputFlexContainer.d.ts +25 -0
- package/cjs/core/utils/components/InputFlexContainer.js +19 -1
- package/cjs/core/utils/components/LineClamp.d.ts +5 -0
- package/cjs/core/utils/components/LineClamp.js +49 -0
- package/cjs/core/utils/components/index.d.ts +1 -0
- package/cjs/core/utils/components/index.js +1 -0
- package/cjs/core/utils/icons/SvgStatusError.js +1 -1
- package/esm/core/Carousel/Carousel.d.ts +4 -4
- package/esm/core/Carousel/CarouselNavigation.d.ts +4 -4
- package/esm/core/Dialog/Dialog.js +19 -17
- package/esm/core/Dialog/DialogContext.d.ts +4 -0
- package/esm/core/Dialog/DialogMain.js +1 -1
- package/esm/core/Dialog/DialogTitleBar.js +1 -1
- package/esm/core/InputGroup/InputGroup.d.ts +4 -0
- package/esm/core/InputGroup/InputGroup.js +20 -4
- package/esm/core/InputWithDecorations/InputWithDecorations.d.ts +18 -62
- package/esm/core/InputWithDecorations/InputWithDecorations.js +22 -7
- package/esm/core/LabeledSelect/LabeledSelect.d.ts +376 -4
- package/esm/core/LabeledSelect/LabeledSelect.js +1 -1
- package/esm/core/Popover/Popover.d.ts +6 -3
- package/esm/core/SearchBox/SearchBox.js +5 -7
- package/esm/core/Select/Select.d.ts +194 -101
- package/esm/core/Select/Select.js +72 -19
- package/esm/core/Table/Table.js +2 -1
- package/esm/core/Tabs/Tabs.js +19 -3
- package/esm/core/Tile/Tile.d.ts +2 -2
- package/esm/core/utils/components/InputFlexContainer.d.ts +25 -0
- package/esm/core/utils/components/InputFlexContainer.js +18 -0
- package/esm/core/utils/components/LineClamp.d.ts +5 -0
- package/esm/core/utils/components/LineClamp.js +23 -0
- package/esm/core/utils/components/index.d.ts +1 -0
- package/esm/core/utils/components/index.js +1 -0
- package/esm/core/utils/icons/SvgStatusError.js +1 -1
- package/package.json +24 -26
- package/styles.css +23 -23
|
@@ -1,54 +1,193 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import type { CommonProps } from '../utils/index.js';
|
|
3
3
|
import { usePopover } from '../Popover/Popover.js';
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Select component to select value from options.
|
|
6
|
+
* Generic type is used for value. It prevents you from mistakenly using other types in `options`, `value` and `onChange`.
|
|
7
|
+
* @example
|
|
8
|
+
* <caption>Basic select.</caption>
|
|
9
|
+
* <Select
|
|
10
|
+
* options={[
|
|
11
|
+
* { value: '1', label: 'Option 1' },
|
|
12
|
+
* { value: '2', label: 'Option 2' },
|
|
13
|
+
* { value: '3', label: 'Option 3' },
|
|
14
|
+
* ]}
|
|
15
|
+
* />
|
|
16
|
+
* @example
|
|
17
|
+
* <caption>Native select.</caption>
|
|
18
|
+
* <Select
|
|
19
|
+
* native
|
|
20
|
+
* options={[
|
|
21
|
+
* { value: '1', label: 'Option 1' },
|
|
22
|
+
* { value: '2', label: 'Option 2' },
|
|
23
|
+
* { value: '3', label: 'Option 3' },
|
|
24
|
+
* ]}
|
|
25
|
+
* />
|
|
26
|
+
* @example
|
|
27
|
+
* <caption>Disabled select with placeholder.</caption>
|
|
28
|
+
* <Select
|
|
29
|
+
* disabled={true}
|
|
30
|
+
* placeholder='Placeholder text'
|
|
31
|
+
* options={[{ value: 1, label: 'Option 1' }, { value: 2, label: 'Option 2' }, { value: 3, label: 'Option 3' }]}
|
|
32
|
+
* />
|
|
33
|
+
* @example
|
|
34
|
+
* <caption>Select with selected value and change handler.</caption>
|
|
35
|
+
* <Select
|
|
36
|
+
* value={selectedValue}
|
|
37
|
+
* onChange={(value) => setSelectedValue(value)}
|
|
38
|
+
* options={[{ value: 1, label: 'Option 1' }, { value: 2, label: 'Option 2' }, { value: 3, label: 'Option 3' }]}
|
|
39
|
+
* />
|
|
40
|
+
* @example
|
|
41
|
+
* <caption>Select using custom renderers for menu items and selected value.</caption>
|
|
42
|
+
* <Select
|
|
43
|
+
* options={[
|
|
44
|
+
* { value: 'yellow', label: 'Yellow' },
|
|
45
|
+
* { value: 'green', label: 'Green' },
|
|
46
|
+
* { value: 'red', label: 'Red' },
|
|
47
|
+
* ]}
|
|
48
|
+
* value={selectedValue}
|
|
49
|
+
* placeholder='Placeholder text'
|
|
50
|
+
* itemRenderer={(option, itemProps) => (
|
|
51
|
+
* <MenuItem
|
|
52
|
+
* style={{ color: option.value }}
|
|
53
|
+
* isSelected={itemProps.isSelected}
|
|
54
|
+
* onClick={() => {
|
|
55
|
+
* setSelectedValue(option.value);
|
|
56
|
+
* itemProps.close();
|
|
57
|
+
* }}
|
|
58
|
+
* role='option'
|
|
59
|
+
* ref={(el) => itemProps.isSelected && el?.scrollIntoView()}
|
|
60
|
+
* >
|
|
61
|
+
* {option.label}
|
|
62
|
+
* </MenuItem>
|
|
63
|
+
* )}
|
|
64
|
+
* selectedItemRenderer={(option) => (
|
|
65
|
+
* <span style={{ backgroundColor: option.value }}>{option.label}</span>
|
|
66
|
+
* )}
|
|
67
|
+
* />
|
|
68
|
+
*/
|
|
69
|
+
export declare const Select: <T>(props: SelectProps<T> & {
|
|
70
|
+
ref?: React.ForwardedRef<HTMLElement> | undefined;
|
|
71
|
+
}) => JSX.Element;
|
|
72
|
+
export type SelectProps<T> = Omit<React.ComponentPropsWithoutRef<'div'>, 'onChange' | 'placeholder' | 'value' | 'defaultValue'> & (({
|
|
5
73
|
/**
|
|
6
|
-
*
|
|
74
|
+
* If true, the native `<select>` element will be rendered.
|
|
75
|
+
*
|
|
76
|
+
* Extra props, such as `name` can be passed to the `<select>` using `triggerProps`.
|
|
77
|
+
*
|
|
78
|
+
* @default false
|
|
7
79
|
*/
|
|
8
|
-
|
|
80
|
+
native: true;
|
|
81
|
+
} & NativeSelectProps) | ({
|
|
82
|
+
native?: false;
|
|
83
|
+
} & CustomSelectProps<T> & {
|
|
9
84
|
/**
|
|
10
|
-
*
|
|
85
|
+
* styleType is only supported for `<Select native>`.
|
|
11
86
|
*/
|
|
12
|
-
|
|
13
|
-
};
|
|
14
|
-
|
|
87
|
+
styleType?: never;
|
|
88
|
+
}));
|
|
89
|
+
type NativeSelectProps = SelectCommonProps & {
|
|
15
90
|
/**
|
|
16
|
-
*
|
|
91
|
+
* Selected option value.
|
|
92
|
+
*
|
|
93
|
+
* Must be a string, because it is passed as an attribute to the native <select>.
|
|
94
|
+
*
|
|
95
|
+
* Alternatively, pass `null` to reset the value.
|
|
17
96
|
*/
|
|
18
|
-
|
|
97
|
+
value?: string | null;
|
|
19
98
|
/**
|
|
20
|
-
*
|
|
99
|
+
* Callback invoked when the selected value changes.
|
|
21
100
|
*/
|
|
22
|
-
|
|
101
|
+
onChange?: (value: string, event: React.ChangeEvent<HTMLSelectElement>) => void;
|
|
23
102
|
/**
|
|
24
|
-
*
|
|
25
|
-
* Use 'large' when any of the select options have `sublabel`.
|
|
103
|
+
* Array of options that populates the select menu.
|
|
26
104
|
*
|
|
27
|
-
*
|
|
105
|
+
* The `value` property of each option must be a string.
|
|
28
106
|
*/
|
|
29
|
-
|
|
107
|
+
options: Array<{
|
|
108
|
+
label: string;
|
|
109
|
+
value: string;
|
|
110
|
+
disabled?: boolean;
|
|
111
|
+
}>;
|
|
30
112
|
/**
|
|
31
|
-
*
|
|
113
|
+
* Default value that is selected on initial render. This is useful when you don't want to
|
|
114
|
+
* maintain your own state but still want to control the initial value.
|
|
115
|
+
*
|
|
116
|
+
* If not passed, the first option (or placeholder) will be automatically selected.
|
|
32
117
|
*/
|
|
33
|
-
|
|
118
|
+
defaultValue?: string;
|
|
34
119
|
/**
|
|
35
|
-
*
|
|
36
|
-
* SVG icon component shown on the left.
|
|
120
|
+
* Props to pass to the select element.
|
|
37
121
|
*/
|
|
38
|
-
|
|
122
|
+
triggerProps?: Omit<React.ComponentPropsWithRef<'select'>, 'size'>;
|
|
123
|
+
required?: boolean;
|
|
124
|
+
multiple?: never;
|
|
125
|
+
} & NativeSelectStyleTypeProps;
|
|
126
|
+
type NativeSelectStyleTypeProps = {
|
|
39
127
|
/**
|
|
40
|
-
*
|
|
128
|
+
* Style of the select.
|
|
129
|
+
* Use 'borderless' to hide outline.
|
|
130
|
+
* @default 'default'
|
|
41
131
|
*/
|
|
42
|
-
|
|
132
|
+
styleType?: 'default';
|
|
43
133
|
/**
|
|
44
|
-
*
|
|
134
|
+
* Placeholder for when no item is selected.
|
|
135
|
+
*
|
|
136
|
+
* Will be rendered as a disabled option at the top of the list, and automatically
|
|
137
|
+
* selected when no `value` or `defaultValue` is provided.
|
|
138
|
+
*
|
|
139
|
+
* Not allowed when `styleType` is `borderless`.
|
|
140
|
+
*/
|
|
141
|
+
placeholder?: string;
|
|
142
|
+
} | {
|
|
143
|
+
styleType: 'borderless';
|
|
144
|
+
placeholder?: never;
|
|
145
|
+
};
|
|
146
|
+
type SelectCommonProps = {
|
|
147
|
+
/**
|
|
148
|
+
* Disables select.
|
|
149
|
+
* @default false
|
|
45
150
|
*/
|
|
46
151
|
disabled?: boolean;
|
|
47
152
|
/**
|
|
48
|
-
*
|
|
153
|
+
* Modify size of select.
|
|
49
154
|
*/
|
|
50
|
-
|
|
51
|
-
|
|
155
|
+
size?: 'small' | 'large';
|
|
156
|
+
/**
|
|
157
|
+
* Status of select.
|
|
158
|
+
*/
|
|
159
|
+
status?: 'positive' | 'warning' | 'negative';
|
|
160
|
+
};
|
|
161
|
+
export type CustomSelectProps<T> = SelectCommonProps & {
|
|
162
|
+
/**
|
|
163
|
+
* Placeholder when no item is selected.
|
|
164
|
+
*/
|
|
165
|
+
placeholder?: React.ReactNode;
|
|
166
|
+
/**
|
|
167
|
+
* Array of options that populates the select menu.
|
|
168
|
+
*/
|
|
169
|
+
options: SelectOption<T>[];
|
|
170
|
+
/**
|
|
171
|
+
* Custom renderer for an item in the dropdown list. `MenuItem` item props are going to be populated if not provided.
|
|
172
|
+
*/
|
|
173
|
+
itemRenderer?: (option: SelectOption<T>, itemProps: ItemRendererProps) => JSX.Element;
|
|
174
|
+
/**
|
|
175
|
+
* Custom class for menu.
|
|
176
|
+
*/
|
|
177
|
+
menuClassName?: string;
|
|
178
|
+
/**
|
|
179
|
+
* Custom style for menu.
|
|
180
|
+
*/
|
|
181
|
+
menuStyle?: React.CSSProperties;
|
|
182
|
+
/**
|
|
183
|
+
* Props to customize Popover behavior.
|
|
184
|
+
*/
|
|
185
|
+
popoverProps?: Pick<Parameters<typeof usePopover>[0], 'visible' | 'onVisibleChange' | 'placement' | 'matchWidth' | 'closeOnOutsideClick'>;
|
|
186
|
+
/**
|
|
187
|
+
* Props to pass to the select button (trigger) element.
|
|
188
|
+
*/
|
|
189
|
+
triggerProps?: React.ComponentPropsWithRef<'div'>;
|
|
190
|
+
} & SelectMultipleTypeProps<T> & Omit<React.ComponentPropsWithoutRef<'div'>, 'size' | 'disabled' | 'placeholder' | 'onChange'>;
|
|
52
191
|
export type SelectValueChangeEvent = 'added' | 'removed';
|
|
53
192
|
export type SelectMultipleTypeProps<T> = {
|
|
54
193
|
/**
|
|
@@ -78,98 +217,52 @@ export type SelectMultipleTypeProps<T> = {
|
|
|
78
217
|
value?: T[];
|
|
79
218
|
onChange?: (value: T, event: SelectValueChangeEvent) => void;
|
|
80
219
|
};
|
|
81
|
-
export type
|
|
220
|
+
export type ItemRendererProps = {
|
|
82
221
|
/**
|
|
83
|
-
*
|
|
222
|
+
* Close handler that closes the dropdown.
|
|
84
223
|
*/
|
|
85
|
-
|
|
224
|
+
close: () => void;
|
|
86
225
|
/**
|
|
87
|
-
*
|
|
226
|
+
* Indicates whether an item is selected.
|
|
88
227
|
*/
|
|
89
|
-
|
|
228
|
+
isSelected: boolean;
|
|
229
|
+
};
|
|
230
|
+
export type SelectOption<T> = {
|
|
90
231
|
/**
|
|
91
|
-
*
|
|
92
|
-
* @default false
|
|
232
|
+
* Label of the item used in dropdown list and when selected.
|
|
93
233
|
*/
|
|
94
|
-
|
|
234
|
+
label: string;
|
|
95
235
|
/**
|
|
96
|
-
*
|
|
236
|
+
* Sublabel of the item shown below the label.
|
|
97
237
|
*/
|
|
98
|
-
|
|
238
|
+
sublabel?: React.ReactNode;
|
|
99
239
|
/**
|
|
100
|
-
*
|
|
240
|
+
* Modify height of the item.
|
|
241
|
+
* Use 'large' when any of the select options have `sublabel`.
|
|
242
|
+
*
|
|
243
|
+
* Defaults to 'large' if `sublabel` provided, otherwise 'default'.
|
|
101
244
|
*/
|
|
102
|
-
|
|
245
|
+
size?: 'default' | 'large';
|
|
103
246
|
/**
|
|
104
|
-
*
|
|
247
|
+
* Value of the item.
|
|
105
248
|
*/
|
|
106
|
-
|
|
249
|
+
value: T;
|
|
107
250
|
/**
|
|
108
|
-
*
|
|
251
|
+
* @deprecated Use startIcon
|
|
252
|
+
* SVG icon component shown on the left.
|
|
109
253
|
*/
|
|
110
|
-
|
|
254
|
+
icon?: JSX.Element;
|
|
111
255
|
/**
|
|
112
|
-
*
|
|
256
|
+
* SVG icon component shown on the left.
|
|
113
257
|
*/
|
|
114
|
-
|
|
258
|
+
startIcon?: JSX.Element;
|
|
115
259
|
/**
|
|
116
|
-
*
|
|
260
|
+
* Item is disabled.
|
|
117
261
|
*/
|
|
118
|
-
|
|
262
|
+
disabled?: boolean;
|
|
119
263
|
/**
|
|
120
|
-
*
|
|
264
|
+
* Any other props.
|
|
121
265
|
*/
|
|
122
|
-
|
|
123
|
-
} &
|
|
124
|
-
|
|
125
|
-
* Select component to select value from options.
|
|
126
|
-
* Generic type is used for value. It prevents you from mistakenly using other types in `options`, `value` and `onChange`.
|
|
127
|
-
* @example
|
|
128
|
-
* <caption>Basic select.</caption>
|
|
129
|
-
* <Select<number> options={[{ value: 1, label: 'Option 1' }, { value: 2, label: 'Option 2' }, { value: 3, label: 'Option 3' }]} />
|
|
130
|
-
* @example
|
|
131
|
-
* <caption>Disabled select with placeholder.</caption>
|
|
132
|
-
* <Select
|
|
133
|
-
* disabled={true}
|
|
134
|
-
* placeholder='Placeholder text'
|
|
135
|
-
* options={[{ value: 1, label: 'Option 1' }, { value: 2, label: 'Option 2' }, { value: 3, label: 'Option 3' }]}
|
|
136
|
-
* />
|
|
137
|
-
* @example
|
|
138
|
-
* <caption>Select with selected value and change handler.</caption>
|
|
139
|
-
* <Select<number>
|
|
140
|
-
* value={selectedValue}
|
|
141
|
-
* onChange={(value) => setSelectedValue(value)}
|
|
142
|
-
* options={[{ value: 1, label: 'Option 1' }, { value: 2, label: 'Option 2' }, { value: 3, label: 'Option 3' }]}
|
|
143
|
-
* />
|
|
144
|
-
* @example
|
|
145
|
-
* <caption>Select using custom renderers for menu items and selected value.</caption>
|
|
146
|
-
* <Select<string>
|
|
147
|
-
* options={[
|
|
148
|
-
* { value: 'yellow', label: 'Yellow' },
|
|
149
|
-
* { value: 'green', label: 'Green' },
|
|
150
|
-
* { value: 'red', label: 'Red' },
|
|
151
|
-
* ]}
|
|
152
|
-
* value={selectedValue}
|
|
153
|
-
* placeholder='Placeholder text'
|
|
154
|
-
* itemRenderer={(option, itemProps) => (
|
|
155
|
-
* <MenuItem
|
|
156
|
-
* style={{ color: option.value }}
|
|
157
|
-
* isSelected={itemProps.isSelected}
|
|
158
|
-
* onClick={() => {
|
|
159
|
-
* setSelectedValue(option.value);
|
|
160
|
-
* itemProps.close();
|
|
161
|
-
* }}
|
|
162
|
-
* role='option'
|
|
163
|
-
* ref={(el) => itemProps.isSelected && el?.scrollIntoView()}
|
|
164
|
-
* >
|
|
165
|
-
* {option.label}
|
|
166
|
-
* </MenuItem>
|
|
167
|
-
* )}
|
|
168
|
-
* selectedItemRenderer={(option) => (
|
|
169
|
-
* <span style={{ backgroundColor: option.value }}>{option.label}</span>
|
|
170
|
-
* )}
|
|
171
|
-
* />
|
|
172
|
-
*/
|
|
173
|
-
export declare const Select: <T>(props: SelectProps<T> & {
|
|
174
|
-
ref?: React.ForwardedRef<HTMLElement> | undefined;
|
|
175
|
-
}) => JSX.Element;
|
|
266
|
+
[key: string]: unknown;
|
|
267
|
+
} & CommonProps;
|
|
268
|
+
export {};
|
|
@@ -6,24 +6,34 @@ import * as React from 'react';
|
|
|
6
6
|
import cx from 'classnames';
|
|
7
7
|
import { Menu } from '../Menu/Menu.js';
|
|
8
8
|
import { MenuItem } from '../Menu/MenuItem.js';
|
|
9
|
-
import { SvgCaretDownSmall, useId, AutoclearingHiddenLiveRegion, Box, Portal, useMergedRefs, SvgCheckmark, useLatestRef, InputWithIcon, } from '../utils/index.js';
|
|
9
|
+
import { SvgCaretDownSmall, useId, AutoclearingHiddenLiveRegion, Box, Portal, useMergedRefs, SvgCheckmark, useLatestRef, InputWithIcon, mergeEventHandlers, } from '../utils/index.js';
|
|
10
10
|
import { SelectTag } from './SelectTag.js';
|
|
11
11
|
import { SelectTagContainer } from './SelectTagContainer.js';
|
|
12
12
|
import { Icon } from '../Icon/Icon.js';
|
|
13
13
|
import { usePopover } from '../Popover/Popover.js';
|
|
14
|
-
|
|
15
|
-
return multiple;
|
|
16
|
-
};
|
|
17
|
-
// Type guard for multiple did not work
|
|
18
|
-
const isSingleOnChange = (onChange, multiple) => {
|
|
19
|
-
return !multiple;
|
|
20
|
-
};
|
|
14
|
+
// ----------------------------------------------------------------------------
|
|
21
15
|
/**
|
|
22
16
|
* Select component to select value from options.
|
|
23
17
|
* Generic type is used for value. It prevents you from mistakenly using other types in `options`, `value` and `onChange`.
|
|
24
18
|
* @example
|
|
25
19
|
* <caption>Basic select.</caption>
|
|
26
|
-
* <Select
|
|
20
|
+
* <Select
|
|
21
|
+
* options={[
|
|
22
|
+
* { value: '1', label: 'Option 1' },
|
|
23
|
+
* { value: '2', label: 'Option 2' },
|
|
24
|
+
* { value: '3', label: 'Option 3' },
|
|
25
|
+
* ]}
|
|
26
|
+
* />
|
|
27
|
+
* @example
|
|
28
|
+
* <caption>Native select.</caption>
|
|
29
|
+
* <Select
|
|
30
|
+
* native
|
|
31
|
+
* options={[
|
|
32
|
+
* { value: '1', label: 'Option 1' },
|
|
33
|
+
* { value: '2', label: 'Option 2' },
|
|
34
|
+
* { value: '3', label: 'Option 3' },
|
|
35
|
+
* ]}
|
|
36
|
+
* />
|
|
27
37
|
* @example
|
|
28
38
|
* <caption>Disabled select with placeholder.</caption>
|
|
29
39
|
* <Select
|
|
@@ -33,14 +43,14 @@ const isSingleOnChange = (onChange, multiple) => {
|
|
|
33
43
|
* />
|
|
34
44
|
* @example
|
|
35
45
|
* <caption>Select with selected value and change handler.</caption>
|
|
36
|
-
* <Select
|
|
46
|
+
* <Select
|
|
37
47
|
* value={selectedValue}
|
|
38
48
|
* onChange={(value) => setSelectedValue(value)}
|
|
39
49
|
* options={[{ value: 1, label: 'Option 1' }, { value: 2, label: 'Option 2' }, { value: 3, label: 'Option 3' }]}
|
|
40
50
|
* />
|
|
41
51
|
* @example
|
|
42
52
|
* <caption>Select using custom renderers for menu items and selected value.</caption>
|
|
43
|
-
* <Select
|
|
53
|
+
* <Select
|
|
44
54
|
* options={[
|
|
45
55
|
* { value: 'yellow', label: 'Yellow' },
|
|
46
56
|
* { value: 'green', label: 'Green' },
|
|
@@ -68,6 +78,32 @@ const isSingleOnChange = (onChange, multiple) => {
|
|
|
68
78
|
* />
|
|
69
79
|
*/
|
|
70
80
|
export const Select = React.forwardRef((props, forwardedRef) => {
|
|
81
|
+
const { native, ...rest } = props;
|
|
82
|
+
const Component = native ? NativeSelect : CustomSelect;
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
84
|
+
// @ts-ignore
|
|
85
|
+
return React.createElement(Component, { ...rest, ref: forwardedRef });
|
|
86
|
+
});
|
|
87
|
+
// ----------------------------------------------------------------------------
|
|
88
|
+
const NativeSelect = React.forwardRef((props, forwardedRef) => {
|
|
89
|
+
const { triggerProps, options, disabled, placeholder, defaultValue: defaultValueProp = placeholder !== undefined ? '' : undefined, value: valueProp, onChange: onChangeProp, size, status, styleType, required, ...rest } = props;
|
|
90
|
+
return (React.createElement(InputWithIcon, { ...rest, ref: forwardedRef },
|
|
91
|
+
React.createElement(SelectButton, { as: 'select', size: size, status: status, styleType: styleType, disabled: disabled, defaultValue: valueProp === undefined ? defaultValueProp : undefined, value: valueProp === null ? '' : valueProp, required: required, ...triggerProps, onKeyDown: mergeEventHandlers(triggerProps?.onKeyDown, (event) => {
|
|
92
|
+
// Firefox does not open the menu on Enter, so we need to do it manually.
|
|
93
|
+
if (event.key === 'Enter') {
|
|
94
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
95
|
+
// @ts-ignore
|
|
96
|
+
event.currentTarget.showPicker?.();
|
|
97
|
+
}
|
|
98
|
+
}), onChange: mergeEventHandlers(triggerProps?.onChange, (event) => {
|
|
99
|
+
onChangeProp?.(event.currentTarget.value, event);
|
|
100
|
+
}) },
|
|
101
|
+
styleType !== 'borderless' && placeholder !== undefined ? (React.createElement("option", { value: '', disabled: true }, placeholder)) : null,
|
|
102
|
+
options.map((option) => (React.createElement("option", { key: option.value, ...option }, option.label)))),
|
|
103
|
+
React.createElement(SelectEndIcon, { disabled: disabled })));
|
|
104
|
+
});
|
|
105
|
+
// ----------------------------------------------------------------------------
|
|
106
|
+
const CustomSelect = React.forwardRef((props, forwardedRef) => {
|
|
71
107
|
const uid = useId();
|
|
72
108
|
const { options, value: valueProp, onChange: onChangeProp, placeholder, disabled = false, size, itemRenderer, selectedItemRenderer, menuClassName, menuStyle, multiple = false, triggerProps, status, popoverProps, ...rest } = props;
|
|
73
109
|
const [isOpen, setIsOpen] = React.useState(false);
|
|
@@ -161,18 +197,13 @@ export const Select = React.forwardRef((props, forwardedRef) => {
|
|
|
161
197
|
});
|
|
162
198
|
return (React.createElement(React.Fragment, null,
|
|
163
199
|
React.createElement(InputWithIcon, { ...rest, ref: useMergedRefs(popover.refs.setPositionReference, forwardedRef) },
|
|
164
|
-
React.createElement(
|
|
165
|
-
'iui-placeholder': (!selectedItems || selectedItems.length === 0) &&
|
|
166
|
-
!!placeholder,
|
|
200
|
+
React.createElement(SelectButton, { ...popover.getReferenceProps(), tabIndex: 0, role: 'combobox', size: size, status: status, "aria-disabled": disabled ? 'true' : undefined, "aria-autocomplete": 'none', "aria-expanded": isOpen, "aria-haspopup": 'listbox', "aria-controls": `${uid}-menu`, ...triggerProps, ref: useMergedRefs(selectRef, triggerProps?.ref, popover.refs.setReference), className: cx({
|
|
201
|
+
'iui-placeholder': (!selectedItems || selectedItems.length === 0) && !!placeholder,
|
|
167
202
|
'iui-disabled': disabled,
|
|
168
203
|
}, triggerProps?.className) },
|
|
169
204
|
(!selectedItems || selectedItems.length === 0) && (React.createElement(Box, { as: 'span', className: 'iui-content' }, placeholder)),
|
|
170
205
|
isMultipleEnabled(selectedItems, multiple) ? (React.createElement(MultipleSelectButton, { selectedItems: selectedItems, selectedItemsRenderer: selectedItemRenderer, tagRenderer: tagRenderer })) : (React.createElement(SingleSelectButton, { selectedItem: selectedItems, selectedItemRenderer: selectedItemRenderer }))),
|
|
171
|
-
React.createElement(
|
|
172
|
-
'iui-disabled': disabled,
|
|
173
|
-
'iui-open': isOpen,
|
|
174
|
-
}) },
|
|
175
|
-
React.createElement(SvgCaretDownSmall, null)),
|
|
206
|
+
React.createElement(SelectEndIcon, { disabled: disabled, isOpen: isOpen }),
|
|
176
207
|
multiple ? (React.createElement(AutoclearingHiddenLiveRegion, { text: liveRegionSelection })) : null),
|
|
177
208
|
popover.open && (React.createElement(Portal, null,
|
|
178
209
|
React.createElement(Menu, { role: 'listbox', className: menuClassName, id: `${uid}-menu`, key: `${uid}-menu`, ...popover.getFloatingProps({
|
|
@@ -184,6 +215,27 @@ export const Select = React.forwardRef((props, forwardedRef) => {
|
|
|
184
215
|
},
|
|
185
216
|
}), ref: popover.refs.setFloating }, menuItems)))));
|
|
186
217
|
});
|
|
218
|
+
// ----------------------------------------------------------------------------
|
|
219
|
+
// Type guards
|
|
220
|
+
const isMultipleEnabled = (variable, multiple) => {
|
|
221
|
+
return multiple;
|
|
222
|
+
};
|
|
223
|
+
// Type guard for multiple did not work
|
|
224
|
+
const isSingleOnChange = (onChange, multiple) => {
|
|
225
|
+
return !multiple;
|
|
226
|
+
};
|
|
227
|
+
// ----------------------------------------------------------------------------
|
|
228
|
+
const SelectButton = React.forwardRef((props, forwardedRef) => {
|
|
229
|
+
const { size, status, styleType = 'default', ...rest } = props;
|
|
230
|
+
return (React.createElement(Box, { "data-iui-size": size, "data-iui-status": status, "data-iui-variant": styleType !== 'default' ? styleType : undefined, ...rest, ref: forwardedRef, className: cx('iui-select-button', props.className) }));
|
|
231
|
+
});
|
|
232
|
+
// ----------------------------------------------------------------------------
|
|
233
|
+
const SelectEndIcon = React.forwardRef((props, forwardedRef) => {
|
|
234
|
+
const { disabled, isOpen, ...rest } = props;
|
|
235
|
+
return (React.createElement(Icon, { "aria-hidden": true, ...rest, ref: forwardedRef, className: cx('iui-end-icon', { 'iui-disabled': disabled, 'iui-open': isOpen }, props.className) },
|
|
236
|
+
React.createElement(SvgCaretDownSmall, null)));
|
|
237
|
+
});
|
|
238
|
+
// ----------------------------------------------------------------------------
|
|
187
239
|
const SingleSelectButton = ({ selectedItem, selectedItemRenderer, }) => {
|
|
188
240
|
const startIcon = selectedItem?.startIcon ?? selectedItem?.icon;
|
|
189
241
|
return (React.createElement(React.Fragment, null,
|
|
@@ -194,6 +246,7 @@ const SingleSelectButton = ({ selectedItem, selectedItemRenderer, }) => {
|
|
|
194
246
|
startIcon && (React.createElement(Box, { as: 'span', className: 'iui-icon', "aria-hidden": true }, startIcon)),
|
|
195
247
|
React.createElement(Box, { as: 'span', className: 'iui-content' }, selectedItem.label)))));
|
|
196
248
|
};
|
|
249
|
+
// ----------------------------------------------------------------------------
|
|
197
250
|
const MultipleSelectButton = ({ selectedItems, selectedItemsRenderer, tagRenderer, }) => {
|
|
198
251
|
const selectedItemsElements = React.useMemo(() => {
|
|
199
252
|
if (!selectedItems) {
|
package/esm/core/Table/Table.js
CHANGED
|
@@ -261,7 +261,8 @@ export const Table = (props) => {
|
|
|
261
261
|
// This is to avoid the old columnOrder from affecting the new columns' columnOrder
|
|
262
262
|
React.useEffect(() => {
|
|
263
263
|
// Check if columns have changed (by value)
|
|
264
|
-
if (
|
|
264
|
+
if (lastPassedColumns.current.length > 0 &&
|
|
265
|
+
JSON.stringify(lastPassedColumns.current) !== JSON.stringify(columns)) {
|
|
265
266
|
instance.setColumnOrder([]);
|
|
266
267
|
}
|
|
267
268
|
lastPassedColumns.current = columns;
|
package/esm/core/Tabs/Tabs.js
CHANGED
|
@@ -42,13 +42,14 @@ const TabList = React.forwardRef((props, ref) => {
|
|
|
42
42
|
}, className), role: 'tablist', ref: refs, ...rest },
|
|
43
43
|
React.createElement(TabListContext.Provider, { value: {
|
|
44
44
|
tabsWidth,
|
|
45
|
+
tablistRef,
|
|
45
46
|
} }, children)));
|
|
46
47
|
});
|
|
47
48
|
TabList.displayName = 'Tabs.TabList';
|
|
48
49
|
const Tab = React.forwardRef((props, forwardedRef) => {
|
|
49
50
|
const { className, children, value, label, ...rest } = props;
|
|
50
51
|
const { orientation, activeValue, setActiveValue, type, setStripeProperties, idPrefix, focusActivationMode, } = useSafeContext(TabsContext);
|
|
51
|
-
const { tabsWidth } = useSafeContext(TabListContext);
|
|
52
|
+
const { tabsWidth, tablistRef } = useSafeContext(TabListContext);
|
|
52
53
|
const tabRef = React.useRef();
|
|
53
54
|
const isActive = activeValue === value;
|
|
54
55
|
const isActiveRef = useLatestRef(isActive);
|
|
@@ -65,13 +66,27 @@ const Tab = React.forwardRef((props, forwardedRef) => {
|
|
|
65
66
|
useLayoutEffect(() => {
|
|
66
67
|
const updateStripe = () => {
|
|
67
68
|
const currentTabRect = tabRef.current?.getBoundingClientRect();
|
|
69
|
+
const tabslistRect = tablistRef.current?.getBoundingClientRect();
|
|
70
|
+
// Using getBoundingClientRect() to get decimal granularity.
|
|
71
|
+
// Not using offsetLeft/offsetTop because they round to the nearest integer.
|
|
72
|
+
// Even minor inaccuracies in the stripe position can cause unexpected scroll/scrollbar.
|
|
73
|
+
// See: https://github.com/iTwin/iTwinUI/issues/1870
|
|
74
|
+
const tabsStripePosition = currentTabRect != null && tabslistRect != null
|
|
75
|
+
? {
|
|
76
|
+
horizontal: currentTabRect.x - tabslistRect.x,
|
|
77
|
+
vertical: currentTabRect.y - tabslistRect.y,
|
|
78
|
+
}
|
|
79
|
+
: {
|
|
80
|
+
horizontal: 0,
|
|
81
|
+
vertical: 0,
|
|
82
|
+
};
|
|
68
83
|
setStripeProperties({
|
|
69
84
|
'--iui-tabs-stripe-size': orientation === 'horizontal'
|
|
70
85
|
? `${currentTabRect?.width}px`
|
|
71
86
|
: `${currentTabRect?.height}px`,
|
|
72
87
|
'--iui-tabs-stripe-position': orientation === 'horizontal'
|
|
73
|
-
? `${
|
|
74
|
-
: `${
|
|
88
|
+
? `${tabsStripePosition.horizontal}px`
|
|
89
|
+
: `${tabsStripePosition.vertical}px`,
|
|
75
90
|
});
|
|
76
91
|
};
|
|
77
92
|
if (type !== 'default' && isActive) {
|
|
@@ -83,6 +98,7 @@ const Tab = React.forwardRef((props, forwardedRef) => {
|
|
|
83
98
|
isActive,
|
|
84
99
|
tabsWidth,
|
|
85
100
|
setStripeProperties,
|
|
101
|
+
tablistRef,
|
|
86
102
|
]);
|
|
87
103
|
const onKeyDown = (event) => {
|
|
88
104
|
if (event.altKey) {
|
package/esm/core/Tile/Tile.d.ts
CHANGED
|
@@ -255,7 +255,7 @@ export declare const Tile: PolymorphicForwardRefComponent<"div", TileLegacyProps
|
|
|
255
255
|
labelProps?: Omit<Omit<Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
|
|
256
256
|
ref?: ((instance: HTMLDivElement | null) => void) | React.RefObject<HTMLDivElement> | null | undefined;
|
|
257
257
|
}, "as" | "children" | "content" | "portal" | keyof {
|
|
258
|
-
placement?: import("@floating-ui/
|
|
258
|
+
placement?: import("@floating-ui/core").Placement | undefined;
|
|
259
259
|
visible?: boolean | undefined;
|
|
260
260
|
onVisibleChange?: ((visible: boolean) => void) | undefined;
|
|
261
261
|
autoUpdateOptions?: {
|
|
@@ -284,7 +284,7 @@ export declare const Tile: PolymorphicForwardRefComponent<"div", TileLegacyProps
|
|
|
284
284
|
content: React.ReactNode;
|
|
285
285
|
children?: React.ReactNode;
|
|
286
286
|
} & import("../utils/index.js").PortalProps & {
|
|
287
|
-
placement?: import("@floating-ui/
|
|
287
|
+
placement?: import("@floating-ui/core").Placement | undefined;
|
|
288
288
|
visible?: boolean | undefined;
|
|
289
289
|
onVisibleChange?: ((visible: boolean) => void) | undefined;
|
|
290
290
|
autoUpdateOptions?: {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { PolymorphicForwardRefComponent } from '../props.js';
|
|
2
|
+
import type { IconProps } from '../../Icon/Icon.js';
|
|
3
|
+
import type { IconButtonProps } from '../../Buttons/IconButton.js';
|
|
2
4
|
export type InputFlexContainerProps = {
|
|
3
5
|
isDisabled?: boolean;
|
|
4
6
|
status?: 'positive' | 'warning' | 'negative';
|
|
@@ -9,3 +11,26 @@ export type InputFlexContainerProps = {
|
|
|
9
11
|
* @private
|
|
10
12
|
*/
|
|
11
13
|
export declare const InputFlexContainer: PolymorphicForwardRefComponent<"div", InputFlexContainerProps>;
|
|
14
|
+
/**
|
|
15
|
+
* Button inside InputFlexContainer that collapses the padding between the button and the input/textarea.
|
|
16
|
+
* @private
|
|
17
|
+
*/
|
|
18
|
+
export declare const InputFlexContainerButton: PolymorphicForwardRefComponent<"button", Omit<IconButtonProps, "styleType"> & {
|
|
19
|
+
/**
|
|
20
|
+
* Style of the button.
|
|
21
|
+
* Use 'borderless' to hide outline.
|
|
22
|
+
* @default 'borderless'
|
|
23
|
+
*/
|
|
24
|
+
styleType?: IconButtonProps['styleType'];
|
|
25
|
+
}>;
|
|
26
|
+
/**
|
|
27
|
+
* Icon inside InputFlexContainer that collapses the padding between the icon and the input/textarea.
|
|
28
|
+
* @private
|
|
29
|
+
*/
|
|
30
|
+
export declare const InputFlexContainerIcon: PolymorphicForwardRefComponent<"span", Omit<IconProps, "padded"> & {
|
|
31
|
+
/**
|
|
32
|
+
* Option to add padding to the icon.
|
|
33
|
+
* @default true
|
|
34
|
+
*/
|
|
35
|
+
padded?: IconProps['padded'];
|
|
36
|
+
}>;
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import cx from 'classnames';
|
|
7
7
|
import { Box } from './Box.js';
|
|
8
|
+
import { Icon } from '../../Icon/Icon.js';
|
|
9
|
+
import { IconButton } from '../../Buttons/IconButton.js';
|
|
8
10
|
/**
|
|
9
11
|
* Utility component for input container with display flex abilities.
|
|
10
12
|
* @private
|
|
@@ -13,3 +15,19 @@ export const InputFlexContainer = React.forwardRef((props, ref) => {
|
|
|
13
15
|
const { isDisabled, status, children, className, size, style, ...rest } = props;
|
|
14
16
|
return (React.createElement(Box, { className: cx('iui-input-flex-container', className), "data-iui-status": status, "data-iui-size": size, "data-iui-disabled": isDisabled ? 'true' : undefined, ref: ref, style: style, ...rest }, children));
|
|
15
17
|
});
|
|
18
|
+
/**
|
|
19
|
+
* Button inside InputFlexContainer that collapses the padding between the button and the input/textarea.
|
|
20
|
+
* @private
|
|
21
|
+
*/
|
|
22
|
+
export const InputFlexContainerButton = React.forwardRef((props, ref) => {
|
|
23
|
+
const { className, ...rest } = props;
|
|
24
|
+
return (React.createElement(IconButton, { ref: ref, className: cx('iui-input-flex-container-icon', className), styleType: 'borderless', ...rest }));
|
|
25
|
+
});
|
|
26
|
+
/**
|
|
27
|
+
* Icon inside InputFlexContainer that collapses the padding between the icon and the input/textarea.
|
|
28
|
+
* @private
|
|
29
|
+
*/
|
|
30
|
+
export const InputFlexContainerIcon = React.forwardRef((props, ref) => {
|
|
31
|
+
const { className, ...rest } = props;
|
|
32
|
+
return (React.createElement(Icon, { ref: ref, className: cx('iui-input-flex-container-icon', className), padded: true, ...rest }));
|
|
33
|
+
});
|