@itwin/itwinui-react 3.14.2 → 3.15.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 (101) hide show
  1. package/CHANGELOG.md +68 -2
  2. package/DEV-cjs/core/Carousel/CarouselDotsList.js +11 -4
  3. package/DEV-cjs/core/Carousel/CarouselSlider.js +3 -3
  4. package/DEV-cjs/core/ColorPicker/ColorBuilder.js +72 -24
  5. package/DEV-cjs/core/ColorPicker/ColorInputPanel.js +35 -7
  6. package/DEV-cjs/core/ColorPicker/ColorPalette.js +21 -3
  7. package/DEV-cjs/core/ComboBox/ComboBox.js +2 -1
  8. package/DEV-cjs/core/ComboBox/ComboBoxMenu.js +2 -2
  9. package/DEV-cjs/core/DropdownMenu/DropdownMenu.js +20 -19
  10. package/DEV-cjs/core/Overlay/Overlay.js +0 -18
  11. package/DEV-cjs/core/Popover/Popover.js +17 -10
  12. package/DEV-cjs/core/Select/Select.js +4 -2
  13. package/DEV-cjs/core/Table/Table.js +50 -25
  14. package/DEV-cjs/core/Table/TableExpandableContentMemoized.js +47 -0
  15. package/DEV-cjs/core/Table/TableRowMemoized.js +0 -18
  16. package/DEV-cjs/core/Table/hooks/useColumnDragAndDrop.js +12 -14
  17. package/DEV-cjs/core/ThemeProvider/ThemeProvider.js +18 -0
  18. package/DEV-cjs/core/Toast/Toast.js +6 -7
  19. package/DEV-cjs/styles.js +1 -1
  20. package/DEV-cjs/utils/components/ShadowRoot.js +22 -8
  21. package/DEV-cjs/utils/hooks/useWarningLogger.js +5 -3
  22. package/DEV-esm/core/Carousel/CarouselDotsList.js +10 -5
  23. package/DEV-esm/core/Carousel/CarouselSlider.js +2 -3
  24. package/DEV-esm/core/ColorPicker/ColorBuilder.js +56 -22
  25. package/DEV-esm/core/ColorPicker/ColorInputPanel.js +37 -8
  26. package/DEV-esm/core/ColorPicker/ColorPalette.js +18 -3
  27. package/DEV-esm/core/ComboBox/ComboBox.js +2 -1
  28. package/DEV-esm/core/ComboBox/ComboBoxMenu.js +2 -2
  29. package/DEV-esm/core/DropdownMenu/DropdownMenu.js +20 -19
  30. package/DEV-esm/core/Overlay/Overlay.js +1 -19
  31. package/DEV-esm/core/Popover/Popover.js +18 -10
  32. package/DEV-esm/core/Select/Select.js +4 -2
  33. package/DEV-esm/core/Table/Table.js +50 -25
  34. package/DEV-esm/core/Table/TableExpandableContentMemoized.js +33 -0
  35. package/DEV-esm/core/Table/TableRowMemoized.js +1 -21
  36. package/DEV-esm/core/Table/hooks/useColumnDragAndDrop.js +12 -6
  37. package/DEV-esm/core/ThemeProvider/ThemeProvider.js +18 -0
  38. package/DEV-esm/core/Toast/Toast.js +5 -5
  39. package/DEV-esm/styles.js +1 -1
  40. package/DEV-esm/utils/components/ShadowRoot.js +22 -8
  41. package/DEV-esm/utils/hooks/useWarningLogger.js +4 -3
  42. package/cjs/core/Buttons/SplitButton.d.ts +5 -0
  43. package/cjs/core/Carousel/CarouselDotsList.js +11 -4
  44. package/cjs/core/Carousel/CarouselSlider.js +3 -3
  45. package/cjs/core/ColorPicker/ColorBuilder.d.ts +22 -1
  46. package/cjs/core/ColorPicker/ColorBuilder.js +72 -24
  47. package/cjs/core/ColorPicker/ColorInputPanel.d.ts +18 -0
  48. package/cjs/core/ColorPicker/ColorInputPanel.js +35 -7
  49. package/cjs/core/ColorPicker/ColorPalette.d.ts +8 -0
  50. package/cjs/core/ColorPicker/ColorPalette.js +21 -3
  51. package/cjs/core/ComboBox/ComboBox.d.ts +3 -2
  52. package/cjs/core/ComboBox/ComboBox.js +2 -1
  53. package/cjs/core/ComboBox/ComboBoxMenu.js +2 -2
  54. package/cjs/core/DropdownMenu/DropdownMenu.d.ts +11 -0
  55. package/cjs/core/DropdownMenu/DropdownMenu.js +20 -19
  56. package/cjs/core/Overlay/Overlay.js +0 -18
  57. package/cjs/core/Popover/Popover.d.ts +4 -1
  58. package/cjs/core/Popover/Popover.js +17 -10
  59. package/cjs/core/Select/Select.d.ts +14 -2
  60. package/cjs/core/Select/Select.js +4 -2
  61. package/cjs/core/Table/Table.js +50 -25
  62. package/cjs/core/Table/TableExpandableContentMemoized.d.ts +10 -0
  63. package/cjs/core/Table/TableExpandableContentMemoized.js +47 -0
  64. package/cjs/core/Table/TableRowMemoized.js +0 -18
  65. package/cjs/core/Table/hooks/useColumnDragAndDrop.js +12 -14
  66. package/cjs/core/ThemeProvider/ThemeProvider.js +18 -0
  67. package/cjs/core/Toast/Toast.js +6 -7
  68. package/cjs/styles.js +1 -1
  69. package/cjs/utils/components/ShadowRoot.js +22 -8
  70. package/cjs/utils/hooks/useWarningLogger.js +1 -0
  71. package/esm/core/Buttons/SplitButton.d.ts +5 -0
  72. package/esm/core/Carousel/CarouselDotsList.js +10 -5
  73. package/esm/core/Carousel/CarouselSlider.js +2 -3
  74. package/esm/core/ColorPicker/ColorBuilder.d.ts +22 -1
  75. package/esm/core/ColorPicker/ColorBuilder.js +56 -22
  76. package/esm/core/ColorPicker/ColorInputPanel.d.ts +18 -0
  77. package/esm/core/ColorPicker/ColorInputPanel.js +37 -8
  78. package/esm/core/ColorPicker/ColorPalette.d.ts +8 -0
  79. package/esm/core/ColorPicker/ColorPalette.js +18 -3
  80. package/esm/core/ComboBox/ComboBox.d.ts +3 -2
  81. package/esm/core/ComboBox/ComboBox.js +2 -1
  82. package/esm/core/ComboBox/ComboBoxMenu.js +2 -2
  83. package/esm/core/DropdownMenu/DropdownMenu.d.ts +11 -0
  84. package/esm/core/DropdownMenu/DropdownMenu.js +20 -19
  85. package/esm/core/Overlay/Overlay.js +1 -19
  86. package/esm/core/Popover/Popover.d.ts +4 -1
  87. package/esm/core/Popover/Popover.js +18 -10
  88. package/esm/core/Select/Select.d.ts +14 -2
  89. package/esm/core/Select/Select.js +4 -2
  90. package/esm/core/Table/Table.js +50 -25
  91. package/esm/core/Table/TableExpandableContentMemoized.d.ts +10 -0
  92. package/esm/core/Table/TableExpandableContentMemoized.js +33 -0
  93. package/esm/core/Table/TableRowMemoized.js +1 -21
  94. package/esm/core/Table/hooks/useColumnDragAndDrop.js +12 -6
  95. package/esm/core/ThemeProvider/ThemeProvider.js +18 -0
  96. package/esm/core/Toast/Toast.js +5 -5
  97. package/esm/styles.js +1 -1
  98. package/esm/utils/components/ShadowRoot.js +22 -8
  99. package/esm/utils/hooks/useWarningLogger.js +1 -0
  100. package/package.json +8 -6
  101. package/styles.css +8 -8
@@ -50,6 +50,19 @@ function useShadowRoot(templateRef, { css = '' }) {
50
50
  let styleSheet = React.useRef();
51
51
  let latestCss = useLatestRef(css);
52
52
  let latestShadowRoot = useLatestRef(shadowRoot);
53
+ let createStyleSheet = React.useCallback(
54
+ (shadow) => {
55
+ if (shadow && supportsAdoptedStylesheets) {
56
+ let currentWindow = shadow.ownerDocument.defaultView || globalThis;
57
+ if (styleSheet.current instanceof currentWindow.CSSStyleSheet) return;
58
+ styleSheet.current = new currentWindow.CSSStyleSheet();
59
+ shadow.adoptedStyleSheets.push(styleSheet.current);
60
+ if (latestCss.current)
61
+ styleSheet.current.replaceSync(latestCss.current);
62
+ }
63
+ },
64
+ [latestCss],
65
+ );
53
66
  useLayoutEffect(() => {
54
67
  let parent = templateRef.current?.parentElement;
55
68
  if (!parent) return;
@@ -61,22 +74,23 @@ function useShadowRoot(templateRef, { css = '' }) {
61
74
  parent.attachShadow({
62
75
  mode: 'open',
63
76
  });
64
- if (supportsAdoptedStylesheets) {
65
- let currentWindow = shadow.ownerDocument.defaultView || globalThis;
66
- styleSheet.current = new currentWindow.CSSStyleSheet();
67
- shadow.adoptedStyleSheets = [styleSheet.current];
68
- if (latestCss.current)
69
- styleSheet.current.replaceSync(latestCss.current);
70
- }
77
+ createStyleSheet(shadow);
71
78
  ReactDOM.flushSync(() => setShadowRoot(shadow));
72
79
  };
73
80
  queueMicrotask(() => {
74
81
  setupOrReuseShadowRoot();
75
82
  });
76
83
  return () => void setShadowRoot(null);
77
- }, [templateRef, latestCss, latestShadowRoot]);
84
+ }, [templateRef, createStyleSheet, latestShadowRoot]);
78
85
  useLayoutEffect(() => {
79
86
  if (css && supportsAdoptedStylesheets) styleSheet.current?.replaceSync(css);
80
87
  }, [css]);
88
+ React.useEffect(() => {
89
+ let listener = () => createStyleSheet(latestShadowRoot.current);
90
+ window.addEventListener('appui:reparent', listener);
91
+ return () => {
92
+ window.removeEventListener('appui:reparent', listener);
93
+ };
94
+ }, [createStyleSheet, latestShadowRoot]);
81
95
  return shadowRoot;
82
96
  }
@@ -1,18 +1,19 @@
1
1
  import * as React from 'react';
2
2
  import { isUnitTest } from '../functions/dev.js';
3
+ import { getWindow } from '../functions/dom.js';
3
4
  let _React = React;
4
5
  let ReactInternals = _React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
5
6
  export const useWarningLogger = isUnitTest
6
7
  ? () => () => {}
7
8
  : function () {
8
9
  let loggedRef = React.useRef(false);
9
- let timeoutRef = React.useRef(null);
10
+ let timeoutRef = React.useRef(void 0);
10
11
  let stack = ReactInternals?.ReactDebugCurrentFrame?.getCurrentStack?.();
11
12
  let componentName = stack?.trim().split('\n')[1]?.trim();
12
13
  let prefix = componentName ? `Warning (${componentName}):` : 'Warning:';
13
14
  let logWarning = React.useCallback(
14
15
  (message) => {
15
- timeoutRef.current = window.setTimeout(() => {
16
+ timeoutRef.current = getWindow()?.setTimeout(() => {
16
17
  if (!loggedRef.current) {
17
18
  console.error(prefix, message);
18
19
  loggedRef.current = true;
@@ -23,7 +24,7 @@ export const useWarningLogger = isUnitTest
23
24
  );
24
25
  React.useEffect(
25
26
  () => () => {
26
- if (timeoutRef.current) window.clearTimeout(timeoutRef.current);
27
+ if (timeoutRef.current) getWindow()?.clearTimeout(timeoutRef.current);
27
28
  },
28
29
  [],
29
30
  );
@@ -3,6 +3,7 @@ import type { ButtonProps } from './Button.js';
3
3
  import { IconButton } from './IconButton.js';
4
4
  import type { PolymorphicForwardRefComponent, PortalProps } from '../../utils/index.js';
5
5
  import type { Placement } from '@floating-ui/react';
6
+ import type { usePopover } from '../Popover/Popover.js';
6
7
  export type SplitButtonProps = ButtonProps & {
7
8
  /**
8
9
  * Items in the dropdown menu.
@@ -27,6 +28,10 @@ export type SplitButtonProps = ButtonProps & {
27
28
  * Passes props to SplitButton menu button.
28
29
  */
29
30
  menuButtonProps?: Omit<React.ComponentProps<typeof IconButton>, 'label' | 'size'>;
31
+ /**
32
+ * Props to customize menu behavior.
33
+ */
34
+ dropdownMenuProps?: Pick<Parameters<typeof usePopover>[0], 'middleware'>;
30
35
  } & Pick<PortalProps, 'portal'>;
31
36
  /**
32
37
  * Split button component with a DropdownMenu.
@@ -104,18 +104,25 @@ const CarouselDotsList = _react.forwardRef((props, ref) => {
104
104
  handleSlideChange,
105
105
  ],
106
106
  );
107
+ let motionOk = (0, _index.useMediaQuery)(
108
+ '(prefers-reduced-motion: no-preference)',
109
+ );
107
110
  _react.useEffect(() => {
108
111
  let firstDot = listRef.current?.children[firstVisibleDotIndex];
109
112
  if (!listRef.current || !firstDot) return;
110
- let motionOk = (0, _index.getWindow)()?.matchMedia(
111
- '(prefers-reduced-motion: no-preference)',
112
- )?.matches;
113
113
  listRef.current.scrollTo({
114
114
  left: firstDot.offsetLeft - listRef.current.offsetLeft,
115
115
  behavior: motionOk && !justMounted.current ? 'smooth' : 'auto',
116
116
  });
117
117
  if (justMounted.current) justMounted.current = false;
118
- }, [currentIndex, firstVisibleDotIndex, slideCount, visibleCount, width]);
118
+ }, [
119
+ currentIndex,
120
+ firstVisibleDotIndex,
121
+ motionOk,
122
+ slideCount,
123
+ visibleCount,
124
+ width,
125
+ ]);
119
126
  let handleKeyDown = (event) => {
120
127
  if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey)
121
128
  return;
@@ -37,13 +37,13 @@ const CarouselSlider = _react.forwardRef((props, ref) => {
37
37
  }, [items.length, setSlideCount]);
38
38
  let sliderRef = _react.useRef(null);
39
39
  let refs = (0, _index.useMergedRefs)(sliderRef, ref);
40
+ let motionOk = (0, _index.useMediaQuery)(
41
+ '(prefers-reduced-motion: no-preference)',
42
+ );
40
43
  scrollToSlide.current = (slideIndex, { instant } = {}) => {
41
44
  isManuallyUpdating.current = true;
42
45
  let slideToShow = sliderRef.current?.children.item(slideIndex);
43
46
  if (!sliderRef.current || !slideToShow) return;
44
- let motionOk = (0, _index.getWindow)()?.matchMedia(
45
- '(prefers-reduced-motion: no-preference)',
46
- )?.matches;
47
47
  sliderRef.current.scrollTo({
48
48
  left: slideToShow.offsetLeft - sliderRef.current.offsetLeft,
49
49
  behavior: instant || !motionOk ? 'instant' : 'smooth',
@@ -1,7 +1,28 @@
1
+ import * as React from 'react';
1
2
  import type { PolymorphicForwardRefComponent } from '../../utils/index.js';
3
+ import { Slider } from '../Slider/Slider.js';
4
+ type ColorBuilderProps = {
5
+ /**
6
+ * Passes props to the color field.
7
+ */
8
+ colorFieldProps?: React.ComponentProps<'div'>;
9
+ /**
10
+ * Passes props to the color dot.
11
+ */
12
+ colorDotProps?: React.ComponentProps<'div'>;
13
+ /**
14
+ * Passes props to the color opacity slider.
15
+ */
16
+ opacitySliderProps?: React.ComponentProps<typeof Slider>;
17
+ /**
18
+ * Passes props to the color hue slider.
19
+ */
20
+ hueSliderProps?: React.ComponentProps<typeof Slider>;
21
+ };
2
22
  /**
3
23
  * `ColorBuilder` consists of two parts:
4
24
  * a color canvas to adjust saturation and lightness values,
5
25
  * and a set of sliders to adjust hue and alpha values.
6
26
  */
7
- export declare const ColorBuilder: PolymorphicForwardRefComponent<"div">;
27
+ export declare const ColorBuilder: PolymorphicForwardRefComponent<"div", ColorBuilderProps>;
28
+ export {};
@@ -24,7 +24,14 @@ const getHorizontalPercentageOfRectangle = (rect, pointer) => {
24
24
  return ((position - rect.left) / rect.width) * 100;
25
25
  };
26
26
  const ColorBuilder = _react.forwardRef((props, ref) => {
27
- let { className, ...rest } = props;
27
+ let {
28
+ className,
29
+ colorFieldProps,
30
+ colorDotProps,
31
+ opacitySliderProps,
32
+ hueSliderProps,
33
+ ...rest
34
+ } = props;
28
35
  let builderRef = _react.useRef();
29
36
  let refs = (0, _index.useMergedRefs)(builderRef, ref);
30
37
  let {
@@ -215,54 +222,86 @@ const ColorBuilder = _react.forwardRef((props, ref) => {
215
222
  _react.createElement(
216
223
  _index.Box,
217
224
  {
218
- className: 'iui-color-field',
225
+ as: 'div',
226
+ ...colorFieldProps,
227
+ className: (0, _classnames.default)(
228
+ 'iui-color-field',
229
+ colorFieldProps?.className,
230
+ ),
219
231
  style: {
220
232
  '--iui-color-field-hue': hueColorString,
221
233
  '--iui-color-picker-selected-color': dotColorString,
234
+ ...colorFieldProps?.style,
222
235
  },
223
- ref: squareRef,
224
- onPointerDown: (event) => {
225
- event.preventDefault();
226
- updateSquareValue(event, 'onClick');
227
- setColorDotActive(true);
228
- colorDotRef.current?.focus();
229
- },
236
+ ref: (0, _index.useMergedRefs)(squareRef, colorFieldProps?.ref),
237
+ onPointerDown: (0, _index.mergeEventHandlers)(
238
+ colorFieldProps?.onPointerDown,
239
+ (event) => {
240
+ event.preventDefault();
241
+ updateSquareValue(event, 'onClick');
242
+ setColorDotActive(true);
243
+ colorDotRef.current?.focus();
244
+ },
245
+ ),
230
246
  },
231
247
  _react.createElement(_index.Box, {
232
- className: 'iui-color-dot',
248
+ as: 'div',
249
+ ...colorDotProps,
250
+ className: (0, _classnames.default)(
251
+ 'iui-color-dot',
252
+ colorDotProps?.className,
253
+ ),
233
254
  style: {
234
255
  '--iui-color-dot-inset-block': `${squareTop.toString()}% auto`,
235
256
  '--iui-color-dot-inset-inline': `${squareLeft.toString()}% auto`,
257
+ ...colorDotProps?.style,
236
258
  },
237
- onPointerDown: () => {
238
- setColorDotActive(true);
239
- colorDotRef.current?.focus();
240
- },
241
- onKeyDown: handleColorDotKeyDown,
242
- onKeyUp: handleColorDotKeyUp,
259
+ onPointerDown: (0, _index.mergeEventHandlers)(
260
+ colorDotProps?.onPointerDown,
261
+ () => {
262
+ setColorDotActive(true);
263
+ colorDotRef.current?.focus();
264
+ },
265
+ ),
266
+ onKeyDown: (0, _index.mergeEventHandlers)(
267
+ colorDotProps?.onKeyDown,
268
+ handleColorDotKeyDown,
269
+ ),
270
+ onKeyUp: (0, _index.mergeEventHandlers)(
271
+ colorDotProps?.onKeyUp,
272
+ handleColorDotKeyUp,
273
+ ),
243
274
  tabIndex: 0,
244
- ref: colorDotRef,
275
+ ref: (0, _index.useMergedRefs)(colorDotRef, colorDotProps?.ref),
245
276
  }),
246
277
  ),
247
278
  _react.createElement(_Slider.Slider, {
248
279
  minLabel: '',
249
280
  maxLabel: '',
250
281
  values: [sliderValue],
251
- className: 'iui-hue-slider',
252
282
  trackDisplayMode: 'none',
283
+ min: 0,
284
+ max: 359,
285
+ ...hueSliderProps,
286
+ className: (0, _classnames.default)(
287
+ 'iui-hue-slider',
288
+ hueSliderProps?.className,
289
+ ),
253
290
  tooltipProps: () => ({
254
291
  visible: false,
292
+ ...hueSliderProps?.tooltipProps,
255
293
  }),
256
294
  onChange: (values) => {
295
+ hueSliderProps?.onChange?.(values);
257
296
  updateHueSlider(values[0], true);
258
297
  },
259
298
  onUpdate: (values) => {
299
+ hueSliderProps?.onUpdate?.(values);
260
300
  updateHueSlider(values[0], false);
261
301
  },
262
- min: 0,
263
- max: 359,
264
302
  thumbProps: () => ({
265
303
  'aria-label': 'Hue',
304
+ ...hueSliderProps?.thumbProps,
266
305
  }),
267
306
  }),
268
307
  showAlpha &&
@@ -270,25 +309,34 @@ const ColorBuilder = _react.forwardRef((props, ref) => {
270
309
  minLabel: '',
271
310
  maxLabel: '',
272
311
  values: [alphaValue],
273
- className: 'iui-opacity-slider',
274
312
  trackDisplayMode: 'none',
313
+ min: 0,
314
+ max: 1,
315
+ step: 0.01,
316
+ ...opacitySliderProps,
317
+ className: (0, _classnames.default)(
318
+ 'iui-opacity-slider',
319
+ opacitySliderProps?.className,
320
+ ),
275
321
  tooltipProps: () => ({
276
322
  visible: false,
323
+ ...opacitySliderProps?.tooltipProps,
277
324
  }),
278
325
  onChange: (values) => {
326
+ opacitySliderProps?.onChange?.(values);
279
327
  updateOpacitySlider(values[0], true);
280
328
  },
281
329
  onUpdate: (values) => {
330
+ opacitySliderProps?.onUpdate?.(values);
282
331
  updateOpacitySlider(values[0], false);
283
332
  },
284
- min: 0,
285
- max: 1,
286
- step: 0.01,
287
333
  style: {
288
334
  '--iui-color-picker-selected-color': hueColorString,
335
+ ...opacitySliderProps?.style,
289
336
  },
290
337
  thumbProps: () => ({
291
338
  'aria-label': 'Opacity',
339
+ ...opacitySliderProps?.thumbProps,
292
340
  }),
293
341
  }),
294
342
  );
@@ -1,3 +1,5 @@
1
+ import * as React from 'react';
2
+ import { IconButton } from '../Buttons/IconButton.js';
1
3
  import type { PolymorphicForwardRefComponent } from '../../utils/index.js';
2
4
  type ColorInputPanelProps = {
3
5
  /**
@@ -12,6 +14,22 @@ type ColorInputPanelProps = {
12
14
  * @default ['hsl', 'rgb', 'hex']
13
15
  */
14
16
  allowedColorFormats?: ('hsl' | 'rgb' | 'hex')[];
17
+ /**
18
+ * Passes props to the color picker section label.
19
+ */
20
+ panelLabelProps?: React.ComponentProps<'div'>;
21
+ /**
22
+ * Passes props to the color input container.
23
+ */
24
+ colorInputContainerProps?: React.ComponentProps<'div'>;
25
+ /**
26
+ * Passes props to the color input fields group.
27
+ */
28
+ inputFieldsGroupProps?: React.ComponentProps<'div'>;
29
+ /**
30
+ * Passes props to the swap color format button.
31
+ */
32
+ swapColorFormatButtonProps?: React.ComponentProps<typeof IconButton>;
15
33
  };
16
34
  /**
17
35
  * `ColorInputPanel` shows input fields to enter precise color values in the specified format.
@@ -21,6 +21,10 @@ const ColorInputPanel = _react.forwardRef((props, ref) => {
21
21
  defaultColorFormat,
22
22
  allowedColorFormats = ['hsl', 'rgb', 'hex'],
23
23
  className,
24
+ colorInputContainerProps,
25
+ panelLabelProps,
26
+ inputFieldsGroupProps,
27
+ swapColorFormatButtonProps,
24
28
  ...rest
25
29
  } = props;
26
30
  let inputsContainerRef = _react.useRef(null);
@@ -364,10 +368,12 @@ const ColorInputPanel = _react.forwardRef((props, ref) => {
364
368
  Number(input[3]) < 0 || Number(input[3]) > 1 ? 'negative' : void 0,
365
369
  }),
366
370
  );
367
- let labelId = (0, _index.useId)();
371
+ let fallbackLabelId = (0, _index.useId)();
372
+ let labelId = panelLabelProps?.id ?? fallbackLabelId;
368
373
  return _react.createElement(
369
374
  _index.Box,
370
375
  {
376
+ as: 'div',
371
377
  className: (0, _classnames.default)('iui-color-input-wrapper', className),
372
378
  ref: ref,
373
379
  ...rest,
@@ -375,7 +381,12 @@ const ColorInputPanel = _react.forwardRef((props, ref) => {
375
381
  _react.createElement(
376
382
  _index.Box,
377
383
  {
378
- className: 'iui-color-picker-section-label',
384
+ as: 'div',
385
+ ...panelLabelProps,
386
+ className: (0, _classnames.default)(
387
+ 'iui-color-picker-section-label',
388
+ panelLabelProps?.className,
389
+ ),
379
390
  id: labelId,
380
391
  },
381
392
  showAlpha && 'hex' !== currentFormat
@@ -385,26 +396,43 @@ const ColorInputPanel = _react.forwardRef((props, ref) => {
385
396
  _react.createElement(
386
397
  _index.Box,
387
398
  {
388
- className: 'iui-color-input',
399
+ as: 'div',
400
+ ...colorInputContainerProps,
401
+ className: (0, _classnames.default)(
402
+ 'iui-color-input',
403
+ colorInputContainerProps?.className,
404
+ ),
389
405
  },
390
406
  allowedColorFormats.length > 1 &&
391
407
  _react.createElement(
392
408
  _IconButton.IconButton,
393
409
  {
394
- styleType: 'borderless',
395
- onClick: swapColorFormat,
396
410
  size: 'small',
411
+ styleType: 'borderless',
397
412
  label: 'Switch format',
413
+ ...swapColorFormatButtonProps,
414
+ onClick: (0, _index.mergeEventHandlers)(
415
+ swapColorFormatButtonProps?.onClick,
416
+ swapColorFormat,
417
+ ),
398
418
  },
399
419
  _react.createElement(_index.SvgSwap, null),
400
420
  ),
401
421
  _react.createElement(
402
422
  _index.Box,
403
423
  {
404
- ref: inputsContainerRef,
405
- className: 'iui-color-input-fields',
424
+ as: 'div',
406
425
  role: 'hex' !== currentFormat ? 'group' : void 0,
407
426
  'aria-labelledby': 'hex' !== currentFormat ? labelId : void 0,
427
+ ...inputFieldsGroupProps,
428
+ ref: (0, _index.useMergedRefs)(
429
+ inputsContainerRef,
430
+ inputFieldsGroupProps?.ref,
431
+ ),
432
+ className: (0, _classnames.default)(
433
+ 'iui-color-input-fields',
434
+ inputFieldsGroupProps?.className,
435
+ ),
408
436
  },
409
437
  'hex' === currentFormat && hexInputField,
410
438
  'rgb' === currentFormat && rgbInputs,
@@ -6,6 +6,10 @@ export type ColorPaletteProps = {
6
6
  * Label shown above the palette.
7
7
  */
8
8
  label?: React.ReactNode;
9
+ /**
10
+ * Passes props to the color picker section label.
11
+ */
12
+ labelProps?: React.ComponentProps<'div'>;
9
13
  /**
10
14
  * List of colors shown as swatches in the palette.
11
15
  */
@@ -14,6 +18,10 @@ export type ColorPaletteProps = {
14
18
  * Pass any custom swatches as children.
15
19
  */
16
20
  children?: React.ReactNode;
21
+ /**
22
+ * Passes props to the color palette container.
23
+ */
24
+ paletteContainerProps?: React.ComponentProps<'div'>;
17
25
  };
18
26
  /**
19
27
  * `ColorPalette` is used to show a group of `ColorSwatch` components.
@@ -17,7 +17,15 @@ const _ColorPicker = require('./ColorPicker.js');
17
17
  const _ColorSwatch = require('./ColorSwatch.js');
18
18
  const _ColorPickerContext = require('./ColorPickerContext.js');
19
19
  const ColorPalette = _react.forwardRef((props, ref) => {
20
- let { colors, label, className, children, ...rest } = props;
20
+ let {
21
+ colors,
22
+ label,
23
+ labelProps,
24
+ className,
25
+ children,
26
+ paletteContainerProps,
27
+ ...rest
28
+ } = props;
21
29
  let { activeColor, setActiveColor, onChangeComplete } = (0,
22
30
  _ColorPickerContext.useColorPickerContext)();
23
31
  return _react.createElement(
@@ -34,14 +42,24 @@ const ColorPalette = _react.forwardRef((props, ref) => {
34
42
  _react.createElement(
35
43
  _index.Box,
36
44
  {
37
- className: 'iui-color-picker-section-label',
45
+ as: 'div',
46
+ ...labelProps,
47
+ className: (0, _classnames.default)(
48
+ 'iui-color-picker-section-label',
49
+ labelProps?.className,
50
+ ),
38
51
  },
39
52
  label,
40
53
  ),
41
54
  _react.createElement(
42
55
  _index.Box,
43
56
  {
44
- className: 'iui-color-palette',
57
+ as: 'div',
58
+ ...paletteContainerProps,
59
+ className: (0, _classnames.default)(
60
+ 'iui-color-palette',
61
+ paletteContainerProps?.className,
62
+ ),
45
63
  },
46
64
  children,
47
65
  colors &&
@@ -1,7 +1,8 @@
1
1
  import * as React from 'react';
2
2
  import type { SelectOption } from '../Select/Select.js';
3
3
  import type { Input } from '../Input/Input.js';
4
- import type { InputContainerProps, CommonProps } from '../../utils/index.js';
4
+ import { usePopover } from '../Popover/Popover.js';
5
+ import type { InputContainerProps, CommonProps, PortalProps } from '../../utils/index.js';
5
6
  import { ComboBoxEndIcon } from './ComboBoxEndIcon.js';
6
7
  type ActionType = 'added' | 'removed';
7
8
  type MultipleOnChangeProps<T> = {
@@ -59,7 +60,7 @@ export type ComboBoxProps<T> = {
59
60
  /**
60
61
  * Props to customize dropdown menu behavior.
61
62
  */
62
- dropdownMenuProps?: React.ComponentProps<'div'>;
63
+ dropdownMenuProps?: React.ComponentProps<'div'> & Pick<Parameters<typeof usePopover>['0'], 'middleware'> & Pick<PortalProps, 'portal'>;
63
64
  /**
64
65
  * End icon props.
65
66
  */
@@ -42,7 +42,7 @@ const ComboBox = _react.forwardRef((props, forwardedRef) => {
42
42
  filterFunction = defaultFilterFunction,
43
43
  inputProps,
44
44
  endIconProps,
45
- dropdownMenuProps,
45
+ dropdownMenuProps: { middleware, ...dropdownMenuProps } = {},
46
46
  emptyStateMessage = 'No options found',
47
47
  itemRenderer,
48
48
  enableVirtualization = false,
@@ -322,6 +322,7 @@ const ComboBox = _react.forwardRef((props, forwardedRef) => {
322
322
  size: {
323
323
  maxHeight: 'var(--iui-menu-max-height)',
324
324
  },
325
+ ...middleware,
325
326
  },
326
327
  closeOnOutsideClick: true,
327
328
  interactions: {
@@ -94,7 +94,7 @@ const VirtualizedComboBoxMenu = (props) => {
94
94
  );
95
95
  };
96
96
  const ComboBoxMenu = _react.forwardRef((props, forwardedRef) => {
97
- let { className, children, style, ...rest } = props;
97
+ let { className, children, style, portal = true, ...rest } = props;
98
98
  let { id, enableVirtualization, popover } = (0, _index.useSafeContext)(
99
99
  _helpers.ComboBoxStateContext,
100
100
  );
@@ -109,7 +109,7 @@ const ComboBoxMenu = _react.forwardRef((props, forwardedRef) => {
109
109
  _react.createElement(
110
110
  _index.Portal,
111
111
  {
112
- portal: true,
112
+ portal: portal,
113
113
  },
114
114
  _react.createElement(
115
115
  _List.List,
@@ -16,6 +16,17 @@ export type DropdownMenuProps = {
16
16
  * Child element to wrap dropdown with.
17
17
  */
18
18
  children: React.ReactNode;
19
+ /**
20
+ * Middleware options.
21
+ *
22
+ * By default, `hide` is enabled. If the menu gets hidden even when it shouldn't (e.g. some custom styles interfering
23
+ * with the trigger's hide detection) consider disabling the `hide` middleware.
24
+ *
25
+ * @see https://floating-ui.com/docs/middleware
26
+ */
27
+ middleware?: {
28
+ hide?: boolean;
29
+ };
19
30
  } & Pick<Parameters<typeof usePopover>[0], 'visible' | 'onVisibleChange' | 'placement' | 'matchWidth'> & Pick<PortalProps, 'portal'>;
20
31
  /**
21
32
  * Dropdown menu component.
@@ -33,6 +33,7 @@ const DropdownMenuContent = _react.forwardRef((props, forwardedRef) => {
33
33
  matchWidth = false,
34
34
  onVisibleChange,
35
35
  portal = true,
36
+ middleware,
36
37
  ...rest
37
38
  } = props;
38
39
  let [visible, setVisible] = (0, _index.useControlledState)(
@@ -46,28 +47,28 @@ const DropdownMenuContent = _react.forwardRef((props, forwardedRef) => {
46
47
  return menuItems;
47
48
  }, [menuItems, setVisible]);
48
49
  return _react.createElement(
49
- _react.Fragment,
50
- null,
51
- _react.createElement(
52
- _Menu.Menu,
53
- {
54
- trigger: children,
55
- onKeyDown: (0, _index.mergeEventHandlers)(props.onKeyDown, (e) => {
56
- if (e.defaultPrevented) return;
57
- if ('Tab' === e.key) setVisible(false);
58
- }),
59
- role: role,
60
- ref: forwardedRef,
61
- portal: portal,
62
- popoverProps: {
50
+ _Menu.Menu,
51
+ {
52
+ trigger: children,
53
+ onKeyDown: (0, _index.mergeEventHandlers)(props.onKeyDown, (e) => {
54
+ if (e.defaultPrevented) return;
55
+ if ('Tab' === e.key) setVisible(false);
56
+ }),
57
+ role: role,
58
+ ref: forwardedRef,
59
+ portal: portal,
60
+ popoverProps: _react.useMemo(
61
+ () => ({
63
62
  placement,
64
63
  matchWidth,
65
64
  visible,
66
65
  onVisibleChange: setVisible,
67
- },
68
- ...rest,
69
- },
70
- menuContent,
71
- ),
66
+ middleware,
67
+ }),
68
+ [matchWidth, middleware, placement, setVisible, visible],
69
+ ),
70
+ ...rest,
71
+ },
72
+ menuContent,
72
73
  );
73
74
  });