@itwin/itwinui-react 3.5.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/cjs/core/Carousel/Carousel.d.ts +4 -4
  3. package/cjs/core/Carousel/CarouselNavigation.d.ts +4 -4
  4. package/cjs/core/Dialog/Dialog.js +19 -17
  5. package/cjs/core/Dialog/DialogContext.d.ts +4 -0
  6. package/cjs/core/Dialog/DialogMain.js +1 -1
  7. package/cjs/core/Dialog/DialogTitleBar.js +1 -1
  8. package/cjs/core/InputGroup/InputGroup.d.ts +4 -0
  9. package/cjs/core/InputGroup/InputGroup.js +20 -4
  10. package/cjs/core/InputWithDecorations/InputWithDecorations.d.ts +18 -62
  11. package/cjs/core/InputWithDecorations/InputWithDecorations.js +21 -6
  12. package/cjs/core/LabeledSelect/LabeledSelect.d.ts +376 -4
  13. package/cjs/core/LabeledSelect/LabeledSelect.js +1 -1
  14. package/cjs/core/Popover/Popover.d.ts +6 -3
  15. package/cjs/core/SearchBox/SearchBox.js +4 -6
  16. package/cjs/core/Select/Select.d.ts +194 -101
  17. package/cjs/core/Select/Select.js +71 -18
  18. package/cjs/core/Table/Table.js +2 -1
  19. package/cjs/core/Tabs/Tabs.js +19 -3
  20. package/cjs/core/Tile/Tile.d.ts +2 -2
  21. package/cjs/core/utils/components/InputFlexContainer.d.ts +25 -0
  22. package/cjs/core/utils/components/InputFlexContainer.js +19 -1
  23. package/cjs/core/utils/components/LineClamp.d.ts +5 -0
  24. package/cjs/core/utils/components/LineClamp.js +49 -0
  25. package/cjs/core/utils/components/index.d.ts +1 -0
  26. package/cjs/core/utils/components/index.js +1 -0
  27. package/cjs/core/utils/icons/SvgStatusError.js +1 -1
  28. package/esm/core/Carousel/Carousel.d.ts +4 -4
  29. package/esm/core/Carousel/CarouselNavigation.d.ts +4 -4
  30. package/esm/core/Dialog/Dialog.js +19 -17
  31. package/esm/core/Dialog/DialogContext.d.ts +4 -0
  32. package/esm/core/Dialog/DialogMain.js +1 -1
  33. package/esm/core/Dialog/DialogTitleBar.js +1 -1
  34. package/esm/core/InputGroup/InputGroup.d.ts +4 -0
  35. package/esm/core/InputGroup/InputGroup.js +20 -4
  36. package/esm/core/InputWithDecorations/InputWithDecorations.d.ts +18 -62
  37. package/esm/core/InputWithDecorations/InputWithDecorations.js +22 -7
  38. package/esm/core/LabeledSelect/LabeledSelect.d.ts +376 -4
  39. package/esm/core/LabeledSelect/LabeledSelect.js +1 -1
  40. package/esm/core/Popover/Popover.d.ts +6 -3
  41. package/esm/core/SearchBox/SearchBox.js +5 -7
  42. package/esm/core/Select/Select.d.ts +194 -101
  43. package/esm/core/Select/Select.js +72 -19
  44. package/esm/core/Table/Table.js +2 -1
  45. package/esm/core/Tabs/Tabs.js +19 -3
  46. package/esm/core/Tile/Tile.d.ts +2 -2
  47. package/esm/core/utils/components/InputFlexContainer.d.ts +25 -0
  48. package/esm/core/utils/components/InputFlexContainer.js +18 -0
  49. package/esm/core/utils/components/LineClamp.d.ts +5 -0
  50. package/esm/core/utils/components/LineClamp.js +23 -0
  51. package/esm/core/utils/components/index.d.ts +1 -0
  52. package/esm/core/utils/components/index.js +1 -0
  53. package/esm/core/utils/icons/SvgStatusError.js +1 -1
  54. package/package.json +27 -27
  55. 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
- export type ItemRendererProps = {
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
- * Close handler that closes the dropdown.
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
- close: () => void;
80
+ native: true;
81
+ } & NativeSelectProps) | ({
82
+ native?: false;
83
+ } & CustomSelectProps<T> & {
9
84
  /**
10
- * Indicates whether an item is selected.
85
+ * styleType is only supported for `<Select native>`.
11
86
  */
12
- isSelected: boolean;
13
- };
14
- export type SelectOption<T> = {
87
+ styleType?: never;
88
+ }));
89
+ type NativeSelectProps = SelectCommonProps & {
15
90
  /**
16
- * Label of the item used in dropdown list and when selected.
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
- label: string;
97
+ value?: string | null;
19
98
  /**
20
- * Sublabel of the item shown below the label.
99
+ * Callback invoked when the selected value changes.
21
100
  */
22
- sublabel?: React.ReactNode;
101
+ onChange?: (value: string, event: React.ChangeEvent<HTMLSelectElement>) => void;
23
102
  /**
24
- * Modify height of the item.
25
- * Use 'large' when any of the select options have `sublabel`.
103
+ * Array of options that populates the select menu.
26
104
  *
27
- * Defaults to 'large' if `sublabel` provided, otherwise 'default'.
105
+ * The `value` property of each option must be a string.
28
106
  */
29
- size?: 'default' | 'large';
107
+ options: Array<{
108
+ label: string;
109
+ value: string;
110
+ disabled?: boolean;
111
+ }>;
30
112
  /**
31
- * Value of the item.
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
- value: T;
118
+ defaultValue?: string;
34
119
  /**
35
- * @deprecated Use startIcon
36
- * SVG icon component shown on the left.
120
+ * Props to pass to the select element.
37
121
  */
38
- icon?: JSX.Element;
122
+ triggerProps?: Omit<React.ComponentPropsWithRef<'select'>, 'size'>;
123
+ required?: boolean;
124
+ multiple?: never;
125
+ } & NativeSelectStyleTypeProps;
126
+ type NativeSelectStyleTypeProps = {
39
127
  /**
40
- * SVG icon component shown on the left.
128
+ * Style of the select.
129
+ * Use 'borderless' to hide outline.
130
+ * @default 'default'
41
131
  */
42
- startIcon?: JSX.Element;
132
+ styleType?: 'default';
43
133
  /**
44
- * Item is disabled.
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
- * Any other props.
153
+ * Modify size of select.
49
154
  */
50
- [key: string]: unknown;
51
- } & CommonProps;
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 SelectProps<T> = {
220
+ export type ItemRendererProps = {
82
221
  /**
83
- * Array of options that populates the select menu.
222
+ * Close handler that closes the dropdown.
84
223
  */
85
- options: SelectOption<T>[];
224
+ close: () => void;
86
225
  /**
87
- * Placeholder when no item is selected.
226
+ * Indicates whether an item is selected.
88
227
  */
89
- placeholder?: React.ReactNode;
228
+ isSelected: boolean;
229
+ };
230
+ export type SelectOption<T> = {
90
231
  /**
91
- * Disables select.
92
- * @default false
232
+ * Label of the item used in dropdown list and when selected.
93
233
  */
94
- disabled?: boolean;
234
+ label: string;
95
235
  /**
96
- * Modify size of select.
236
+ * Sublabel of the item shown below the label.
97
237
  */
98
- size?: 'small' | 'large';
238
+ sublabel?: React.ReactNode;
99
239
  /**
100
- * Status of select.
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
- status?: 'positive' | 'warning' | 'negative';
245
+ size?: 'default' | 'large';
103
246
  /**
104
- * Custom renderer for an item in the dropdown list. `MenuItem` item props are going to be populated if not provided.
247
+ * Value of the item.
105
248
  */
106
- itemRenderer?: (option: SelectOption<T>, itemProps: ItemRendererProps) => JSX.Element;
249
+ value: T;
107
250
  /**
108
- * Custom class for menu.
251
+ * @deprecated Use startIcon
252
+ * SVG icon component shown on the left.
109
253
  */
110
- menuClassName?: string;
254
+ icon?: JSX.Element;
111
255
  /**
112
- * Custom style for menu.
256
+ * SVG icon component shown on the left.
113
257
  */
114
- menuStyle?: React.CSSProperties;
258
+ startIcon?: JSX.Element;
115
259
  /**
116
- * Props to customize Popover behavior.
260
+ * Item is disabled.
117
261
  */
118
- popoverProps?: Pick<Parameters<typeof usePopover>[0], 'visible' | 'onVisibleChange' | 'placement' | 'matchWidth' | 'closeOnOutsideClick'>;
262
+ disabled?: boolean;
119
263
  /**
120
- * Props to pass to the select button (trigger) element.
264
+ * Any other props.
121
265
  */
122
- triggerProps?: React.ComponentPropsWithRef<'div'>;
123
- } & SelectMultipleTypeProps<T> & Omit<React.ComponentPropsWithoutRef<'div'>, 'size' | 'disabled' | 'placeholder' | 'onChange'>;
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
- const isMultipleEnabled = (variable, multiple) => {
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<number> options={[{ value: 1, label: 'Option 1' }, { value: 2, label: 'Option 2' }, { value: 3, label: 'Option 3' }]} />
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<number>
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<string>
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(Box, { ...popover.getReferenceProps(), tabIndex: 0, role: 'combobox', "data-iui-size": size, "data-iui-status": status, "aria-disabled": disabled, "aria-autocomplete": 'none', "aria-expanded": isOpen, "aria-haspopup": 'listbox', "aria-controls": `${uid}-menu`, ...triggerProps, ref: useMergedRefs(selectRef, triggerProps?.ref, popover.refs.setReference), className: cx('iui-select-button', {
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(Icon, { as: 'span', "aria-hidden": true, className: cx('iui-end-icon', {
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) {
@@ -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 (JSON.stringify(lastPassedColumns.current) !== JSON.stringify(columns)) {
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;
@@ -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
- ? `${tabRef.current?.offsetLeft}px`
74
- : `${tabRef.current?.offsetTop}px`,
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) {
@@ -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/utils").Placement | undefined;
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/utils").Placement | undefined;
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
+ });
@@ -0,0 +1,5 @@
1
+ import type { PolymorphicForwardRefComponent } from '../props.js';
2
+ /** @private */
3
+ export declare const LineClamp: PolymorphicForwardRefComponent<"div", {
4
+ lines?: number | undefined;
5
+ }>;