@itwin/itwinui-react 3.0.0-dev.7 → 3.0.0-dev.8

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 (189) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/cjs/core/Alert/Alert.d.ts +20 -9
  3. package/cjs/core/Alert/Alert.js +48 -10
  4. package/cjs/core/ButtonGroup/ButtonGroup.js +41 -36
  5. package/cjs/core/Buttons/IconButton/IconButton.js +27 -44
  6. package/cjs/core/Buttons/SplitButton/SplitButton.js +3 -0
  7. package/cjs/core/ColorPicker/ColorInputPanel.js +172 -231
  8. package/cjs/core/ComboBox/ComboBox.js +3 -3
  9. package/cjs/core/ComboBox/ComboBoxDropdown.js +4 -9
  10. package/cjs/core/ComboBox/ComboBoxEndIcon.js +3 -22
  11. package/cjs/core/ComboBox/ComboBoxMenu.d.ts +2 -2
  12. package/cjs/core/ComboBox/ComboBoxMenuItem.d.ts +1 -1
  13. package/cjs/core/ComboBox/ComboBoxMenuItem.js +8 -6
  14. package/cjs/core/ComboBox/helpers.d.ts +1 -2
  15. package/cjs/core/DatePicker/DatePicker.d.ts +30 -8
  16. package/cjs/core/DatePicker/DatePicker.js +40 -5
  17. package/cjs/core/Dialog/Dialog.js +10 -16
  18. package/cjs/core/Dialog/DialogContext.d.ts +3 -4
  19. package/cjs/core/ExpandableBlock/ExpandableBlock.d.ts +20 -14
  20. package/cjs/core/ExpandableBlock/ExpandableBlock.js +38 -15
  21. package/cjs/core/Header/HeaderSplitButton.js +1 -0
  22. package/cjs/core/Input/Input.d.ts +4 -0
  23. package/cjs/core/Input/Input.js +2 -1
  24. package/cjs/core/InputGrid/InputGrid.d.ts +25 -0
  25. package/cjs/core/InputGrid/InputGrid.js +39 -0
  26. package/cjs/core/InputGrid/index.d.ts +3 -0
  27. package/cjs/core/InputGrid/index.js +15 -0
  28. package/cjs/core/InputGroup/InputGroup.d.ts +13 -0
  29. package/cjs/core/InputGroup/InputGroup.js +35 -9
  30. package/cjs/core/InputWithDecorations/InputWithDecorations.d.ts +39 -0
  31. package/cjs/core/InputWithDecorations/InputWithDecorations.js +81 -0
  32. package/cjs/core/InputWithDecorations/index.d.ts +3 -0
  33. package/cjs/core/InputWithDecorations/index.js +15 -0
  34. package/cjs/core/Label/Label.d.ts +5 -0
  35. package/cjs/core/Label/Label.js +2 -0
  36. package/cjs/core/LabeledInput/LabeledInput.d.ts +22 -16
  37. package/cjs/core/LabeledInput/LabeledInput.js +52 -29
  38. package/cjs/core/LabeledSelect/LabeledSelect.d.ts +17 -7
  39. package/cjs/core/LabeledSelect/LabeledSelect.js +36 -17
  40. package/cjs/core/LabeledTextarea/LabeledTextarea.d.ts +15 -5
  41. package/cjs/core/LabeledTextarea/LabeledTextarea.js +12 -45
  42. package/cjs/core/Menu/Menu.d.ts +1 -1
  43. package/cjs/core/Menu/Menu.js +1 -1
  44. package/cjs/core/Menu/MenuDivider.d.ts +2 -1
  45. package/cjs/core/Menu/MenuDivider.js +1 -1
  46. package/cjs/core/Menu/MenuItem.d.ts +1 -1
  47. package/cjs/core/Menu/MenuItem.js +1 -0
  48. package/cjs/core/Menu/MenuItemSkeleton.d.ts +1 -1
  49. package/cjs/core/Menu/MenuItemSkeleton.js +0 -1
  50. package/cjs/core/SearchBox/SearchBox.js +1 -1
  51. package/cjs/core/Select/Select.d.ts +4 -0
  52. package/cjs/core/Select/Select.js +10 -9
  53. package/cjs/core/SideNavigation/SideNavigation.js +2 -0
  54. package/cjs/core/Slider/Thumb.js +1 -0
  55. package/cjs/core/StatusMessage/StatusMessage.d.ts +12 -2
  56. package/cjs/core/StatusMessage/StatusMessage.js +23 -9
  57. package/cjs/core/Table/SubRowExpander.js +2 -0
  58. package/cjs/core/Table/filters/DateRangeFilter/DatePickerInput.d.ts +6 -1
  59. package/cjs/core/Table/filters/DateRangeFilter/DatePickerInput.js +46 -18
  60. package/cjs/core/Table/filters/DateRangeFilter/DateRangeFilter.d.ts +2 -0
  61. package/cjs/core/Table/filters/DateRangeFilter/DateRangeFilter.js +2 -0
  62. package/cjs/core/Textarea/Textarea.d.ts +7 -1
  63. package/cjs/core/Textarea/Textarea.js +6 -11
  64. package/cjs/core/ThemeProvider/ThemeProvider.js +1 -1
  65. package/cjs/core/Tile/Tile.d.ts +139 -15
  66. package/cjs/core/Tile/Tile.js +107 -16
  67. package/cjs/core/Toast/Toast.d.ts +12 -4
  68. package/cjs/core/Toast/Toast.js +20 -4
  69. package/cjs/core/Tooltip/Tooltip.d.ts +35 -28
  70. package/cjs/core/Tooltip/Tooltip.js +116 -117
  71. package/cjs/core/TransferList/TransferList.js +4 -12
  72. package/cjs/core/index.d.ts +2 -0
  73. package/cjs/core/index.js +20 -4
  74. package/cjs/core/utils/components/Icon.d.ts +5 -0
  75. package/cjs/core/utils/components/Icon.js +8 -1
  76. package/cjs/core/utils/components/InputContainer.d.ts +0 -1
  77. package/cjs/core/utils/components/InputContainer.js +14 -34
  78. package/cjs/core/utils/components/InputFlexContainer.d.ts +1 -0
  79. package/cjs/core/utils/components/InputFlexContainer.js +3 -1
  80. package/cjs/core/utils/components/Portal.d.ts +27 -0
  81. package/cjs/core/utils/components/Portal.js +43 -0
  82. package/cjs/core/utils/components/index.d.ts +1 -0
  83. package/cjs/core/utils/components/index.js +1 -0
  84. package/cjs/core/utils/functions/index.d.ts +1 -0
  85. package/cjs/core/utils/functions/index.js +1 -0
  86. package/cjs/core/utils/functions/react.d.ts +8 -0
  87. package/cjs/core/utils/functions/react.js +40 -0
  88. package/cjs/core/utils/hooks/index.d.ts +1 -1
  89. package/cjs/core/utils/hooks/index.js +1 -1
  90. package/cjs/core/utils/hooks/useControlledState.d.ts +13 -0
  91. package/cjs/core/utils/hooks/useControlledState.js +39 -0
  92. package/cjs/styles.js +10 -28
  93. package/esm/core/Alert/Alert.d.ts +20 -9
  94. package/esm/core/Alert/Alert.js +49 -10
  95. package/esm/core/ButtonGroup/ButtonGroup.js +41 -36
  96. package/esm/core/Buttons/IconButton/IconButton.js +25 -40
  97. package/esm/core/Buttons/SplitButton/SplitButton.js +9 -1
  98. package/esm/core/ColorPicker/ColorInputPanel.js +173 -232
  99. package/esm/core/ComboBox/ComboBox.js +3 -3
  100. package/esm/core/ComboBox/ComboBoxDropdown.js +4 -9
  101. package/esm/core/ComboBox/ComboBoxEndIcon.js +4 -25
  102. package/esm/core/ComboBox/ComboBoxMenu.d.ts +2 -2
  103. package/esm/core/ComboBox/ComboBoxMenuItem.d.ts +1 -1
  104. package/esm/core/ComboBox/ComboBoxMenuItem.js +9 -7
  105. package/esm/core/ComboBox/helpers.d.ts +1 -2
  106. package/esm/core/DatePicker/DatePicker.d.ts +30 -8
  107. package/esm/core/DatePicker/DatePicker.js +25 -5
  108. package/esm/core/Dialog/Dialog.js +11 -23
  109. package/esm/core/Dialog/DialogContext.d.ts +3 -4
  110. package/esm/core/ExpandableBlock/ExpandableBlock.d.ts +20 -14
  111. package/esm/core/ExpandableBlock/ExpandableBlock.js +39 -17
  112. package/esm/core/Header/HeaderSplitButton.js +1 -0
  113. package/esm/core/Input/Input.d.ts +4 -0
  114. package/esm/core/Input/Input.js +2 -1
  115. package/esm/core/InputGrid/InputGrid.d.ts +25 -0
  116. package/esm/core/InputGrid/InputGrid.js +35 -0
  117. package/esm/core/InputGrid/index.d.ts +3 -0
  118. package/esm/core/InputGrid/index.js +6 -0
  119. package/esm/core/InputGroup/InputGroup.d.ts +13 -0
  120. package/esm/core/InputGroup/InputGroup.js +34 -10
  121. package/esm/core/InputWithDecorations/InputWithDecorations.d.ts +39 -0
  122. package/esm/core/InputWithDecorations/InputWithDecorations.js +80 -0
  123. package/esm/core/InputWithDecorations/index.d.ts +3 -0
  124. package/esm/core/InputWithDecorations/index.js +6 -0
  125. package/esm/core/Label/Label.d.ts +5 -0
  126. package/esm/core/Label/Label.js +2 -0
  127. package/esm/core/LabeledInput/LabeledInput.d.ts +22 -16
  128. package/esm/core/LabeledInput/LabeledInput.js +53 -29
  129. package/esm/core/LabeledSelect/LabeledSelect.d.ts +17 -7
  130. package/esm/core/LabeledSelect/LabeledSelect.js +37 -18
  131. package/esm/core/LabeledTextarea/LabeledTextarea.d.ts +15 -5
  132. package/esm/core/LabeledTextarea/LabeledTextarea.js +14 -45
  133. package/esm/core/Menu/Menu.d.ts +1 -1
  134. package/esm/core/Menu/Menu.js +1 -1
  135. package/esm/core/Menu/MenuDivider.d.ts +2 -1
  136. package/esm/core/Menu/MenuDivider.js +1 -1
  137. package/esm/core/Menu/MenuItem.d.ts +1 -1
  138. package/esm/core/Menu/MenuItem.js +1 -0
  139. package/esm/core/Menu/MenuItemSkeleton.d.ts +1 -1
  140. package/esm/core/Menu/MenuItemSkeleton.js +0 -1
  141. package/esm/core/SearchBox/SearchBox.js +1 -1
  142. package/esm/core/Select/Select.d.ts +4 -0
  143. package/esm/core/Select/Select.js +10 -9
  144. package/esm/core/SideNavigation/SideNavigation.js +2 -0
  145. package/esm/core/Slider/Thumb.js +1 -0
  146. package/esm/core/StatusMessage/StatusMessage.d.ts +12 -2
  147. package/esm/core/StatusMessage/StatusMessage.js +23 -16
  148. package/esm/core/Table/SubRowExpander.js +2 -0
  149. package/esm/core/Table/filters/DateRangeFilter/DatePickerInput.d.ts +6 -1
  150. package/esm/core/Table/filters/DateRangeFilter/DatePickerInput.js +45 -17
  151. package/esm/core/Table/filters/DateRangeFilter/DateRangeFilter.d.ts +2 -0
  152. package/esm/core/Table/filters/DateRangeFilter/DateRangeFilter.js +2 -0
  153. package/esm/core/Textarea/Textarea.d.ts +7 -1
  154. package/esm/core/Textarea/Textarea.js +6 -11
  155. package/esm/core/ThemeProvider/ThemeProvider.js +4 -3
  156. package/esm/core/Tile/Tile.d.ts +139 -15
  157. package/esm/core/Tile/Tile.js +107 -16
  158. package/esm/core/Toast/Toast.d.ts +12 -4
  159. package/esm/core/Toast/Toast.js +21 -4
  160. package/esm/core/Tooltip/Tooltip.d.ts +35 -28
  161. package/esm/core/Tooltip/Tooltip.js +119 -116
  162. package/esm/core/TransferList/TransferList.js +4 -9
  163. package/esm/core/index.d.ts +2 -0
  164. package/esm/core/index.js +2 -0
  165. package/esm/core/utils/components/Icon.d.ts +5 -0
  166. package/esm/core/utils/components/Icon.js +8 -1
  167. package/esm/core/utils/components/InputContainer.d.ts +0 -1
  168. package/esm/core/utils/components/InputContainer.js +14 -30
  169. package/esm/core/utils/components/InputFlexContainer.d.ts +1 -0
  170. package/esm/core/utils/components/InputFlexContainer.js +3 -1
  171. package/esm/core/utils/components/Portal.d.ts +27 -0
  172. package/esm/core/utils/components/Portal.js +36 -0
  173. package/esm/core/utils/components/index.d.ts +1 -0
  174. package/esm/core/utils/components/index.js +1 -0
  175. package/esm/core/utils/functions/index.d.ts +1 -0
  176. package/esm/core/utils/functions/index.js +1 -0
  177. package/esm/core/utils/functions/react.d.ts +8 -0
  178. package/esm/core/utils/functions/react.js +35 -0
  179. package/esm/core/utils/hooks/index.d.ts +1 -1
  180. package/esm/core/utils/hooks/index.js +1 -1
  181. package/esm/core/utils/hooks/useControlledState.d.ts +13 -0
  182. package/esm/core/utils/hooks/useControlledState.js +34 -0
  183. package/esm/styles.js +10 -28
  184. package/package.json +3 -3
  185. package/styles.css +22 -19
  186. package/cjs/core/utils/hooks/useUncontrolledState.d.ts +0 -6
  187. package/cjs/core/utils/hooks/useUncontrolledState.js +0 -18
  188. package/esm/core/utils/hooks/useUncontrolledState.d.ts +0 -6
  189. package/esm/core/utils/hooks/useUncontrolledState.js +0 -13
@@ -20,7 +20,7 @@ import { IconButton } from '../Buttons/index.js';
20
20
  import { ProgressRadial } from '../ProgressIndicators/index.js';
21
21
  const TileContext = React.createContext(undefined);
22
22
  TileContext.displayName = 'TileContext';
23
- const TileComponent = React.forwardRef((props, forwardedRef) => {
23
+ const TileWrapper = React.forwardRef((props, forwardedRef) => {
24
24
  const {
25
25
  className,
26
26
  status,
@@ -66,7 +66,7 @@ const TileComponent = React.forwardRef((props, forwardedRef) => {
66
66
  }),
67
67
  );
68
68
  });
69
- TileComponent.displayName = 'Tile';
69
+ TileWrapper.displayName = 'Tile.Wrapper';
70
70
  // ----------------------------------------------------------------------------
71
71
  // Tile.Action component
72
72
  const TileAction = React.forwardRef((props, forwardedRef) => {
@@ -257,10 +257,84 @@ TileMoreOptions.displayName = 'Tile.MoreOptions';
257
257
  // Tile.Buttons component
258
258
  const TileButtons = polymorphic('iui-tile-buttons');
259
259
  TileButtons.displayName = 'Tile.Buttons';
260
+ const TileComponent = React.forwardRef((props, forwardedRef) => {
261
+ const {
262
+ name,
263
+ description,
264
+ status,
265
+ isNew,
266
+ isLoading,
267
+ isSelected,
268
+ thumbnail,
269
+ badge,
270
+ leftIcon,
271
+ rightIcon,
272
+ buttons,
273
+ metadata,
274
+ moreOptions,
275
+ children,
276
+ isActionable,
277
+ isDisabled,
278
+ onClick,
279
+ ...rest
280
+ } = props;
281
+ return React.createElement(
282
+ TileWrapper,
283
+ {
284
+ ref: forwardedRef,
285
+ isNew: isNew,
286
+ isSelected: isSelected,
287
+ isLoading: isLoading,
288
+ status: status,
289
+ isDisabled: isDisabled,
290
+ ...rest,
291
+ },
292
+ React.createElement(
293
+ TileName,
294
+ null,
295
+ (status || isNew || isLoading || isSelected) &&
296
+ React.createElement(TileNameIcon, null),
297
+ React.createElement(
298
+ TileNameLabel,
299
+ null,
300
+ isActionable
301
+ ? React.createElement(
302
+ TileAction,
303
+ {
304
+ onClick: !isDisabled ? onClick : undefined,
305
+ 'aria-disabled': isDisabled,
306
+ },
307
+ name,
308
+ )
309
+ : name,
310
+ ),
311
+ ),
312
+ React.createElement(
313
+ TileThumbnailArea,
314
+ null,
315
+ typeof thumbnail !== 'string'
316
+ ? React.createElement(TileThumbnailPicture, null, thumbnail)
317
+ : React.createElement(TileThumbnailPicture, { url: thumbnail }),
318
+ badge && React.createElement(TileBadgeContainer, null, badge),
319
+ leftIcon && React.createElement(TileTypeIndicator, null, leftIcon),
320
+ rightIcon && React.createElement(TileQuickAction, null, rightIcon),
321
+ ),
322
+ React.createElement(
323
+ TileContentArea,
324
+ null,
325
+ description && React.createElement(TileDescription, null, description),
326
+ moreOptions && React.createElement(TileMoreOptions, null, moreOptions),
327
+ metadata && React.createElement(TileMetadata, null, metadata),
328
+ children,
329
+ ),
330
+ buttons && React.createElement(TileButtons, null, buttons),
331
+ );
332
+ });
333
+ TileComponent.displayName = 'Tile';
260
334
  /**
261
335
  * Tile with customizable Thumbnail, Name, Content and Buttons subcomponents
262
336
  * @example
263
- * <Tile>
337
+ * <Tile.Wrapper>
264
338
  * <Tile.ThumbnailArea>
265
339
  * <Tile.ThumbnailPicture/>
266
340
  * <Tile.Badge/>
@@ -270,23 +344,40 @@ TileButtons.displayName = 'Tile.Buttons';
270
344
  * <Tile.Name>
271
345
  * <Tile.NameIcon/>
272
346
  * <Tile.NameLabel/>
273
- * <Tile.Name/>
347
+ * </Tile.Name>
274
348
  * <Tile.ContentArea>
275
349
  * <Tile.Description />
276
350
  * <Tile.Metadata/>
277
351
  * <Tile.MoreOptions/>
278
352
  * </Tile.ContentArea>
279
353
  * <Tile.Buttons/>
280
- * </Tile>
354
+ * </Tile.Wrapper>
355
+ *
356
+ * @example
357
+ * <Tile
358
+ * name='Tile name'
359
+ * description='Tile description that takes upto 3 lines'
360
+ * metadata={<TagContainer><Tag variant='basic'>Tag 1</Tag></TagContainer>}
361
+ * thumbnail='/url/to/image.jpg'
362
+ * badge={<Badge backgroundColor='blue'>Badge label</Badge>}
363
+ * buttons={[<Button>Button 1</Button>, <Button>Button 2</Button>]}
364
+ * moreOptions={[<MenuItem>Item 1</MenuItem>, <MenuItem>Item 2</MenuItem>]}
365
+ * leftIcon={<IconButton><SvgInfo /></IconButton>}
366
+ * rightIcon={<IconButton><SvgStar /></IconButton>}
367
+ * isSelected={true}
368
+ * isNew={false}
369
+ * />
281
370
  */
282
371
  export const Tile = Object.assign(TileComponent, {
372
+ /**
373
+ * Wrapper subcomponent for fully customisable Tile.
374
+ */
375
+ Wrapper: TileWrapper,
283
376
  /**
284
377
  * ThumbnailArea subcomponent that contains `ThumbnailPicture`, `QuickAction`, `TypeIndicator` or `BadgeContainer`
285
378
  * @example
286
379
  * <Tile.ThumbnailArea>
287
380
  * <Tile.ThumbnailPicture/>
288
- * // or
289
- * <Tile.ThumbnailAvatar/>
290
381
  * <Tile.QuickAction/>
291
382
  * <Tile.TypeIndicator/>
292
383
  * <Tile.BadgeContainer/>
@@ -296,14 +387,14 @@ export const Tile = Object.assign(TileComponent, {
296
387
  /**
297
388
  * Thumbnail image url, a custom component or an svg for thumbnail avatar.
298
389
  * @example
299
- * <Tile>
390
+ * <Tile.Wrapper>
300
391
  * // ...
301
392
  * <Tile.ThumbnailArea>
302
393
  * <Tile.ThumbnailPicture url = '/url/to/image.jpg'/>
303
394
  * </Tile.ThumbnailArea>
304
- * </Tile>
395
+ * </Tile.Wrapper>
305
396
  * or
306
- * <Tile>
397
+ * <Tile.Wrapper>
307
398
  * // ...
308
399
  * <Tile.ThumbnailArea>
309
400
  * <Tile.ThumbnailPicture>
@@ -340,21 +431,21 @@ export const Tile = Object.assign(TileComponent, {
340
431
  /**
341
432
  * `NameIcon` next to name of the tile. Goes under <Tile.Name>
342
433
  * @example
343
- * <Tile>
434
+ * <Tile.Wrapper>
344
435
  * <Tile.Name>
345
436
  * <Tile.NameIcon/>
346
437
  * </Tile.Name>
347
- * <Tile/>
438
+ * </Tile.Wrapper>
348
439
  */
349
440
  NameIcon: TileNameIcon,
350
441
  /*
351
442
  * `NameLabel` of the tile
352
443
  * @example
353
- * <Tile>
444
+ * <Tile.Wrapper>
354
445
  * <Tile.Name>
355
446
  * <Tile.NameLabel> Tile Name <Tile.NameLabel/>
356
447
  * </Tile.Name/>
357
- * <Tile/>
448
+ * </Tile.Wrapper>
358
449
  */
359
450
  NameLabel: TileNameLabel,
360
451
  /**
@@ -371,13 +462,13 @@ export const Tile = Object.assign(TileComponent, {
371
462
  /**
372
463
  * Tile content area that contains `Description`, `Metadata` and `MoreOptions` Tile subcomponents
373
464
  * @example
374
- * <Tile>
465
+ * <Tile.Wrapper>
375
466
  * <Tile.ContentArea>
376
467
  * <Tile.Description/>
377
468
  * <Tile.Metadata/>
378
469
  * <Tile.MoreOptions/>
379
470
  * </Tile.ContentArea>
380
- * </Tile>
471
+ * </Tile.Wrapper>
381
472
  */
382
473
  ContentArea: TileContentArea,
383
474
  /**
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import type { CommonProps } from '../utils/index.js';
2
+ import type { PolymorphicForwardRefComponent } from '../utils/index.js';
3
3
  export type ToastCategory = 'informational' | 'negative' | 'positive' | 'warning';
4
4
  export type ToastProps = {
5
5
  /**
@@ -10,6 +10,13 @@ export type ToastProps = {
10
10
  * Content of the Toast message
11
11
  */
12
12
  content: React.ReactNode;
13
+ /**
14
+ * Passes props to toast and content
15
+ */
16
+ domProps?: {
17
+ toastProps?: React.ComponentProps<'div'>;
18
+ contentProps?: React.ComponentProps<'div'>;
19
+ };
13
20
  /**
14
21
  * Category of the Toast, which controls the border color, as well as the category icon.
15
22
  */
@@ -60,12 +67,13 @@ export type ToastProps = {
60
67
  * <Toast type='persisting' content='Job processing error.' category='negative' />
61
68
  */
62
69
  export declare const Toast: (props: ToastProps) => React.JSX.Element;
63
- export type ToastPresentationProps = Omit<ToastProps, 'duration' | 'id' | 'isVisible' | 'onRemove'> & {
70
+ export type ToastPresentationProps = Omit<ToastProps, 'duration' | 'id' | 'isVisible' | 'onRemove' | 'domProps'> & {
64
71
  onClose?: () => void;
65
- } & CommonProps;
72
+ contentProps?: React.ComponentProps<'div'>;
73
+ };
66
74
  /**
67
75
  * The presentational part of a toast, without any animation or logic.
68
76
  * @private
69
77
  */
70
- export declare const ToastPresentation: (props: ToastPresentationProps) => React.JSX.Element;
78
+ export declare const ToastPresentation: PolymorphicForwardRefComponent<"div", ToastPresentationProps>;
71
79
  export default Toast;
@@ -36,6 +36,7 @@ export const Toast = (props) => {
36
36
  hasCloseButton,
37
37
  onRemove,
38
38
  animateOutTo,
39
+ domProps,
39
40
  } = props;
40
41
  const closeTimeout = React.useRef(0);
41
42
  const { placement } = useSafeContext(ToasterStateContext).settings;
@@ -148,12 +149,15 @@ export const Toast = (props) => {
148
149
  'div',
149
150
  { ref: onRef },
150
151
  React.createElement(ToastPresentation, {
152
+ as: 'div',
151
153
  category: category,
152
154
  content: content,
153
155
  link: link,
154
156
  type: type,
155
157
  hasCloseButton: hasCloseButton,
156
158
  onClose: close,
159
+ ...domProps?.toastProps,
160
+ contentProps: domProps?.contentProps,
157
161
  }),
158
162
  ),
159
163
  ),
@@ -163,7 +167,7 @@ export const Toast = (props) => {
163
167
  * The presentational part of a toast, without any animation or logic.
164
168
  * @private
165
169
  */
166
- export const ToastPresentation = (props) => {
170
+ export const ToastPresentation = React.forwardRef((props, forwardedRef) => {
167
171
  const {
168
172
  content,
169
173
  category,
@@ -172,18 +176,31 @@ export const ToastPresentation = (props) => {
172
176
  hasCloseButton,
173
177
  onClose,
174
178
  className,
179
+ contentProps,
175
180
  ...rest
176
181
  } = props;
177
182
  const StatusIcon = StatusIconMap[category];
178
183
  return React.createElement(
179
184
  Box,
180
- { className: cx(`iui-toast iui-${category}`, className), ...rest },
185
+ {
186
+ className: cx(`iui-toast iui-${category}`, className),
187
+ ref: forwardedRef,
188
+ ...rest,
189
+ },
181
190
  React.createElement(
182
191
  Box,
183
192
  { className: 'iui-status-area' },
184
193
  React.createElement(StatusIcon, { className: 'iui-icon' }),
185
194
  ),
186
- React.createElement(Box, { className: 'iui-message' }, content),
195
+ React.createElement(
196
+ Box,
197
+ {
198
+ as: 'div',
199
+ ...contentProps,
200
+ className: cx('iui-message', contentProps?.className),
201
+ },
202
+ content,
203
+ ),
187
204
  link &&
188
205
  React.createElement(
189
206
  ButtonBase,
@@ -202,5 +219,5 @@ export const ToastPresentation = (props) => {
202
219
  React.createElement(SvgCloseSmall, null),
203
220
  ),
204
221
  );
205
- };
222
+ });
206
223
  export default Toast;
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import type { Placement } from '@floating-ui/react';
3
- import type { PolymorphicForwardRefComponent } from '../utils/index.js';
3
+ import type { PolymorphicForwardRefComponent, PortalProps } from '../utils/index.js';
4
4
  type TooltipOptions = {
5
5
  /**
6
6
  * Placement of the Tooltip
@@ -11,6 +11,11 @@ type TooltipOptions = {
11
11
  * Property for manual visibility control
12
12
  */
13
13
  visible?: boolean;
14
+ /**
15
+ * Callback invoked every time the tooltip visibility changes as a result
16
+ * of internal logic. Should be used alongside `visible` prop.
17
+ */
18
+ onVisibleChange?: (visible: boolean) => void;
14
19
  /**
15
20
  * autoUpdate options that recalculates position
16
21
  * to ensure the floating element remains anchored
@@ -42,34 +47,26 @@ type TooltipOptions = {
42
47
  hide?: boolean;
43
48
  inline?: boolean;
44
49
  };
45
- };
46
- type TooltipOwnProps = {
47
- /**
48
- * Content of the tooltip.
49
- */
50
- content: React.ReactNode;
51
- /**
52
- * Element to have tooltip on. Has to be a valid JSX element and needs to forward its ref.
53
- * If not specified, the `reference` prop should be used instead.
54
- */
55
- children?: React.ReactNode;
56
- /**
57
- * Element to portal tooltip to.
58
- * Portals to ThemeProvider portalContainer by default.
59
- * @default true;
60
- */
61
- portal?: boolean | {
62
- to: HTMLElement;
63
- };
64
50
  /**
65
51
  * Sets reference point to user provided element.
66
52
  * @example
67
- * const buttonRef = React.useRef();
53
+ * const [trigger, setTrigger] = React.useState(null);
68
54
  * ...
69
- * <Button ref={buttonRef} />
70
- * <Tooltip content='tooltip text' reference={buttonRef} />
55
+ * <Button ref={setTrigger} />
56
+ * <Tooltip content='tooltip text' reference={trigger} />
71
57
  */
72
- reference?: React.RefObject<HTMLElement>;
58
+ reference?: HTMLElement | null;
59
+ /**
60
+ * By default, the tooltip will be associated with the reference element
61
+ * using `aria-describedby`.
62
+ *
63
+ * Pass "label" if you want to use `aria-labelledby` instead, or pass "none"
64
+ * if you don't want any association.
65
+ *
66
+ * @default 'description'
67
+ */
68
+ ariaStrategy?: 'description' | 'label' | 'none';
69
+ id?: string;
73
70
  };
74
71
  /**
75
72
  * Basic tooltip component to display informative content when an element is hovered or focused.
@@ -77,10 +74,20 @@ type TooltipOwnProps = {
77
74
  * @example
78
75
  * <Tooltip content='tooltip text' placement='top'>Hover here</Tooltip>
79
76
  * @example
80
- * const buttonRef = React.useRef();
77
+ * const [trigger, setTrigger] = React.useState(null);
81
78
  * ...
82
- * <Button ref={buttonRef} />
83
- * <Tooltip content='tooltip text' reference={buttonRef} />
79
+ * <Button ref={setTrigger} />
80
+ * <Tooltip content='tooltip text' reference={trigger} />
84
81
  */
85
- export declare const Tooltip: PolymorphicForwardRefComponent<"div", TooltipOwnProps & TooltipOptions>;
82
+ export declare const Tooltip: PolymorphicForwardRefComponent<"div", {
83
+ /**
84
+ * Content of the tooltip.
85
+ */
86
+ content: React.ReactNode;
87
+ /**
88
+ * Element to have tooltip on. Has to be a valid JSX element and needs to forward its ref.
89
+ * If not specified, the `reference` prop should be used instead.
90
+ */
91
+ children?: React.ReactNode;
92
+ } & PortalProps & TooltipOptions>;
86
93
  export default Tooltip;
@@ -14,46 +14,46 @@ import {
14
14
  useHover,
15
15
  useFocus,
16
16
  useDismiss,
17
- useRole,
18
17
  useInteractions,
19
18
  safePolygon,
20
19
  size,
21
20
  autoPlacement,
22
21
  hide,
23
22
  inline,
23
+ useDelayGroupContext,
24
+ useDelayGroup,
24
25
  } from '@floating-ui/react';
25
26
  import {
26
27
  Box,
27
- getDocument,
28
- mergeRefs,
29
- useGlobals,
28
+ Portal,
29
+ cloneElementWithRef,
30
+ useControlledState,
31
+ useId,
30
32
  useMergedRefs,
31
33
  } from '../utils/index.js';
32
- import ReactDOM from 'react-dom';
33
34
  const useTooltip = (options = {}) => {
35
+ const uniqueId = useId();
34
36
  const {
35
- placement,
36
- visible: controlledOpen,
37
- middleware = {
38
- flip: true,
39
- shift: true,
40
- },
37
+ placement = 'top',
38
+ visible,
39
+ onVisibleChange,
40
+ middleware = { flip: true, shift: true },
41
41
  autoUpdateOptions = {},
42
+ reference,
43
+ ariaStrategy = 'description',
44
+ id = uniqueId,
45
+ ...props
42
46
  } = options;
43
- const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false);
44
- const open = controlledOpen ?? uncontrolledOpen;
45
- const data = useFloating({
47
+ const [open, onOpenChange] = useControlledState(
48
+ false,
49
+ visible,
50
+ onVisibleChange,
51
+ );
52
+ const floating = useFloating({
46
53
  placement,
47
54
  open,
48
- onOpenChange: setUncontrolledOpen,
49
- whileElementsMounted: (referenceEl, floatingEl, update) =>
50
- autoUpdate(referenceEl, floatingEl, update, {
51
- animationFrame: autoUpdateOptions.animationFrame,
52
- ancestorScroll: autoUpdateOptions.ancestorScroll,
53
- ancestorResize: autoUpdateOptions.ancestorResize,
54
- elementResize: autoUpdateOptions.elementResize,
55
- layoutShift: autoUpdateOptions.layoutShift,
56
- }),
55
+ onOpenChange,
56
+ whileElementsMounted: (...args) => autoUpdate(...args, autoUpdateOptions),
57
57
  middleware: [
58
58
  middleware.offset !== undefined ? offset(middleware.offset) : offset(4),
59
59
  middleware.flip && flip(),
@@ -63,35 +63,81 @@ const useTooltip = (options = {}) => {
63
63
  middleware.inline && inline(),
64
64
  middleware.hide && hide(),
65
65
  ].filter(Boolean),
66
+ ...(reference && { elements: { reference } }),
66
67
  });
67
- const context = data.context;
68
- const hover = useHover(context, {
69
- enabled: controlledOpen == null,
70
- delay: {
71
- open: 50,
72
- close: 250,
68
+ const ariaProps = React.useMemo(
69
+ () =>
70
+ ariaStrategy === 'description'
71
+ ? { 'aria-describedby': id }
72
+ : ariaStrategy === 'label'
73
+ ? { 'aria-labelledby': id }
74
+ : {},
75
+ [ariaStrategy, id],
76
+ );
77
+ const { delay } = useDelayGroupContext();
78
+ useDelayGroup(floating.context, { id: useId() });
79
+ const interactions = useInteractions([
80
+ useHover(floating.context, {
81
+ delay: delay ?? { open: 50, close: 250 },
82
+ handleClose: safePolygon({ buffer: -Infinity }),
83
+ }),
84
+ useFocus(floating.context),
85
+ useClick(floating.context),
86
+ useDismiss(floating.context),
87
+ ]);
88
+ // Manually add attributes and event handlers to external reference element,
89
+ // because we cannot spread getReferenceProps onto it.
90
+ React.useEffect(() => {
91
+ if (!reference) {
92
+ return;
93
+ }
94
+ /** e.g. onPointerDown --> pointerdown */
95
+ const domEventName = (e) => e.toLowerCase().substring(2);
96
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
97
+ const cleanupValues = {};
98
+ Object.entries({
99
+ ...ariaProps,
100
+ ...interactions.getReferenceProps(),
101
+ }).forEach(([key, value]) => {
102
+ if (typeof value === 'function') {
103
+ reference.addEventListener(domEventName(key), value);
104
+ cleanupValues[key] = value;
105
+ } else if (value) {
106
+ cleanupValues[key] = reference.getAttribute(key);
107
+ reference.setAttribute(key, value);
108
+ }
109
+ });
110
+ return () => {
111
+ Object.entries(cleanupValues).forEach(([key, value]) => {
112
+ if (typeof value === 'function') {
113
+ reference.removeEventListener(domEventName(key), value);
114
+ } else if (value) {
115
+ reference.setAttribute(key, value);
116
+ } else {
117
+ reference.removeAttribute(key);
118
+ }
119
+ });
120
+ };
121
+ }, [ariaProps, reference, interactions]);
122
+ const getReferenceProps = React.useCallback(
123
+ (userProps) => {
124
+ return interactions.getReferenceProps({ ...userProps, ...ariaProps });
73
125
  },
74
- handleClose: safePolygon({ buffer: -Infinity }),
75
- });
76
- const focus = useFocus(context, {
77
- enabled: controlledOpen == null,
78
- });
79
- const click = useClick(context, {
80
- enabled: controlledOpen == null,
81
- });
82
- const dismiss = useDismiss(context, {
83
- enabled: controlledOpen == null,
84
- });
85
- const role = useRole(context, { role: 'tooltip' });
86
- const interactions = useInteractions([click, hover, focus, dismiss, role]);
126
+ [interactions, ariaProps],
127
+ );
128
+ const floatingProps = React.useMemo(
129
+ () =>
130
+ interactions.getFloatingProps({
131
+ hidden: !open,
132
+ 'aria-hidden': 'true',
133
+ ...props,
134
+ id,
135
+ }),
136
+ [interactions, props, id, open],
137
+ );
87
138
  return React.useMemo(
88
- () => ({
89
- open,
90
- setUncontrolledOpen,
91
- ...interactions,
92
- ...data,
93
- }),
94
- [open, interactions, data],
139
+ () => ({ getReferenceProps, floatingProps, ...floating }),
140
+ [getReferenceProps, floatingProps, floating],
95
141
  );
96
142
  };
97
143
  /**
@@ -100,78 +146,35 @@ const useTooltip = (options = {}) => {
100
146
  * @example
101
147
  * <Tooltip content='tooltip text' placement='top'>Hover here</Tooltip>
102
148
  * @example
103
- * const buttonRef = React.useRef();
149
+ * const [trigger, setTrigger] = React.useState(null);
104
150
  * ...
105
- * <Button ref={buttonRef} />
106
- * <Tooltip content='tooltip text' reference={buttonRef} />
151
+ * <Button ref={setTrigger} />
152
+ * <Tooltip content='tooltip text' reference={trigger} />
107
153
  */
108
- export const Tooltip = React.forwardRef((props, forwardRef) => {
109
- const {
110
- content,
111
- children,
112
- portal = true,
113
- placement = 'top',
114
- autoUpdateOptions,
115
- middleware,
116
- style,
117
- className,
118
- visible,
119
- reference,
120
- ...rest
121
- } = props;
122
- const tooltip = useTooltip({
123
- placement,
124
- visible,
125
- autoUpdateOptions,
126
- middleware,
127
- });
128
- const context = useGlobals();
129
- React.useEffect(() => {
130
- if (reference) {
131
- tooltip.refs.setReference(reference.current);
132
- }
133
- }, [reference, tooltip.refs]);
134
- const portalTo =
135
- typeof portal !== 'boolean'
136
- ? portal.to
137
- : portal
138
- ? context?.portalContainer || getDocument()?.body
139
- : null;
140
- const contentBox = React.createElement(
141
- Box,
142
- {
143
- className: cx('iui-tooltip', className),
144
- ref: useMergedRefs(tooltip.refs.setFloating, forwardRef),
145
- style: { ...tooltip.floatingStyles, ...style },
146
- ...tooltip.getFloatingProps(),
147
- ...rest,
148
- },
149
- content,
150
- );
151
- const childrenRef =
152
- React.isValidElement(children) &&
153
- mergeRefs(
154
- tooltip.refs.setReference,
155
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
156
- children.ref,
157
- );
154
+ export const Tooltip = React.forwardRef((props, forwardedRef) => {
155
+ const { content, children, portal = true, className, style, ...rest } = props;
156
+ const tooltip = useTooltip(rest);
158
157
  return React.createElement(
159
158
  React.Fragment,
160
159
  null,
161
- React.isValidElement(children)
162
- ? React.cloneElement(
163
- children,
164
- tooltip.getReferenceProps({
165
- ref: childrenRef,
166
- ...children.props,
167
- }),
168
- )
169
- : null,
170
- tooltip.open
171
- ? portalTo
172
- ? ReactDOM.createPortal(contentBox, portalTo)
173
- : contentBox
174
- : null,
160
+ cloneElementWithRef(children, (children) => ({
161
+ ...tooltip.getReferenceProps(children.props),
162
+ ref: tooltip.refs.setReference,
163
+ })),
164
+ React.createElement(
165
+ Portal,
166
+ { portal: portal },
167
+ React.createElement(
168
+ Box,
169
+ {
170
+ className: cx('iui-tooltip', className),
171
+ ref: useMergedRefs(tooltip.refs.setFloating, forwardedRef),
172
+ style: { ...tooltip.floatingStyles, ...style },
173
+ ...tooltip.floatingProps,
174
+ },
175
+ content,
176
+ ),
177
+ ),
175
178
  );
176
179
  });
177
180
  export default Tooltip;