@lumx/react 3.0.4 → 3.0.5-alpha.1

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.
package/package.json CHANGED
@@ -7,8 +7,8 @@
7
7
  },
8
8
  "dependencies": {
9
9
  "@juggle/resize-observer": "^3.2.0",
10
- "@lumx/core": "^3.0.4",
11
- "@lumx/icons": "^3.0.4",
10
+ "@lumx/core": "^3.0.5-alpha.1",
11
+ "@lumx/icons": "^3.0.5-alpha.1",
12
12
  "@popperjs/core": "^2.5.4",
13
13
  "body-scroll-lock": "^3.1.5",
14
14
  "classnames": "^2.2.6",
@@ -114,6 +114,6 @@
114
114
  "build:storybook": "cd storybook && ./build"
115
115
  },
116
116
  "sideEffects": false,
117
- "version": "3.0.4",
118
- "gitHead": "a2e7c8a0dab1f63d23d14a32255525cd8bef1be5"
117
+ "version": "3.0.5-alpha.1",
118
+ "gitHead": "e05f8419842b4c8ebdb9b21026e17d18ec6ebbbb"
119
119
  }
@@ -5,7 +5,7 @@ import classNames from 'classnames';
5
5
 
6
6
  import { Progress, ProgressVariant, Size } from '@lumx/react';
7
7
 
8
- import { DIALOG_TRANSITION_DURATION, DOCUMENT } from '@lumx/react/constants';
8
+ import { DOCUMENT } from '@lumx/react/constants';
9
9
  import { useCallbackOnEscape } from '@lumx/react/hooks/useCallbackOnEscape';
10
10
  import { useFocusTrap } from '@lumx/react/hooks/useFocusTrap';
11
11
  import { useIntersectionObserver } from '@lumx/react/hooks/useIntersectionObserver';
@@ -16,8 +16,8 @@ import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/classNam
16
16
  import { ClickAwayProvider } from '@lumx/react/utils/ClickAwayProvider';
17
17
  import { mergeRefs } from '@lumx/react/utils/mergeRefs';
18
18
 
19
- import { useDelayedVisibility } from '@lumx/react/hooks/useDelayedVisibility';
20
19
  import { useDisableBodyScroll } from '@lumx/react/hooks/useDisableBodyScroll';
20
+ import { useTransitionVisibility } from '@lumx/react/hooks/useTransitionVisibility';
21
21
 
22
22
  /**
23
23
  * Defines the props of the component.
@@ -166,15 +166,15 @@ export const Dialog: Comp<DialogProps, HTMLDivElement> = forwardRef((props, ref)
166
166
  const footerChildProps = (footerChild as ReactElement)?.props;
167
167
  const footerChildContent = footerChildProps?.children;
168
168
 
169
- // eslint-disable-next-line react-hooks/rules-of-hooks
170
- const isVisible = useDelayedVisibility(Boolean(isOpen), DIALOG_TRANSITION_DURATION, onVisibilityChange);
171
-
172
169
  // eslint-disable-next-line react-hooks/rules-of-hooks
173
170
  const clickAwayRefs = useRef([wrapperRef]);
174
171
 
175
172
  // eslint-disable-next-line react-hooks/rules-of-hooks
176
173
  const rootRef = useRef<HTMLDivElement>(null);
177
174
 
175
+ // eslint-disable-next-line react-hooks/rules-of-hooks
176
+ const isVisible = useTransitionVisibility(rootRef, Boolean(isOpen), onVisibilityChange);
177
+
178
178
  return isOpen || isVisible
179
179
  ? createPortal(
180
180
  <div
@@ -10,13 +10,11 @@ import { Comp, GenericProps, HasTheme } from '@lumx/react/utils/type';
10
10
  import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
11
11
 
12
12
  import { useFocusTrap } from '@lumx/react/hooks/useFocusTrap';
13
- import { useDelayedVisibility } from '@lumx/react/hooks/useDelayedVisibility';
14
13
  import { useDisableBodyScroll } from '@lumx/react/hooks/useDisableBodyScroll';
15
14
  import { ClickAwayProvider } from '@lumx/react/utils/ClickAwayProvider';
16
15
  import { mergeRefs } from '@lumx/react/utils/mergeRefs';
17
16
  import { useCallbackOnEscape } from '@lumx/react/hooks/useCallbackOnEscape';
18
-
19
- const LIGHTBOX_TRANSITION_DURATION = 400;
17
+ import { useTransitionVisibility } from '@lumx/react/hooks/useTransitionVisibility';
20
18
 
21
19
  /**
22
20
  * Defines the props of the component.
@@ -82,7 +80,7 @@ export const Lightbox: Comp<LightboxProps, HTMLDivElement> = forwardRef((props,
82
80
  useDisableBodyScroll(isOpen && wrapperRef.current);
83
81
 
84
82
  // eslint-disable-next-line react-hooks/rules-of-hooks
85
- const isVisible = useDelayedVisibility(!!isOpen, LIGHTBOX_TRANSITION_DURATION);
83
+ const isVisible = useTransitionVisibility(wrapperRef, !!isOpen);
86
84
 
87
85
  // Handle focus trap.
88
86
  // eslint-disable-next-line react-hooks/rules-of-hooks
@@ -1,4 +1,4 @@
1
- import React, { forwardRef } from 'react';
1
+ import React, { forwardRef, useRef } from 'react';
2
2
  import { createPortal } from 'react-dom';
3
3
 
4
4
  import classNames from 'classnames';
@@ -7,12 +7,13 @@ import isFunction from 'lodash/isFunction';
7
7
 
8
8
  import { Button, Emphasis, Icon, Kind, Size, Theme } from '@lumx/react';
9
9
 
10
- import { DOCUMENT, NOTIFICATION_TRANSITION_DURATION } from '@lumx/react/constants';
10
+ import { DOCUMENT } from '@lumx/react/constants';
11
11
  import { NOTIFICATION_CONFIGURATION } from '@lumx/react/components/notification/constants';
12
12
  import { Comp, GenericProps, HasTheme } from '@lumx/react/utils/type';
13
13
  import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
14
14
 
15
- import { useDelayedVisibility } from '@lumx/react/hooks/useDelayedVisibility';
15
+ import { useTransitionVisibility } from '@lumx/react/hooks/useTransitionVisibility';
16
+ import { mergeRefs } from '@lumx/react/utils/mergeRefs';
16
17
 
17
18
  /**
18
19
  * Defines the props of the component.
@@ -78,7 +79,8 @@ export const Notification: Comp<NotificationProps, HTMLDivElement> = forwardRef(
78
79
  return null;
79
80
  }
80
81
  const { color, icon } = NOTIFICATION_CONFIGURATION[type as Kind] || {};
81
- const isVisible = useDelayedVisibility(!!isOpen, NOTIFICATION_TRANSITION_DURATION);
82
+ const rootRef = useRef<HTMLDivElement>(null);
83
+ const isVisible = useTransitionVisibility(rootRef, !!isOpen);
82
84
  const hasAction: boolean = Boolean(onActionClick) && Boolean(actionLabel);
83
85
 
84
86
  const handleCallback = (evt: React.MouseEvent) => {
@@ -92,7 +94,7 @@ export const Notification: Comp<NotificationProps, HTMLDivElement> = forwardRef(
92
94
  ? createPortal(
93
95
  // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
94
96
  <div
95
- ref={ref}
97
+ ref={mergeRefs(ref, rootRef)}
96
98
  role="alert"
97
99
  {...forwardedProps}
98
100
  className={classNames(
@@ -0,0 +1,53 @@
1
+ import { RefObject, useEffect, useRef, useState } from 'react';
2
+
3
+ /**
4
+ * Returns true if the component is visible tracking the opacity transition.
5
+ *
6
+ * @param ref Element on which to listen the transition event.
7
+ * @param isComponentVisible Whether the component intends to be visible or not.
8
+ * @param onVisibilityChange Callback called when the visibility changes.
9
+ * @return true if the component should be rendered
10
+ */
11
+ export const useTransitionVisibility = (
12
+ ref: RefObject<HTMLElement>,
13
+ isComponentVisible: boolean,
14
+ onVisibilityChange?: (isVisible: boolean) => void,
15
+ ) => {
16
+ const [isVisible, setVisible] = useState(isComponentVisible);
17
+ const previousVisibility = useRef(isVisible);
18
+
19
+ useEffect(() => {
20
+ if (isComponentVisible) {
21
+ setVisible(true);
22
+ } else if (!window.TransitionEvent) {
23
+ // Transition event is not available so visibility is set to false directly.
24
+ setVisible(false);
25
+ }
26
+ }, [isComponentVisible]);
27
+
28
+ useEffect(() => {
29
+ if (onVisibilityChange && previousVisibility.current !== isVisible) {
30
+ onVisibilityChange(isVisible);
31
+ previousVisibility.current = isVisible;
32
+ }
33
+ }, [isVisible, onVisibilityChange]);
34
+
35
+ useEffect(() => {
36
+ const { current: element } = ref;
37
+ if (!element) {
38
+ return undefined;
39
+ }
40
+
41
+ // Listen opacity transition
42
+ const onTransitionEnd = (e: TransitionEvent) => {
43
+ if (e.target !== ref.current || e.propertyName !== 'opacity') return;
44
+ setVisible((wasVisible) => !wasVisible);
45
+ };
46
+ element.addEventListener('transitionend', onTransitionEnd);
47
+ return () => {
48
+ element.removeEventListener('transitionend', onTransitionEnd);
49
+ };
50
+ });
51
+
52
+ return isVisible || isComponentVisible;
53
+ };
@@ -1,44 +0,0 @@
1
- import { useEffect, useState, useRef } from 'react';
2
-
3
- /**
4
- * Returns true if the component is visible taking into account the component's
5
- * own visibility and the animations delay
6
- *
7
- * @param isComponentVisible Whether the component intends to be visible or not.
8
- * @param transitionDuration time in ms that the transition takes for the specific component.
9
- * @param onVisibilityChange Callback called when the visibility changes.
10
- * @return true if the component should be rendered
11
- */
12
- export function useDelayedVisibility(
13
- isComponentVisible: boolean,
14
- transitionDuration: number,
15
- onVisibilityChange?: (isVisible: boolean) => void,
16
- ): boolean {
17
- // Delay visibility to account for the 400ms of CSS opacity animation.
18
- const [isVisible, setVisible] = useState(isComponentVisible);
19
-
20
- useEffect(() => {
21
- if (isComponentVisible) {
22
- setVisible(true);
23
- } else {
24
- setTimeout(() => setVisible(false), transitionDuration);
25
- }
26
- }, [isComponentVisible, transitionDuration]);
27
-
28
- /**
29
- * Since we don't want onVisibiltyChange function to trigger itself if when it changes,
30
- * we store the previous visibility and only trigger when visibility is different
31
- * than previous value.
32
- */
33
-
34
- const previousVisibility = useRef(isVisible);
35
-
36
- useEffect(() => {
37
- if (onVisibilityChange && previousVisibility.current !== isVisible) {
38
- onVisibilityChange(isVisible);
39
- previousVisibility.current = isVisible;
40
- }
41
- }, [isVisible, onVisibilityChange]);
42
-
43
- return isComponentVisible || isVisible;
44
- }