@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/index.js +53 -50
- package/index.js.map +1 -1
- package/package.json +4 -4
- package/src/components/dialog/Dialog.tsx +5 -5
- package/src/components/lightbox/Lightbox.tsx +2 -4
- package/src/components/notification/Notification.tsx +7 -5
- package/src/hooks/useTransitionVisibility.ts +53 -0
- package/src/hooks/useDelayedVisibility.tsx +0 -44
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.
|
|
11
|
-
"@lumx/icons": "^3.0.
|
|
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.
|
|
118
|
-
"gitHead": "
|
|
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 {
|
|
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 =
|
|
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
|
|
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 {
|
|
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
|
|
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
|
-
}
|