@itwin/itwinui-react 3.4.2 → 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 (79) hide show
  1. package/CHANGELOG.md +46 -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/ProgressIndicators/ProgressLinear.js +6 -1
  16. package/cjs/core/ProgressIndicators/ProgressRadial.js +8 -3
  17. package/cjs/core/SearchBox/SearchBox.js +4 -6
  18. package/cjs/core/Select/Select.d.ts +197 -102
  19. package/cjs/core/Select/Select.js +72 -19
  20. package/cjs/core/Table/Table.js +2 -1
  21. package/cjs/core/Tabs/Tabs.js +19 -3
  22. package/cjs/core/Tile/Tile.d.ts +2 -2
  23. package/cjs/core/ToggleSwitch/ToggleSwitch.d.ts +4 -2
  24. package/cjs/core/ToggleSwitch/ToggleSwitch.js +5 -11
  25. package/cjs/core/Typography/Anchor.d.ts +21 -1
  26. package/cjs/core/Typography/Anchor.js +38 -3
  27. package/cjs/core/VisuallyHidden/VisuallyHidden.js +19 -3
  28. package/cjs/core/utils/components/InputFlexContainer.d.ts +25 -0
  29. package/cjs/core/utils/components/InputFlexContainer.js +19 -1
  30. package/cjs/core/utils/components/LineClamp.d.ts +5 -0
  31. package/cjs/core/utils/components/LineClamp.js +49 -0
  32. package/cjs/core/utils/components/ShadowRoot.d.ts +2 -1
  33. package/cjs/core/utils/components/ShadowRoot.js +19 -4
  34. package/cjs/core/utils/components/index.d.ts +1 -0
  35. package/cjs/core/utils/components/index.js +1 -0
  36. package/cjs/core/utils/functions/polymorphic.d.ts +5 -3
  37. package/cjs/core/utils/functions/polymorphic.js +20 -5
  38. package/cjs/core/utils/icons/Svg.js +5 -1
  39. package/cjs/core/utils/icons/SvgStatusError.js +1 -1
  40. package/esm/core/Carousel/Carousel.d.ts +4 -4
  41. package/esm/core/Carousel/CarouselNavigation.d.ts +4 -4
  42. package/esm/core/Dialog/Dialog.js +19 -17
  43. package/esm/core/Dialog/DialogContext.d.ts +4 -0
  44. package/esm/core/Dialog/DialogMain.js +1 -1
  45. package/esm/core/Dialog/DialogTitleBar.js +1 -1
  46. package/esm/core/InputGroup/InputGroup.d.ts +4 -0
  47. package/esm/core/InputGroup/InputGroup.js +20 -4
  48. package/esm/core/InputWithDecorations/InputWithDecorations.d.ts +18 -62
  49. package/esm/core/InputWithDecorations/InputWithDecorations.js +22 -7
  50. package/esm/core/LabeledSelect/LabeledSelect.d.ts +376 -4
  51. package/esm/core/LabeledSelect/LabeledSelect.js +1 -1
  52. package/esm/core/Popover/Popover.d.ts +6 -3
  53. package/esm/core/ProgressIndicators/ProgressLinear.js +7 -2
  54. package/esm/core/ProgressIndicators/ProgressRadial.js +9 -4
  55. package/esm/core/SearchBox/SearchBox.js +5 -7
  56. package/esm/core/Select/Select.d.ts +197 -102
  57. package/esm/core/Select/Select.js +73 -20
  58. package/esm/core/Table/Table.js +2 -1
  59. package/esm/core/Tabs/Tabs.js +19 -3
  60. package/esm/core/Tile/Tile.d.ts +2 -2
  61. package/esm/core/ToggleSwitch/ToggleSwitch.d.ts +4 -2
  62. package/esm/core/ToggleSwitch/ToggleSwitch.js +6 -12
  63. package/esm/core/Typography/Anchor.d.ts +21 -1
  64. package/esm/core/Typography/Anchor.js +11 -2
  65. package/esm/core/VisuallyHidden/VisuallyHidden.js +19 -3
  66. package/esm/core/utils/components/InputFlexContainer.d.ts +25 -0
  67. package/esm/core/utils/components/InputFlexContainer.js +18 -0
  68. package/esm/core/utils/components/LineClamp.d.ts +5 -0
  69. package/esm/core/utils/components/LineClamp.js +23 -0
  70. package/esm/core/utils/components/ShadowRoot.d.ts +2 -1
  71. package/esm/core/utils/components/ShadowRoot.js +19 -4
  72. package/esm/core/utils/components/index.d.ts +1 -0
  73. package/esm/core/utils/components/index.js +1 -0
  74. package/esm/core/utils/functions/polymorphic.d.ts +5 -3
  75. package/esm/core/utils/functions/polymorphic.js +20 -5
  76. package/esm/core/utils/icons/Svg.js +5 -1
  77. package/esm/core/utils/icons/SvgStatusError.js +1 -1
  78. package/package.json +27 -27
  79. package/styles.css +39 -38
@@ -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
  /**
@@ -64,8 +203,10 @@ export type SelectMultipleTypeProps<T> = {
64
203
  /**
65
204
  * Selected option value.
66
205
  * If `multiple` is enabled, it is an array of values.
206
+ *
207
+ * Pass `null` to reset the value.
67
208
  */
68
- value?: T;
209
+ value?: T | null;
69
210
  /**
70
211
  * Callback function handling change event on select.
71
212
  */
@@ -76,98 +217,52 @@ export type SelectMultipleTypeProps<T> = {
76
217
  value?: T[];
77
218
  onChange?: (value: T, event: SelectValueChangeEvent) => void;
78
219
  };
79
- export type SelectProps<T> = {
220
+ export type ItemRendererProps = {
80
221
  /**
81
- * Array of options that populates the select menu.
222
+ * Close handler that closes the dropdown.
82
223
  */
83
- options: SelectOption<T>[];
224
+ close: () => void;
84
225
  /**
85
- * Placeholder when no item is selected.
226
+ * Indicates whether an item is selected.
86
227
  */
87
- placeholder?: React.ReactNode;
228
+ isSelected: boolean;
229
+ };
230
+ export type SelectOption<T> = {
88
231
  /**
89
- * Disables select.
90
- * @default false
232
+ * Label of the item used in dropdown list and when selected.
91
233
  */
92
- disabled?: boolean;
234
+ label: string;
93
235
  /**
94
- * Modify size of select.
236
+ * Sublabel of the item shown below the label.
95
237
  */
96
- size?: 'small' | 'large';
238
+ sublabel?: React.ReactNode;
97
239
  /**
98
- * 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'.
99
244
  */
100
- status?: 'positive' | 'warning' | 'negative';
245
+ size?: 'default' | 'large';
101
246
  /**
102
- * 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.
103
248
  */
104
- itemRenderer?: (option: SelectOption<T>, itemProps: ItemRendererProps) => JSX.Element;
249
+ value: T;
105
250
  /**
106
- * Custom class for menu.
251
+ * @deprecated Use startIcon
252
+ * SVG icon component shown on the left.
107
253
  */
108
- menuClassName?: string;
254
+ icon?: JSX.Element;
109
255
  /**
110
- * Custom style for menu.
256
+ * SVG icon component shown on the left.
111
257
  */
112
- menuStyle?: React.CSSProperties;
258
+ startIcon?: JSX.Element;
113
259
  /**
114
- * Props to customize Popover behavior.
260
+ * Item is disabled.
115
261
  */
116
- popoverProps?: Pick<Parameters<typeof usePopover>[0], 'visible' | 'onVisibleChange' | 'placement' | 'matchWidth' | 'closeOnOutsideClick'>;
262
+ disabled?: boolean;
117
263
  /**
118
- * Props to pass to the select button (trigger) element.
264
+ * Any other props.
119
265
  */
120
- triggerProps?: React.ComponentPropsWithRef<'div'>;
121
- } & SelectMultipleTypeProps<T> & Omit<React.ComponentPropsWithoutRef<'div'>, 'size' | 'disabled' | 'placeholder' | 'onChange'>;
122
- /**
123
- * Select component to select value from options.
124
- * Generic type is used for value. It prevents you from mistakenly using other types in `options`, `value` and `onChange`.
125
- * @example
126
- * <caption>Basic select.</caption>
127
- * <Select<number> options={[{ value: 1, label: 'Option 1' }, { value: 2, label: 'Option 2' }, { value: 3, label: 'Option 3' }]} />
128
- * @example
129
- * <caption>Disabled select with placeholder.</caption>
130
- * <Select
131
- * disabled={true}
132
- * placeholder='Placeholder text'
133
- * options={[{ value: 1, label: 'Option 1' }, { value: 2, label: 'Option 2' }, { value: 3, label: 'Option 3' }]}
134
- * />
135
- * @example
136
- * <caption>Select with selected value and change handler.</caption>
137
- * <Select<number>
138
- * value={selectedValue}
139
- * onChange={(value) => setSelectedValue(value)}
140
- * options={[{ value: 1, label: 'Option 1' }, { value: 2, label: 'Option 2' }, { value: 3, label: 'Option 3' }]}
141
- * />
142
- * @example
143
- * <caption>Select using custom renderers for menu items and selected value.</caption>
144
- * <Select<string>
145
- * options={[
146
- * { value: 'yellow', label: 'Yellow' },
147
- * { value: 'green', label: 'Green' },
148
- * { value: 'red', label: 'Red' },
149
- * ]}
150
- * value={selectedValue}
151
- * placeholder='Placeholder text'
152
- * itemRenderer={(option, itemProps) => (
153
- * <MenuItem
154
- * style={{ color: option.value }}
155
- * isSelected={itemProps.isSelected}
156
- * onClick={() => {
157
- * setSelectedValue(option.value);
158
- * itemProps.close();
159
- * }}
160
- * role='option'
161
- * ref={(el) => itemProps.isSelected && el?.scrollIntoView()}
162
- * >
163
- * {option.label}
164
- * </MenuItem>
165
- * )}
166
- * selectedItemRenderer={(option) => (
167
- * <span style={{ backgroundColor: option.value }}>{option.label}</span>
168
- * )}
169
- * />
170
- */
171
- export declare const Select: <T>(props: SelectProps<T> & {
172
- ref?: React.ForwardedRef<HTMLElement> | undefined;
173
- }) => JSX.Element;
266
+ [key: string]: unknown;
267
+ } & CommonProps;
268
+ export {};
@@ -40,19 +40,29 @@ const SelectTag_js_1 = require("./SelectTag.js");
40
40
  const SelectTagContainer_js_1 = require("./SelectTagContainer.js");
41
41
  const Icon_js_1 = require("../Icon/Icon.js");
42
42
  const Popover_js_1 = require("../Popover/Popover.js");
43
- const isMultipleEnabled = (variable, multiple) => {
44
- return multiple;
45
- };
46
- // Type guard for multiple did not work
47
- const isSingleOnChange = (onChange, multiple) => {
48
- return !multiple;
49
- };
43
+ // ----------------------------------------------------------------------------
50
44
  /**
51
45
  * Select component to select value from options.
52
46
  * Generic type is used for value. It prevents you from mistakenly using other types in `options`, `value` and `onChange`.
53
47
  * @example
54
48
  * <caption>Basic select.</caption>
55
- * <Select<number> options={[{ value: 1, label: 'Option 1' }, { value: 2, label: 'Option 2' }, { value: 3, label: 'Option 3' }]} />
49
+ * <Select
50
+ * options={[
51
+ * { value: '1', label: 'Option 1' },
52
+ * { value: '2', label: 'Option 2' },
53
+ * { value: '3', label: 'Option 3' },
54
+ * ]}
55
+ * />
56
+ * @example
57
+ * <caption>Native select.</caption>
58
+ * <Select
59
+ * native
60
+ * options={[
61
+ * { value: '1', label: 'Option 1' },
62
+ * { value: '2', label: 'Option 2' },
63
+ * { value: '3', label: 'Option 3' },
64
+ * ]}
65
+ * />
56
66
  * @example
57
67
  * <caption>Disabled select with placeholder.</caption>
58
68
  * <Select
@@ -62,14 +72,14 @@ const isSingleOnChange = (onChange, multiple) => {
62
72
  * />
63
73
  * @example
64
74
  * <caption>Select with selected value and change handler.</caption>
65
- * <Select<number>
75
+ * <Select
66
76
  * value={selectedValue}
67
77
  * onChange={(value) => setSelectedValue(value)}
68
78
  * options={[{ value: 1, label: 'Option 1' }, { value: 2, label: 'Option 2' }, { value: 3, label: 'Option 3' }]}
69
79
  * />
70
80
  * @example
71
81
  * <caption>Select using custom renderers for menu items and selected value.</caption>
72
- * <Select<string>
82
+ * <Select
73
83
  * options={[
74
84
  * { value: 'yellow', label: 'Yellow' },
75
85
  * { value: 'green', label: 'Green' },
@@ -97,12 +107,38 @@ const isSingleOnChange = (onChange, multiple) => {
97
107
  * />
98
108
  */
99
109
  exports.Select = React.forwardRef((props, forwardedRef) => {
110
+ const { native, ...rest } = props;
111
+ const Component = native ? NativeSelect : CustomSelect;
112
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
113
+ // @ts-ignore
114
+ return React.createElement(Component, { ...rest, ref: forwardedRef });
115
+ });
116
+ // ----------------------------------------------------------------------------
117
+ const NativeSelect = React.forwardRef((props, forwardedRef) => {
118
+ const { triggerProps, options, disabled, placeholder, defaultValue: defaultValueProp = placeholder !== undefined ? '' : undefined, value: valueProp, onChange: onChangeProp, size, status, styleType, required, ...rest } = props;
119
+ return (React.createElement(index_js_1.InputWithIcon, { ...rest, ref: forwardedRef },
120
+ 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: (0, index_js_1.mergeEventHandlers)(triggerProps?.onKeyDown, (event) => {
121
+ // Firefox does not open the menu on Enter, so we need to do it manually.
122
+ if (event.key === 'Enter') {
123
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
124
+ // @ts-ignore
125
+ event.currentTarget.showPicker?.();
126
+ }
127
+ }), onChange: (0, index_js_1.mergeEventHandlers)(triggerProps?.onChange, (event) => {
128
+ onChangeProp?.(event.currentTarget.value, event);
129
+ }) },
130
+ styleType !== 'borderless' && placeholder !== undefined ? (React.createElement("option", { value: '', disabled: true }, placeholder)) : null,
131
+ options.map((option) => (React.createElement("option", { key: option.value, ...option }, option.label)))),
132
+ React.createElement(SelectEndIcon, { disabled: disabled })));
133
+ });
134
+ // ----------------------------------------------------------------------------
135
+ const CustomSelect = React.forwardRef((props, forwardedRef) => {
100
136
  const uid = (0, index_js_1.useId)();
101
137
  const { options, value: valueProp, onChange: onChangeProp, placeholder, disabled = false, size, itemRenderer, selectedItemRenderer, menuClassName, menuStyle, multiple = false, triggerProps, status, popoverProps, ...rest } = props;
102
138
  const [isOpen, setIsOpen] = React.useState(false);
103
139
  const [liveRegionSelection, setLiveRegionSelection] = React.useState('');
104
140
  const [uncontrolledValue, setUncontrolledValue] = React.useState();
105
- const value = valueProp ?? uncontrolledValue;
141
+ const value = valueProp !== undefined ? valueProp : uncontrolledValue;
106
142
  const onChangeRef = (0, index_js_1.useLatestRef)(onChangeProp);
107
143
  const selectRef = React.useRef(null);
108
144
  const show = React.useCallback(() => {
@@ -190,18 +226,13 @@ exports.Select = React.forwardRef((props, forwardedRef) => {
190
226
  });
191
227
  return (React.createElement(React.Fragment, null,
192
228
  React.createElement(index_js_1.InputWithIcon, { ...rest, ref: (0, index_js_1.useMergedRefs)(popover.refs.setPositionReference, forwardedRef) },
193
- React.createElement(index_js_1.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: (0, index_js_1.useMergedRefs)(selectRef, triggerProps?.ref, popover.refs.setReference), className: (0, classnames_1.default)('iui-select-button', {
194
- 'iui-placeholder': (!selectedItems || selectedItems.length === 0) &&
195
- !!placeholder,
229
+ 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: (0, index_js_1.useMergedRefs)(selectRef, triggerProps?.ref, popover.refs.setReference), className: (0, classnames_1.default)({
230
+ 'iui-placeholder': (!selectedItems || selectedItems.length === 0) && !!placeholder,
196
231
  'iui-disabled': disabled,
197
232
  }, triggerProps?.className) },
198
233
  (!selectedItems || selectedItems.length === 0) && (React.createElement(index_js_1.Box, { as: 'span', className: 'iui-content' }, placeholder)),
199
234
  isMultipleEnabled(selectedItems, multiple) ? (React.createElement(MultipleSelectButton, { selectedItems: selectedItems, selectedItemsRenderer: selectedItemRenderer, tagRenderer: tagRenderer })) : (React.createElement(SingleSelectButton, { selectedItem: selectedItems, selectedItemRenderer: selectedItemRenderer }))),
200
- React.createElement(Icon_js_1.Icon, { as: 'span', "aria-hidden": true, className: (0, classnames_1.default)('iui-end-icon', {
201
- 'iui-disabled': disabled,
202
- 'iui-open': isOpen,
203
- }) },
204
- React.createElement(index_js_1.SvgCaretDownSmall, null)),
235
+ React.createElement(SelectEndIcon, { disabled: disabled, isOpen: isOpen }),
205
236
  multiple ? (React.createElement(index_js_1.AutoclearingHiddenLiveRegion, { text: liveRegionSelection })) : null),
206
237
  popover.open && (React.createElement(index_js_1.Portal, null,
207
238
  React.createElement(Menu_js_1.Menu, { role: 'listbox', className: menuClassName, id: `${uid}-menu`, key: `${uid}-menu`, ...popover.getFloatingProps({
@@ -213,6 +244,27 @@ exports.Select = React.forwardRef((props, forwardedRef) => {
213
244
  },
214
245
  }), ref: popover.refs.setFloating }, menuItems)))));
215
246
  });
247
+ // ----------------------------------------------------------------------------
248
+ // Type guards
249
+ const isMultipleEnabled = (variable, multiple) => {
250
+ return multiple;
251
+ };
252
+ // Type guard for multiple did not work
253
+ const isSingleOnChange = (onChange, multiple) => {
254
+ return !multiple;
255
+ };
256
+ // ----------------------------------------------------------------------------
257
+ const SelectButton = React.forwardRef((props, forwardedRef) => {
258
+ const { size, status, styleType = 'default', ...rest } = props;
259
+ return (React.createElement(index_js_1.Box, { "data-iui-size": size, "data-iui-status": status, "data-iui-variant": styleType !== 'default' ? styleType : undefined, ...rest, ref: forwardedRef, className: (0, classnames_1.default)('iui-select-button', props.className) }));
260
+ });
261
+ // ----------------------------------------------------------------------------
262
+ const SelectEndIcon = React.forwardRef((props, forwardedRef) => {
263
+ const { disabled, isOpen, ...rest } = props;
264
+ return (React.createElement(Icon_js_1.Icon, { "aria-hidden": true, ...rest, ref: forwardedRef, className: (0, classnames_1.default)('iui-end-icon', { 'iui-disabled': disabled, 'iui-open': isOpen }, props.className) },
265
+ React.createElement(index_js_1.SvgCaretDownSmall, null)));
266
+ });
267
+ // ----------------------------------------------------------------------------
216
268
  const SingleSelectButton = ({ selectedItem, selectedItemRenderer, }) => {
217
269
  const startIcon = selectedItem?.startIcon ?? selectedItem?.icon;
218
270
  return (React.createElement(React.Fragment, null,
@@ -223,6 +275,7 @@ const SingleSelectButton = ({ selectedItem, selectedItemRenderer, }) => {
223
275
  startIcon && (React.createElement(index_js_1.Box, { as: 'span', className: 'iui-icon', "aria-hidden": true }, startIcon)),
224
276
  React.createElement(index_js_1.Box, { as: 'span', className: 'iui-content' }, selectedItem.label)))));
225
277
  };
278
+ // ----------------------------------------------------------------------------
226
279
  const MultipleSelectButton = ({ selectedItems, selectedItemsRenderer, tagRenderer, }) => {
227
280
  const selectedItemsElements = React.useMemo(() => {
228
281
  if (!selectedItems) {
@@ -290,7 +290,8 @@ const Table = (props) => {
290
290
  // This is to avoid the old columnOrder from affecting the new columns' columnOrder
291
291
  React.useEffect(() => {
292
292
  // Check if columns have changed (by value)
293
- if (JSON.stringify(lastPassedColumns.current) !== JSON.stringify(columns)) {
293
+ if (lastPassedColumns.current.length > 0 &&
294
+ JSON.stringify(lastPassedColumns.current) !== JSON.stringify(columns)) {
294
295
  instance.setColumnOrder([]);
295
296
  }
296
297
  lastPassedColumns.current = columns;
@@ -71,13 +71,14 @@ const TabList = React.forwardRef((props, ref) => {
71
71
  }, className), role: 'tablist', ref: refs, ...rest },
72
72
  React.createElement(TabListContext.Provider, { value: {
73
73
  tabsWidth,
74
+ tablistRef,
74
75
  } }, children)));
75
76
  });
76
77
  TabList.displayName = 'Tabs.TabList';
77
78
  const Tab = React.forwardRef((props, forwardedRef) => {
78
79
  const { className, children, value, label, ...rest } = props;
79
80
  const { orientation, activeValue, setActiveValue, type, setStripeProperties, idPrefix, focusActivationMode, } = (0, index_js_1.useSafeContext)(TabsContext);
80
- const { tabsWidth } = (0, index_js_1.useSafeContext)(TabListContext);
81
+ const { tabsWidth, tablistRef } = (0, index_js_1.useSafeContext)(TabListContext);
81
82
  const tabRef = React.useRef();
82
83
  const isActive = activeValue === value;
83
84
  const isActiveRef = (0, index_js_1.useLatestRef)(isActive);
@@ -94,13 +95,27 @@ const Tab = React.forwardRef((props, forwardedRef) => {
94
95
  (0, index_js_1.useLayoutEffect)(() => {
95
96
  const updateStripe = () => {
96
97
  const currentTabRect = tabRef.current?.getBoundingClientRect();
98
+ const tabslistRect = tablistRef.current?.getBoundingClientRect();
99
+ // Using getBoundingClientRect() to get decimal granularity.
100
+ // Not using offsetLeft/offsetTop because they round to the nearest integer.
101
+ // Even minor inaccuracies in the stripe position can cause unexpected scroll/scrollbar.
102
+ // See: https://github.com/iTwin/iTwinUI/issues/1870
103
+ const tabsStripePosition = currentTabRect != null && tabslistRect != null
104
+ ? {
105
+ horizontal: currentTabRect.x - tabslistRect.x,
106
+ vertical: currentTabRect.y - tabslistRect.y,
107
+ }
108
+ : {
109
+ horizontal: 0,
110
+ vertical: 0,
111
+ };
97
112
  setStripeProperties({
98
113
  '--iui-tabs-stripe-size': orientation === 'horizontal'
99
114
  ? `${currentTabRect?.width}px`
100
115
  : `${currentTabRect?.height}px`,
101
116
  '--iui-tabs-stripe-position': orientation === 'horizontal'
102
- ? `${tabRef.current?.offsetLeft}px`
103
- : `${tabRef.current?.offsetTop}px`,
117
+ ? `${tabsStripePosition.horizontal}px`
118
+ : `${tabsStripePosition.vertical}px`,
104
119
  });
105
120
  };
106
121
  if (type !== 'default' && isActive) {
@@ -112,6 +127,7 @@ const Tab = React.forwardRef((props, forwardedRef) => {
112
127
  isActive,
113
128
  tabsWidth,
114
129
  setStripeProperties,
130
+ tablistRef,
115
131
  ]);
116
132
  const onKeyDown = (event) => {
117
133
  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?: {
@@ -17,9 +17,11 @@ type ToggleSwitchProps = {
17
17
  */
18
18
  size?: 'default';
19
19
  /**
20
- * Icon inside the toggle switch. Shown only when toggle is checked and size is not small.
20
+ * Custom icon inside the toggle switch. Shown only when toggle is checked and size is not small.
21
+ *
22
+ * Will override the default checkmark icon.
21
23
  */
22
- icon?: JSX.Element;
24
+ icon?: JSX.Element | null;
23
25
  } | {
24
26
  size: 'small';
25
27
  icon?: never;
@@ -53,21 +53,15 @@ const index_js_1 = require("../utils/index.js");
53
53
  * <ToggleSwitch label='With icon toggle' icon={<svg viewBox='0 0 16 16'><path d='M1 1v14h14V1H1zm13 1.7v10.6L8.7 8 14 2.7zM8 7.3L2.7 2h10.6L8 7.3zm-.7.7L2 13.3V2.7L7.3 8zm.7.7l5.3 5.3H2.7L8 8.7z' /></svg>} />
54
54
  */
55
55
  exports.ToggleSwitch = React.forwardRef((props, ref) => {
56
- let icon;
57
- if (props.size !== 'small') {
58
- icon = props.icon;
59
- props = { ...props };
60
- delete props.icon;
61
- }
62
- const { disabled = false, labelPosition = 'right', label, className, style, size = 'default', ...rest } = props;
63
- const inputElementRef = React.useRef(null);
64
- const refs = (0, index_js_1.useMergedRefs)(inputElementRef, ref);
56
+ const { disabled = false, labelPosition = 'right', label, className, style, size = 'default', icon: iconProp, ...rest } = props;
57
+ // Disallow custom icon for small size, but keep the default checkmark when prop is not passed.
58
+ const shouldShowIcon = iconProp === undefined || (iconProp !== null && size !== 'small');
65
59
  return (React.createElement(index_js_1.Box, { as: label ? 'label' : 'div', className: (0, classnames_1.default)('iui-toggle-switch-wrapper', {
66
60
  'iui-disabled': disabled,
67
61
  'iui-label-on-right': label && labelPosition === 'right',
68
62
  'iui-label-on-left': label && labelPosition === 'left',
69
63
  }, className), "data-iui-size": size, style: style },
70
- React.createElement(index_js_1.Box, { as: 'input', className: 'iui-toggle-switch', type: 'checkbox', role: 'switch', disabled: disabled, ref: refs, ...rest }),
71
- icon && size !== 'small' && (React.createElement(index_js_1.Box, { as: 'span', className: 'iui-toggle-switch-icon', "aria-hidden": true }, icon)),
64
+ React.createElement(index_js_1.Box, { as: 'input', className: 'iui-toggle-switch', type: 'checkbox', role: 'switch', disabled: disabled, ref: ref, ...rest }),
65
+ shouldShowIcon && (React.createElement(index_js_1.Box, { as: 'span', className: 'iui-toggle-switch-icon', "aria-hidden": true }, iconProp || React.createElement(index_js_1.SvgCheckmark, null))),
72
66
  label && (React.createElement(index_js_1.Box, { as: 'span', className: 'iui-toggle-switch-label' }, label))));
73
67
  });