@itwin/itwinui-react 3.17.0-dev.1 → 3.17.0-dev.2

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 (82) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/DEV-cjs/core/Dialog/Dialog.js +35 -42
  3. package/DEV-cjs/core/Dialog/DialogBackdrop.js +6 -1
  4. package/DEV-cjs/core/Dialog/DialogMain.js +42 -34
  5. package/DEV-cjs/core/Dialog/DialogMainContext.js +23 -0
  6. package/DEV-cjs/core/Dialog/DialogTitleBar.js +10 -1
  7. package/DEV-cjs/core/Panels/Panels.js +1 -1
  8. package/DEV-cjs/core/SideNavigation/SideNavigation.js +1 -11
  9. package/DEV-cjs/core/Table/actionHandlers/selectHandler.js +7 -8
  10. package/DEV-cjs/core/Tabs/Tabs.js +9 -1
  11. package/DEV-cjs/core/ThemeProvider/ThemeProvider.js +1 -2
  12. package/DEV-cjs/core/Toast/Toast.js +132 -77
  13. package/DEV-cjs/core/Toast/Toaster.js +11 -3
  14. package/DEV-cjs/styles.js +1 -1
  15. package/DEV-cjs/utils/components/index.js +0 -1
  16. package/DEV-esm/core/Dialog/Dialog.js +32 -42
  17. package/DEV-esm/core/Dialog/DialogBackdrop.js +6 -1
  18. package/DEV-esm/core/Dialog/DialogMain.js +42 -34
  19. package/DEV-esm/core/Dialog/DialogMainContext.js +3 -0
  20. package/DEV-esm/core/Dialog/DialogTitleBar.js +10 -1
  21. package/DEV-esm/core/Panels/Panels.js +2 -1
  22. package/DEV-esm/core/SideNavigation/SideNavigation.js +2 -17
  23. package/DEV-esm/core/Table/actionHandlers/selectHandler.js +7 -8
  24. package/DEV-esm/core/Tabs/Tabs.js +9 -1
  25. package/DEV-esm/core/ThemeProvider/ThemeProvider.js +1 -2
  26. package/DEV-esm/core/Toast/Toast.js +131 -75
  27. package/DEV-esm/core/Toast/Toaster.js +11 -3
  28. package/DEV-esm/styles.js +1 -1
  29. package/DEV-esm/utils/components/index.js +0 -1
  30. package/LICENSE.md +1 -1
  31. package/cjs/core/Dialog/Dialog.d.ts +1 -1
  32. package/cjs/core/Dialog/Dialog.js +35 -42
  33. package/cjs/core/Dialog/DialogBackdrop.d.ts +1 -1
  34. package/cjs/core/Dialog/DialogBackdrop.js +6 -1
  35. package/cjs/core/Dialog/DialogMain.js +42 -34
  36. package/cjs/core/Dialog/DialogMainContext.d.ts +7 -0
  37. package/cjs/core/Dialog/DialogMainContext.js +23 -0
  38. package/cjs/core/Dialog/DialogTitleBar.js +10 -1
  39. package/cjs/core/Panels/Panels.js +1 -1
  40. package/cjs/core/SideNavigation/SideNavigation.d.ts +2 -2
  41. package/cjs/core/SideNavigation/SideNavigation.js +1 -11
  42. package/cjs/core/Table/actionHandlers/selectHandler.js +7 -8
  43. package/cjs/core/Tabs/Tabs.js +9 -1
  44. package/cjs/core/ThemeProvider/ThemeProvider.d.ts +2 -0
  45. package/cjs/core/ThemeProvider/ThemeProvider.js +1 -2
  46. package/cjs/core/Toast/Toast.d.ts +1 -1
  47. package/cjs/core/Toast/Toast.js +132 -77
  48. package/cjs/core/Toast/Toaster.d.ts +1 -1
  49. package/cjs/core/Toast/Toaster.js +11 -3
  50. package/cjs/styles.js +1 -1
  51. package/cjs/utils/components/index.d.ts +0 -1
  52. package/cjs/utils/components/index.js +0 -1
  53. package/esm/core/Dialog/Dialog.d.ts +1 -1
  54. package/esm/core/Dialog/Dialog.js +32 -42
  55. package/esm/core/Dialog/DialogBackdrop.d.ts +1 -1
  56. package/esm/core/Dialog/DialogBackdrop.js +6 -1
  57. package/esm/core/Dialog/DialogMain.js +42 -34
  58. package/esm/core/Dialog/DialogMainContext.d.ts +7 -0
  59. package/esm/core/Dialog/DialogMainContext.js +3 -0
  60. package/esm/core/Dialog/DialogTitleBar.js +10 -1
  61. package/esm/core/Panels/Panels.js +2 -1
  62. package/esm/core/SideNavigation/SideNavigation.d.ts +2 -2
  63. package/esm/core/SideNavigation/SideNavigation.js +2 -17
  64. package/esm/core/Table/actionHandlers/selectHandler.js +7 -8
  65. package/esm/core/Tabs/Tabs.js +9 -1
  66. package/esm/core/ThemeProvider/ThemeProvider.d.ts +2 -0
  67. package/esm/core/ThemeProvider/ThemeProvider.js +1 -2
  68. package/esm/core/Toast/Toast.d.ts +1 -1
  69. package/esm/core/Toast/Toast.js +131 -75
  70. package/esm/core/Toast/Toaster.d.ts +1 -1
  71. package/esm/core/Toast/Toaster.js +11 -3
  72. package/esm/styles.js +1 -1
  73. package/esm/utils/components/index.d.ts +0 -1
  74. package/esm/utils/components/index.js +0 -1
  75. package/package.json +3 -5
  76. package/styles.css +10 -10
  77. package/DEV-cjs/utils/components/WithCSSTransition.js +0 -60
  78. package/DEV-esm/utils/components/WithCSSTransition.js +0 -49
  79. package/cjs/utils/components/WithCSSTransition.d.ts +0 -6
  80. package/cjs/utils/components/WithCSSTransition.js +0 -60
  81. package/esm/utils/components/WithCSSTransition.d.ts +0 -6
  82. package/esm/utils/components/WithCSSTransition.js +0 -49
@@ -96,16 +96,24 @@ const ToastProvider = ({ children, inherit = false }) => {
96
96
  placement: 'top',
97
97
  },
98
98
  });
99
- if (_react.useContext(ToasterStateContext) && inherit) return children;
99
+ let toasterDispatchContext = _react.useContext(ToasterDispatchContext);
100
+ let toasterStateContext = _react.useContext(ToasterStateContext);
101
+ let shouldReuse = toasterStateContext && inherit;
102
+ let toasterDispatchContextValue = shouldReuse
103
+ ? toasterDispatchContext
104
+ : dispatch;
105
+ let toasterStateContextValue = shouldReuse
106
+ ? toasterStateContext
107
+ : toasterState;
100
108
  return _react.createElement(
101
109
  ToasterDispatchContext.Provider,
102
110
  {
103
- value: dispatch,
111
+ value: toasterDispatchContextValue,
104
112
  },
105
113
  _react.createElement(
106
114
  ToasterStateContext.Provider,
107
115
  {
108
- value: toasterState,
116
+ value: toasterStateContextValue,
109
117
  },
110
118
  children,
111
119
  ),
package/DEV-cjs/styles.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3
- const e = '3.17.0-dev.1';
3
+ const e = '3.17.0-dev.2';
4
4
  const u = new Proxy(
5
5
  {},
6
6
  {
@@ -8,7 +8,6 @@ _export_star._(require('./FocusTrap.js'), exports);
8
8
  _export_star._(require('./InputContainer.js'), exports);
9
9
  _export_star._(require('./InputFlexContainer.js'), exports);
10
10
  _export_star._(require('./InputWithIcon.js'), exports);
11
- _export_star._(require('./WithCSSTransition.js'), exports);
12
11
  _export_star._(require('./MiddleTextTruncation.js'), exports);
13
12
  _export_star._(require('./AutoclearingHiddenLiveRegion.js'), exports);
14
13
  _export_star._(require('./Box.js'), exports);
@@ -7,7 +7,6 @@ import { DialogContext } from './DialogContext.js';
7
7
  import { DialogButtonBar } from './DialogButtonBar.js';
8
8
  import { DialogMain } from './DialogMain.js';
9
9
  import { useMergedRefs, Box, Portal } from '../../utils/index.js';
10
- import { Transition } from 'react-transition-group';
11
10
  let DialogComponent = React.forwardRef((props, ref) => {
12
11
  let {
13
12
  trapFocus = false,
@@ -27,49 +26,40 @@ let DialogComponent = React.forwardRef((props, ref) => {
27
26
  ...rest
28
27
  } = props;
29
28
  let dialogRootRef = React.useRef(null);
30
- return React.createElement(
31
- Transition,
32
- {
33
- in: isOpen,
34
- timeout: {
35
- exit: 600,
36
- },
37
- mountOnEnter: true,
38
- unmountOnExit: true,
39
- },
40
- React.createElement(
41
- DialogContext.Provider,
42
- {
43
- value: {
44
- isOpen,
45
- onClose,
46
- closeOnEsc,
47
- closeOnExternalClick,
48
- isDismissible,
49
- preventDocumentScroll,
50
- trapFocus,
51
- setFocus,
52
- isDraggable,
53
- isResizable,
54
- relativeTo,
55
- dialogRootRef,
56
- placement,
57
- },
58
- },
59
- React.createElement(
60
- Portal,
29
+ let mergedRefs = useMergedRefs(ref, dialogRootRef);
30
+ return isOpen
31
+ ? React.createElement(
32
+ DialogContext.Provider,
61
33
  {
62
- portal: portal,
34
+ value: {
35
+ isOpen,
36
+ onClose,
37
+ closeOnEsc,
38
+ closeOnExternalClick,
39
+ isDismissible,
40
+ preventDocumentScroll,
41
+ trapFocus,
42
+ setFocus,
43
+ isDraggable,
44
+ isResizable,
45
+ relativeTo,
46
+ placement,
47
+ },
63
48
  },
64
- React.createElement(Box, {
65
- className: cx('iui-dialog-wrapper', className),
66
- 'data-iui-relative': 'container' === relativeTo,
67
- ref: useMergedRefs(ref, dialogRootRef),
68
- ...rest,
69
- }),
70
- ),
71
- ),
72
- );
49
+ React.createElement(
50
+ Portal,
51
+ {
52
+ portal: portal,
53
+ },
54
+ React.createElement(Box, {
55
+ className: cx('iui-dialog-wrapper', className),
56
+ 'data-iui-relative': 'container' === relativeTo,
57
+ ref: mergedRefs,
58
+ ...rest,
59
+ }),
60
+ ),
61
+ )
62
+ : null;
73
63
  });
74
64
  DialogComponent.displayName = 'Dialog';
75
65
  export const Dialog = Object.assign(DialogComponent, {
@@ -2,9 +2,11 @@ import * as React from 'react';
2
2
  import { Backdrop } from '../Backdrop/Backdrop.js';
3
3
  import { useMergedRefs } from '../../utils/index.js';
4
4
  import { useDialogContext } from './DialogContext.js';
5
+ import { useDialogMainContext } from './DialogMainContext.js';
5
6
  import cx from 'classnames';
6
7
  export const DialogBackdrop = React.forwardRef((props, ref) => {
7
8
  let dialogContext = useDialogContext();
9
+ let dialogMainContext = useDialogMainContext();
8
10
  let {
9
11
  isVisible = dialogContext.isOpen,
10
12
  isDismissible = dialogContext.isDismissible,
@@ -21,7 +23,10 @@ export const DialogBackdrop = React.forwardRef((props, ref) => {
21
23
  let handleMouseDown = (event) => {
22
24
  event.persist();
23
25
  if (event.target !== backdropRef.current) return;
24
- if (isDismissible && closeOnExternalClick && onClose) onClose(event);
26
+ if (isDismissible && closeOnExternalClick && onClose) {
27
+ dialogMainContext?.beforeClose();
28
+ onClose(event);
29
+ }
25
30
  onMouseDown?.(event);
26
31
  };
27
32
  return React.createElement(Backdrop, {
@@ -10,9 +10,9 @@ import {
10
10
  ShadowRoot,
11
11
  } from '../../utils/index.js';
12
12
  import { useDialogContext } from './DialogContext.js';
13
- import { Transition } from 'react-transition-group';
14
13
  import { DialogDragContext } from './DialogDragContext.js';
15
14
  import { useDragAndDrop } from '../../utils/hooks/useDragAndDrop.js';
15
+ import { DialogMainContext } from './DialogMainContext.js';
16
16
  export const DialogMain = React.forwardRef((props, ref) => {
17
17
  let dialogContext = useDialogContext();
18
18
  let {
@@ -33,12 +33,13 @@ export const DialogMain = React.forwardRef((props, ref) => {
33
33
  placement = dialogContext.placement,
34
34
  ...rest
35
35
  } = props;
36
- let [style, setStyle] = React.useState();
36
+ let { dialogRootRef } = dialogContext;
37
37
  let dialogRef = React.useRef(null);
38
- let hasBeenResized = React.useRef(false);
39
38
  let previousFocusedElement = React.useRef();
39
+ let [style, setStyle] = React.useState();
40
+ let hasBeenResized = React.useRef(false);
40
41
  let originalBodyOverflow = React.useRef('');
41
- React.useEffect(() => {
42
+ useLayoutEffect(() => {
42
43
  if (isOpen) originalBodyOverflow.current = document.body.style.overflow;
43
44
  }, [isOpen]);
44
45
  React.useEffect(() => {
@@ -54,17 +55,19 @@ export const DialogMain = React.forwardRef((props, ref) => {
54
55
  return () => {
55
56
  ownerDocument.body.style.overflow = originalBodyOverflow.current;
56
57
  };
57
- }, [isOpen, preventDocumentScroll]);
58
+ }, [dialogRef, isOpen, preventDocumentScroll]);
58
59
  let handleKeyDown = (event) => {
59
60
  if (event.altKey) return;
60
61
  event.persist();
61
- if (isDismissible && closeOnEsc && 'Escape' === event.key && onClose)
62
+ if (isDismissible && closeOnEsc && 'Escape' === event.key && onClose) {
63
+ beforeClose();
62
64
  onClose(event);
65
+ }
63
66
  onKeyDown?.(event);
64
67
  };
65
68
  let { onPointerDown, transform } = useDragAndDrop(
66
69
  dialogRef,
67
- dialogContext.dialogRootRef,
70
+ dialogRootRef,
68
71
  isDraggable,
69
72
  );
70
73
  let handlePointerDown = React.useCallback(
@@ -84,13 +87,35 @@ export const DialogMain = React.forwardRef((props, ref) => {
84
87
  insetBlockStart: dialogRef.current?.offsetTop,
85
88
  transform: `translate(${translateX}px,${translateY}px)`,
86
89
  }));
87
- }, [isDraggable, isOpen]);
90
+ }, [dialogRef, isDraggable, isOpen]);
88
91
  let setResizeStyle = React.useCallback((newStyle) => {
89
92
  setStyle((oldStyle) => ({
90
93
  ...oldStyle,
91
94
  ...newStyle,
92
95
  }));
93
96
  }, []);
97
+ let onEnter = React.useCallback(() => {
98
+ previousFocusedElement.current =
99
+ dialogRef.current?.ownerDocument.activeElement;
100
+ if (setFocus)
101
+ dialogRef.current?.focus({
102
+ preventScroll: true,
103
+ });
104
+ }, [dialogRef, previousFocusedElement, setFocus]);
105
+ let beforeClose = React.useCallback(() => {
106
+ if (
107
+ dialogRef.current?.contains(
108
+ dialogRef.current?.ownerDocument.activeElement,
109
+ )
110
+ )
111
+ previousFocusedElement.current?.focus();
112
+ }, [dialogRef, previousFocusedElement]);
113
+ let mountRef = React.useCallback(
114
+ (element) => {
115
+ if (element) onEnter();
116
+ },
117
+ [onEnter],
118
+ );
94
119
  let content = React.createElement(
95
120
  Box,
96
121
  {
@@ -105,7 +130,7 @@ export const DialogMain = React.forwardRef((props, ref) => {
105
130
  className,
106
131
  ),
107
132
  role: 'dialog',
108
- ref: useMergedRefs(dialogRef, ref),
133
+ ref: useMergedRefs(dialogRef, mountRef, ref),
109
134
  onKeyDown: handleKeyDown,
110
135
  tabIndex: -1,
111
136
  'data-iui-placement': placement,
@@ -123,7 +148,7 @@ export const DialogMain = React.forwardRef((props, ref) => {
123
148
  isResizable &&
124
149
  React.createElement(Resizer, {
125
150
  elementRef: dialogRef,
126
- containerRef: dialogContext.dialogRootRef,
151
+ containerRef: dialogRootRef,
127
152
  onResizeStart: () => {
128
153
  if (!hasBeenResized.current) {
129
154
  hasBeenResized.current = true;
@@ -138,31 +163,14 @@ export const DialogMain = React.forwardRef((props, ref) => {
138
163
  children,
139
164
  );
140
165
  return React.createElement(
141
- Transition,
166
+ DialogMainContext.Provider,
142
167
  {
143
- in: isOpen,
144
- appear: true,
145
- timeout: {
146
- exit: 600,
147
- },
148
- onEntered: () => {
149
- previousFocusedElement.current =
150
- dialogRef.current?.ownerDocument.activeElement;
151
- setFocus &&
152
- dialogRef.current?.focus({
153
- preventScroll: true,
154
- });
155
- },
156
- onExit: () => {
157
- if (
158
- dialogRef.current?.contains(
159
- dialogRef.current?.ownerDocument.activeElement,
160
- )
161
- )
162
- previousFocusedElement.current?.focus();
163
- },
164
- unmountOnExit: true,
165
- nodeRef: dialogRef,
168
+ value: React.useMemo(
169
+ () => ({
170
+ beforeClose,
171
+ }),
172
+ [beforeClose],
173
+ ),
166
174
  },
167
175
  React.createElement(
168
176
  DialogDragContext.Provider,
@@ -0,0 +1,3 @@
1
+ import * as React from 'react';
2
+ export const DialogMainContext = React.createContext(null);
3
+ export const useDialogMainContext = () => React.useContext(DialogMainContext);
@@ -3,11 +3,13 @@ import cx from 'classnames';
3
3
  import { SvgClose, mergeEventHandlers, Box } from '../../utils/index.js';
4
4
  import { IconButton } from '../Buttons/IconButton.js';
5
5
  import { useDialogContext } from './DialogContext.js';
6
+ import { useDialogMainContext } from './DialogMainContext.js';
6
7
  import { DialogTitleBarTitle } from './DialogTitleBarTitle.js';
7
8
  import { useDialogDragContext } from './DialogDragContext.js';
8
9
  export const DialogTitleBar = Object.assign(
9
10
  React.forwardRef((props, ref) => {
10
11
  let dialogContext = useDialogContext();
12
+ let dialogMainContext = useDialogMainContext();
11
13
  let {
12
14
  children,
13
15
  titleText,
@@ -19,6 +21,13 @@ export const DialogTitleBar = Object.assign(
19
21
  ...rest
20
22
  } = props;
21
23
  let { onPointerDown } = useDialogDragContext();
24
+ let onClick = React.useCallback(
25
+ (e) => {
26
+ dialogMainContext?.beforeClose();
27
+ onClose?.(e);
28
+ },
29
+ [dialogMainContext, onClose],
30
+ );
22
31
  return React.createElement(
23
32
  Box,
24
33
  {
@@ -41,7 +50,7 @@ export const DialogTitleBar = Object.assign(
41
50
  {
42
51
  size: 'small',
43
52
  styleType: 'borderless',
44
- onClick: onClose,
53
+ onClick: onClick,
45
54
  'aria-label': 'Close',
46
55
  'data-iui-shift': 'right',
47
56
  },
@@ -12,6 +12,7 @@ import {
12
12
  useWarningLogger,
13
13
  useLayoutEffect,
14
14
  useLatestRef,
15
+ useId,
15
16
  } from '../../utils/index.js';
16
17
  import { IconButton } from '../Buttons/IconButton.js';
17
18
  import { Flex } from '../Flex/Flex.js';
@@ -156,7 +157,7 @@ let PanelTrigger = (props) => {
156
157
  panels,
157
158
  } = useSafeContext(PanelsWrapperContext);
158
159
  let { id: panelId } = useSafeContext(PanelContext);
159
- let fallbackId = React.useId();
160
+ let fallbackId = useId();
160
161
  let triggerId = children.props.id || fallbackId;
161
162
  let onClick = React.useCallback(() => {
162
163
  if (null == activePanel) return;
@@ -1,11 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import cx from 'classnames';
3
- import {
4
- WithCSSTransition,
5
- SvgChevronRight,
6
- Box,
7
- useControlledState,
8
- } from '../../utils/index.js';
3
+ import { SvgChevronRight, Box, useControlledState } from '../../utils/index.js';
9
4
  import { IconButton } from '../Buttons/IconButton.js';
10
5
  export const SidenavExpandedContext = React.createContext(void 0);
11
6
  export const SideNavigation = React.forwardRef((props, forwardedRef) => {
@@ -94,17 +89,7 @@ export const SideNavigation = React.forwardRef((props, forwardedRef) => {
94
89
  ),
95
90
  'bottom' === expanderPlacement && ExpandButton,
96
91
  ),
97
- submenu &&
98
- React.createElement(
99
- WithCSSTransition,
100
- {
101
- in: isSubmenuOpen,
102
- dimension: 'width',
103
- timeout: 200,
104
- classNames: 'iui',
105
- },
106
- submenu,
107
- ),
92
+ submenu && isSubmenuOpen ? submenu : null,
108
93
  ),
109
94
  );
110
95
  });
@@ -7,19 +7,18 @@ let onSelectHandler = (newState, instance, onSelect, isRowDisabled) => {
7
7
  let newSelectedRowIds = {};
8
8
  let handleRow = (row) => {
9
9
  if (isRowDisabled?.(row.original)) return false;
10
+ let hasSubComponents = !!row.initialSubRows[0]?.original[iuiId];
11
+ let hasSubRows = row.subRows.length > 0 && !hasSubComponents;
10
12
  let isAllSubSelected = true;
11
- if (row.initialSubRows[0]?.original[iuiId] === void 0)
13
+ if (hasSubRows)
12
14
  row.initialSubRows.forEach((subRow) => {
13
15
  let result = handleRow(subRow);
14
16
  if (!result) isAllSubSelected = false;
15
17
  });
16
- if (
17
- newState.selectedRowIds[row.id] &&
18
- (!instance.selectSubRows ||
19
- !row.initialSubRows.length ||
20
- isAllSubSelected)
21
- )
22
- newSelectedRowIds[row.id] = true;
18
+ let isRowSelected = newState.selectedRowIds[row.id];
19
+ let case1 = isRowSelected && (!instance.selectSubRows || !hasSubRows);
20
+ let case2 = hasSubRows && isAllSubSelected;
21
+ if (case1 || case2) newSelectedRowIds[row.id] = true;
23
22
  return !!newSelectedRowIds[row.id];
24
23
  };
25
24
  instance.initialRows.forEach((row) => handleRow(row));
@@ -162,7 +162,15 @@ let Tab = React.forwardRef((props, forwardedRef) => {
162
162
  });
163
163
  };
164
164
  if ('default' !== type && isActive) updateStripe();
165
- }, [type, orientation, isActive, tabsWidth, setStripeProperties, tablistRef]);
165
+ }, [
166
+ type,
167
+ orientation,
168
+ isActive,
169
+ tabsWidth,
170
+ setStripeProperties,
171
+ tablistRef,
172
+ value,
173
+ ]);
166
174
  let onKeyDown = (event) => {
167
175
  if (event.altKey) return;
168
176
  let allTabs = Array.from(event.currentTarget.parentElement?.children ?? []);
@@ -43,8 +43,7 @@ export const ThemeProvider = React.forwardRef((props, forwardedRef) => {
43
43
  (_themeOptions1.highContrast =
44
44
  'inherit' === themeProp ? parent.highContrast : void 0);
45
45
  (_future = future).themeBridge ??
46
- (_future.themeBridge =
47
- 'inherit' === themeProp ? parent.context?.future?.themeBridge : void 0);
46
+ (_future.themeBridge = parent.context?.future?.themeBridge);
48
47
  let [portalContainerFromParent] = useScopedAtom(portalContainerAtom);
49
48
  let contextValue = React.useMemo(
50
49
  () => ({
@@ -1,5 +1,4 @@
1
1
  import * as React from 'react';
2
- import { Transition } from 'react-transition-group';
3
2
  import cx from 'classnames';
4
3
  import {
5
4
  getWindow,
@@ -9,6 +8,7 @@ import {
9
8
  useSafeContext,
10
9
  ButtonBase,
11
10
  useMediaQuery,
11
+ useLatestRef,
12
12
  } from '../../utils/index.js';
13
13
  import { IconButton } from '../Buttons/IconButton.js';
14
14
  import { ToasterStateContext } from './Toaster.js';
@@ -33,7 +33,6 @@ export const Toast = (props) => {
33
33
  let [height, setHeight] = React.useState(0);
34
34
  let thisElement = React.useRef(null);
35
35
  let [margin, setMargin] = React.useState(0);
36
- let motionOk = useMediaQuery('(prefers-reduced-motion: no-preference)');
37
36
  let marginStyle = () => {
38
37
  if ('top' === placementPosition)
39
38
  return {
@@ -73,81 +72,41 @@ export const Toast = (props) => {
73
72
  setHeight(height);
74
73
  }
75
74
  };
76
- let calculateOutAnimation = (node) => {
77
- let translateX = 0;
78
- let translateY = 0;
79
- if (animateOutTo && node) {
80
- let { x: startX, y: startY } = node.getBoundingClientRect();
81
- let { x: endX, y: endY } = animateOutTo.getBoundingClientRect();
82
- translateX = endX - startX;
83
- translateY = endY - startY;
84
- }
85
- return {
86
- translateX,
87
- translateY,
88
- };
89
- };
90
- return React.createElement(
91
- Transition,
92
- {
93
- timeout: {
94
- enter: 240,
95
- exit: animateOutTo ? 400 : 120,
96
- },
97
- in: isVisible,
98
- appear: true,
99
- unmountOnExit: true,
100
- onEnter: (node) => {
101
- if (motionOk) {
102
- node.style.transform = 'translateY(15%)';
103
- node.style.transitionTimingFunction = 'ease';
104
- }
105
- },
106
- onEntered: (node) => {
107
- if (motionOk) node.style.transform = 'translateY(0)';
108
- },
109
- onExiting: (node) => {
110
- if (motionOk) {
111
- let { translateX, translateY } = calculateOutAnimation(node);
112
- node.style.transform = animateOutTo
113
- ? `scale(0.9) translate(${translateX}px,${translateY}px)`
114
- : 'scale(0.9)';
115
- node.style.opacity = '0';
116
- node.style.transitionDuration = animateOutTo ? '400ms' : '120ms';
117
- node.style.transitionTimingFunction = 'cubic-bezier(0.4, 0, 1, 1)';
118
- }
119
- },
120
- onExited: onRemove,
121
- },
122
- React.createElement(
123
- Box,
124
- {
125
- ref: thisElement,
126
- className: 'iui-toast-all',
127
- style: {
128
- height,
129
- ...marginStyle(),
130
- },
131
- },
132
- React.createElement(
133
- 'div',
75
+ let shouldBeMounted = useAnimateToastBasedOnVisibility(isVisible, {
76
+ thisElement,
77
+ animateOutTo,
78
+ onRemove,
79
+ });
80
+ return shouldBeMounted
81
+ ? React.createElement(
82
+ Box,
134
83
  {
135
- ref: onRef,
84
+ ref: thisElement,
85
+ className: 'iui-toast-all',
86
+ style: {
87
+ height,
88
+ ...marginStyle(),
89
+ },
136
90
  },
137
- React.createElement(ToastPresentation, {
138
- as: 'div',
139
- category: category,
140
- content: content,
141
- link: link,
142
- type: type,
143
- hasCloseButton: hasCloseButton,
144
- onClose: close,
145
- ...domProps?.toastProps,
146
- contentProps: domProps?.contentProps,
147
- }),
148
- ),
149
- ),
150
- );
91
+ React.createElement(
92
+ 'div',
93
+ {
94
+ ref: onRef,
95
+ },
96
+ React.createElement(ToastPresentation, {
97
+ as: 'div',
98
+ category: category,
99
+ content: content,
100
+ link: link,
101
+ type: type,
102
+ hasCloseButton: hasCloseButton,
103
+ onClose: close,
104
+ ...domProps?.toastProps,
105
+ contentProps: domProps?.contentProps,
106
+ }),
107
+ ),
108
+ )
109
+ : null;
151
110
  };
152
111
  export const ToastPresentation = React.forwardRef((props, forwardedRef) => {
153
112
  let {
@@ -212,3 +171,100 @@ export const ToastPresentation = React.forwardRef((props, forwardedRef) => {
212
171
  ),
213
172
  );
214
173
  });
174
+ let useAnimateToastBasedOnVisibility = (isVisible, args) => {
175
+ let { thisElement, animateOutTo, onRemove } = args;
176
+ let [shouldBeMounted, setShouldBeMounted] = React.useState(isVisible);
177
+ let motionOk = useMediaQuery('(prefers-reduced-motion: no-preference)');
178
+ let onRemoveRef = useLatestRef(onRemove);
179
+ let [prevIsVisible, setPrevIsVisible] = React.useState(void 0);
180
+ React.useEffect(() => {
181
+ if (prevIsVisible !== isVisible) {
182
+ setPrevIsVisible(isVisible);
183
+ if (isVisible) safeAnimateIn();
184
+ else safeAnimateOut();
185
+ }
186
+ function calculateOutAnimation(node) {
187
+ let translateX = 0;
188
+ let translateY = 0;
189
+ if (animateOutTo && node) {
190
+ let { x: startX, y: startY } = node.getBoundingClientRect();
191
+ let { x: endX, y: endY } = animateOutTo.getBoundingClientRect();
192
+ translateX = endX - startX;
193
+ translateY = endY - startY;
194
+ }
195
+ return {
196
+ translateX,
197
+ translateY,
198
+ };
199
+ }
200
+ function safeAnimateIn() {
201
+ setShouldBeMounted(true);
202
+ queueMicrotask(() => {
203
+ animateIn();
204
+ });
205
+ }
206
+ function safeAnimateOut() {
207
+ if (motionOk) {
208
+ let animation = animateOut();
209
+ animation?.addEventListener('finish', () => {
210
+ setShouldBeMounted(false);
211
+ onRemoveRef.current?.();
212
+ });
213
+ } else {
214
+ setShouldBeMounted(false);
215
+ onRemoveRef.current?.();
216
+ }
217
+ }
218
+ function animateIn() {
219
+ if (!motionOk) return;
220
+ thisElement.current?.animate?.(
221
+ [
222
+ {
223
+ transform: 'translateY(15%)',
224
+ },
225
+ {
226
+ transform: 'translateY(0)',
227
+ },
228
+ ],
229
+ {
230
+ duration: 240,
231
+ fill: 'forwards',
232
+ },
233
+ );
234
+ }
235
+ function animateOut() {
236
+ if (null == thisElement.current || !motionOk) return;
237
+ let { translateX, translateY } = calculateOutAnimation(
238
+ thisElement.current,
239
+ );
240
+ let animationDuration = animateOutTo ? 400 : 120;
241
+ let animation = thisElement.current?.animate?.(
242
+ [
243
+ {
244
+ transform: animateOutTo
245
+ ? `scale(0.9) translate(${translateX}px,${translateY}px)`
246
+ : 'scale(0.9)',
247
+ opacity: 0,
248
+ transitionDuration: `${animationDuration}ms`,
249
+ transitionTimingFunction: 'cubic-bezier(0.4, 0, 1, 1)',
250
+ },
251
+ ],
252
+ {
253
+ duration: animationDuration,
254
+ iterations: 1,
255
+ fill: 'forwards',
256
+ },
257
+ );
258
+ return animation;
259
+ }
260
+ }, [
261
+ isVisible,
262
+ prevIsVisible,
263
+ animateOutTo,
264
+ motionOk,
265
+ thisElement,
266
+ setShouldBeMounted,
267
+ onRemoveRef,
268
+ ]);
269
+ return shouldBeMounted;
270
+ };