reshaped 3.1.1 → 3.1.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.
- package/CHANGELOG.md +2 -0
- package/dist/bundle.css +1 -1
- package/dist/bundle.js +11 -11
- package/dist/components/Modal/Modal.js +2 -2
- package/dist/components/Modal/Modal.types.d.ts +2 -0
- package/dist/components/Modal/tests/Modal.stories.d.ts +1 -0
- package/dist/components/Modal/tests/Modal.stories.js +11 -1
- package/dist/components/Overlay/Overlay.js +3 -3
- package/dist/components/Overlay/Overlay.module.css +1 -1
- package/dist/components/Overlay/Overlay.types.d.ts +2 -0
- package/dist/components/Overlay/tests/Overlay.stories.js +11 -3
- package/dist/components/_private/Flyout/Flyout.constants.d.ts +1 -1
- package/dist/components/_private/Flyout/Flyout.constants.js +1 -1
- package/dist/components/_private/Flyout/Flyout.types.d.ts +2 -0
- package/dist/components/_private/Flyout/FlyoutControlled.js +29 -5
- package/dist/components/_private/Flyout/FlyoutTrigger.js +2 -1
- package/package.json +1 -1
@@ -40,7 +40,7 @@ const ModalSubtitle = (props) => {
|
|
40
40
|
return (_jsx(Text, { variant: "body-3", color: "neutral-faded", attributes: { id: `${id}-subtitle` }, children: children }));
|
41
41
|
};
|
42
42
|
const Modal = (props) => {
|
43
|
-
const { children, onClose, onOpen, active, size, padding = 4, position = "center", transparentOverlay, ariaLabel, autoFocus = true, disableSwipeGesture, overlayClassName, className, attributes, } = props;
|
43
|
+
const { children, onClose, onOpen, active, size, padding = 4, position = "center", transparentOverlay, blurredOverlay, ariaLabel, autoFocus = true, disableSwipeGesture, disableCloseOnOutsideClick, overlayClassName, className, attributes, } = props;
|
44
44
|
const onCloseRef = useHandlerRef(onClose);
|
45
45
|
const id = useElementId();
|
46
46
|
const clientPosition = useResponsiveClientValue(position);
|
@@ -165,7 +165,7 @@ const Modal = (props) => {
|
|
165
165
|
setHideProgress(progress / 2);
|
166
166
|
dragDistanceRef.current = dragDistance;
|
167
167
|
}, [dragDistance, clientPosition, rootRef]);
|
168
|
-
return (_jsx(Overlay, { onClose: onClose, onOpen: onOpen, active: active, transparent: transparentOverlay || hideProgress, className: overlayClassName, attributes: {
|
168
|
+
return (_jsx(Overlay, { onClose: onClose, onOpen: onOpen, disableCloseOnClick: disableCloseOnOutsideClick, active: active, transparent: transparentOverlay || hideProgress, blurred: blurredOverlay, className: overlayClassName, attributes: {
|
169
169
|
onTouchStart: handleDragStart,
|
170
170
|
}, children: ({ active }) => {
|
171
171
|
const rootClassNames = classNames(s.root, className, paddingStyles?.classNames, active && s["--active"], dragging && s["--dragging"], responsiveClassNames(s, "--position", position));
|
@@ -20,7 +20,9 @@ export type Props = {
|
|
20
20
|
size?: G.Responsive<string>;
|
21
21
|
padding?: G.Responsive<number>;
|
22
22
|
transparentOverlay?: boolean;
|
23
|
+
blurredOverlay?: boolean;
|
23
24
|
disableSwipeGesture?: boolean;
|
25
|
+
disableCloseOnOutsideClick?: boolean;
|
24
26
|
autoFocus?: boolean;
|
25
27
|
ariaLabel?: string;
|
26
28
|
className?: G.ClassName;
|
@@ -19,5 +19,6 @@ export declare const size: () => React.JSX.Element;
|
|
19
19
|
export declare const padding: () => React.JSX.Element;
|
20
20
|
export declare const composition: () => React.JSX.Element;
|
21
21
|
export declare const overlay: () => React.JSX.Element;
|
22
|
+
export declare const flags: () => React.JSX.Element;
|
22
23
|
export declare const edgeCases: () => React.JSX.Element;
|
23
24
|
export declare const trapFocusEdgeCases: () => React.JSX.Element;
|
@@ -97,9 +97,19 @@ export const composition = () => (<Example>
|
|
97
97
|
export const overlay = () => (<Example>
|
98
98
|
<Example.Item title="transparentOverlay, doesn't lock scroll">
|
99
99
|
<Demo transparentOverlay/>
|
100
|
-
<View height="1000px"/>
|
101
100
|
</Example.Item>
|
101
|
+
<Example.Item title="blurredOverlay">
|
102
|
+
<Demo blurredOverlay/>
|
103
|
+
</Example.Item>
|
104
|
+
<View height="1000px"/>
|
102
105
|
</Example>);
|
106
|
+
export const flags = () => {
|
107
|
+
return (<Example>
|
108
|
+
<Example.Item title="disableCloseOnOutsideClick">
|
109
|
+
<Demo disableCloseOnOutsideClick/>
|
110
|
+
</Example.Item>
|
111
|
+
</Example>);
|
112
|
+
};
|
103
113
|
export const edgeCases = () => {
|
104
114
|
const menuModalToggle = useToggle();
|
105
115
|
const scrollModalToggle = useToggle();
|
@@ -13,7 +13,7 @@ import Portal from "../_private/Portal/index.js";
|
|
13
13
|
import s from "./Overlay.module.css";
|
14
14
|
import useHandlerRef from "../../hooks/useHandlerRef.js";
|
15
15
|
const Overlay = (props) => {
|
16
|
-
const { active, children, transparent, onClose, onOpen, className, attributes } = props;
|
16
|
+
const { active, children, transparent, blurred, onClose, onOpen, disableCloseOnClick, className, attributes, } = props;
|
17
17
|
const onCloseRef = useHandlerRef(onClose);
|
18
18
|
const onOpenRef = useHandlerRef(onOpen);
|
19
19
|
const clickThrough = transparent === true;
|
@@ -26,7 +26,7 @@ const Overlay = (props) => {
|
|
26
26
|
const { active: rendered, activate: render, deactivate: remove } = useToggle(active || false);
|
27
27
|
const { active: visible, activate: show, deactivate: hide } = useToggle(active || false);
|
28
28
|
const isDismissible = useIsDismissible(active, contentRef);
|
29
|
-
const rootClassNames = classNames(s.root, visible && s["--visible"], clickThrough && s["--click-through"], animated && s["--animated"], className);
|
29
|
+
const rootClassNames = classNames(s.root, visible && s["--visible"], clickThrough && s["--click-through"], blurred && s["--blurred"], animated && s["--animated"], className);
|
30
30
|
const isInsideChild = (el) => {
|
31
31
|
if (!contentRef.current)
|
32
32
|
return;
|
@@ -46,7 +46,7 @@ const Overlay = (props) => {
|
|
46
46
|
const handleMouseUp = (event) => {
|
47
47
|
const isMouseUpValid = !isInsideChild(event.target);
|
48
48
|
const shouldClose = isMouseDownValidRef.current && isMouseUpValid && !clickThrough;
|
49
|
-
if (!shouldClose)
|
49
|
+
if (!shouldClose || disableCloseOnClick)
|
50
50
|
return;
|
51
51
|
close();
|
52
52
|
};
|
@@ -1 +1 @@
|
|
1
|
-
.root{overflow:auto;-webkit-overflow-scrolling:touch;background-color:rgba(var(--rs-color-rgb-black),0);color:var(--rs-color-white);cursor:default!important;inset
|
1
|
+
.root{overflow:auto;-webkit-overflow-scrolling:touch;background-color:rgba(var(--rs-color-rgb-black),0);color:var(--rs-color-white);cursor:default!important;inset:-1px;opacity:0;outline:none;position:fixed;z-index:var(--rs-z-index-overlay)}.wrapper{display:table;height:100%;width:100%}.inner{display:table-cell;text-align:center}.content,.inner{vertical-align:middle}.content{display:inline-block;text-align:initial}.root.--visible{background-color:rgba(var(--rs-color-rgb-black),var(--rs-overlay-opacity));opacity:1}.root.--click-through{color:inherit;pointer-events:none}.root.--blurred{backdrop-filter:blur(3px)}.root.--click-through .content,.root.--click-through>:not(.wrapper){pointer-events:all}.root.--animated{transition:var(--rs-duration-medium) var(--rs-easing-accelerate);transition-property:background-color,transform,opacity}.root.--animated.--visible{transition-timing-function:var(--rs-easing-decelerate)}
|
@@ -2,12 +2,14 @@ import type React from "react";
|
|
2
2
|
import type * as G from "../../types/global";
|
3
3
|
export type Props = {
|
4
4
|
transparent?: boolean | number;
|
5
|
+
blurred?: boolean;
|
5
6
|
children?: React.ReactNode | ((props: {
|
6
7
|
active: boolean;
|
7
8
|
}) => React.ReactNode);
|
8
9
|
active?: boolean;
|
9
10
|
onClose?: () => void;
|
10
11
|
onOpen?: () => void;
|
12
|
+
disableCloseOnClick?: boolean;
|
11
13
|
className?: G.ClassName;
|
12
14
|
attributes?: G.Attributes<"div">;
|
13
15
|
};
|
@@ -14,15 +14,15 @@ export default {
|
|
14
14
|
},
|
15
15
|
};
|
16
16
|
export const base = () => {
|
17
|
-
const baseToggle = useToggle(
|
17
|
+
const baseToggle = useToggle(false);
|
18
18
|
const transparentToggle = useToggle(false);
|
19
|
+
const blurredToggle = useToggle(false);
|
19
20
|
return (<Example>
|
20
21
|
<Example.Item title="locks scroll">
|
21
22
|
<Button onClick={() => baseToggle.activate()}>Open overlay</Button>
|
22
23
|
<Overlay active={baseToggle.active} onClose={() => baseToggle.deactivate()}>
|
23
24
|
Overlay content
|
24
25
|
</Overlay>
|
25
|
-
<div style={{ height: 1000 }}/>
|
26
26
|
</Example.Item>
|
27
27
|
|
28
28
|
<Example.Item title="transparent, doesn't lock scroll">
|
@@ -30,8 +30,16 @@ export const base = () => {
|
|
30
30
|
<Overlay active={transparentToggle.active} onClose={() => transparentToggle.deactivate()} transparent>
|
31
31
|
Overlay content
|
32
32
|
</Overlay>
|
33
|
-
<div style={{ height: 1000 }}/>
|
34
33
|
</Example.Item>
|
34
|
+
|
35
|
+
<Example.Item title="blurred">
|
36
|
+
<Button onClick={() => blurredToggle.activate()}>Open overlay</Button>
|
37
|
+
<Overlay active={blurredToggle.active} onClose={() => blurredToggle.deactivate()} blurred>
|
38
|
+
Overlay content
|
39
|
+
</Overlay>
|
40
|
+
</Example.Item>
|
41
|
+
|
42
|
+
<div style={{ height: 1000 }}/>
|
35
43
|
</Example>);
|
36
44
|
};
|
37
45
|
class CustomElement extends window.HTMLElement {
|
@@ -52,6 +52,7 @@ export type TriggerAttributes = {
|
|
52
52
|
onFocus?: () => void;
|
53
53
|
onMouseEnter?: () => void;
|
54
54
|
onMouseLeave?: () => void;
|
55
|
+
onTouchStart?: () => void;
|
55
56
|
onClick?: () => void;
|
56
57
|
"aria-describedby"?: string;
|
57
58
|
"aria-haspopup"?: "dialog" | "menu" | "listbox";
|
@@ -111,6 +112,7 @@ export type ContextProps = {
|
|
111
112
|
handleClick: () => void;
|
112
113
|
handleBlur: (e: React.FocusEvent) => void;
|
113
114
|
handleFocus: () => void;
|
115
|
+
handleTouchStart: () => void;
|
114
116
|
handleContentMouseDown: () => void;
|
115
117
|
handleContentMouseUp: () => void;
|
116
118
|
isSubmenu: boolean;
|
@@ -38,9 +38,16 @@ const FlyoutRoot = (props) => {
|
|
38
38
|
const timerRef = React.useRef();
|
39
39
|
const trapFocusRef = React.useRef(null);
|
40
40
|
const lockedRef = React.useRef(false);
|
41
|
+
// Check if transition had enough time to start when opening a flyout
|
42
|
+
// In some cases there is not enough time to start, like when you're holding tab key
|
41
43
|
const transitionStartedRef = React.useRef(false);
|
44
|
+
// Lock blur event while pressing anywhere inside the flyout content
|
42
45
|
const lockedBlurEffects = React.useRef(false);
|
46
|
+
// Focus shouldn't retrun back to the trigger when user intentionally clicks outside the flyout
|
43
47
|
const shouldReturnFocusRef = React.useRef(true);
|
48
|
+
// Touch devices trigger onMouseEnter but we don't need to apply regular hover timeouts
|
49
|
+
// So we're saving a flag on touch start and then change the mouse enter behavior
|
50
|
+
const hoverTriggeredWithTouchEventRef = React.useRef(false);
|
44
51
|
const flyout = useFlyout(triggerElRef, flyoutElRef, {
|
45
52
|
width,
|
46
53
|
position: passedPosition,
|
@@ -92,6 +99,11 @@ const FlyoutRoot = (props) => {
|
|
92
99
|
}
|
93
100
|
handleClose();
|
94
101
|
}, [handleClose, triggerType, trapFocusMode]);
|
102
|
+
const handleTouchStart = React.useCallback(() => {
|
103
|
+
if (triggerType !== "hover")
|
104
|
+
return;
|
105
|
+
hoverTriggeredWithTouchEventRef.current = true;
|
106
|
+
}, [triggerType]);
|
95
107
|
const handleFocus = React.useCallback(() => {
|
96
108
|
if (triggerType === "hover" && !checkKeyboardMode())
|
97
109
|
return;
|
@@ -99,9 +111,15 @@ const FlyoutRoot = (props) => {
|
|
99
111
|
}, [handleOpen, triggerType]);
|
100
112
|
const handleMouseEnter = React.useCallback(() => {
|
101
113
|
clearTimer();
|
102
|
-
|
103
|
-
|
104
|
-
|
114
|
+
if (hoverTriggeredWithTouchEventRef.current) {
|
115
|
+
handleOpen();
|
116
|
+
hoverTriggeredWithTouchEventRef.current = false;
|
117
|
+
}
|
118
|
+
else {
|
119
|
+
timerRef.current = setTimeout(handleOpen, cooldown.timer || isSubmenu ? timeouts.mouseEnterShort : timeouts.mouseEnter);
|
120
|
+
if (!isSubmenu && triggerType === "hover")
|
121
|
+
cooldown.warm();
|
122
|
+
}
|
105
123
|
}, [clearTimer, timerRef, handleOpen, isSubmenu, triggerType]);
|
106
124
|
const handleMouseLeave = React.useCallback(() => {
|
107
125
|
cooldown.cool();
|
@@ -116,8 +134,13 @@ const FlyoutRoot = (props) => {
|
|
116
134
|
handleClose();
|
117
135
|
}
|
118
136
|
}, [status, handleOpen, handleClose]);
|
119
|
-
const handleContentMouseDown = () =>
|
120
|
-
|
137
|
+
const handleContentMouseDown = () => {
|
138
|
+
lockedBlurEffects.current = true;
|
139
|
+
hoverTriggeredWithTouchEventRef.current = true;
|
140
|
+
};
|
141
|
+
const handleContentMouseUp = () => {
|
142
|
+
lockedBlurEffects.current = false;
|
143
|
+
};
|
121
144
|
const handleTransitionStart = React.useCallback((e) => {
|
122
145
|
if (!resolvedActive)
|
123
146
|
return;
|
@@ -251,6 +274,7 @@ const FlyoutRoot = (props) => {
|
|
251
274
|
handleBlur,
|
252
275
|
handleMouseEnter,
|
253
276
|
handleMouseLeave,
|
277
|
+
handleTouchStart,
|
254
278
|
handleTransitionStart,
|
255
279
|
handleTransitionEnd,
|
256
280
|
handleClick: handleTriggerClick,
|
@@ -3,7 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { useFlyoutContext, TriggerProvider } from "./Flyout.context.js";
|
4
4
|
const FlyoutTrigger = (props) => {
|
5
5
|
const { children } = props;
|
6
|
-
const { id, triggerElRef, triggerType, flyout, handleFocus, handleBlur, handleMouseEnter, handleMouseLeave, handleClick, trapFocusMode, isSubmenu, } = useFlyoutContext();
|
6
|
+
const { id, triggerElRef, triggerType, flyout, handleFocus, handleBlur, handleMouseEnter, handleMouseLeave, handleTouchStart, handleClick, trapFocusMode, isSubmenu, } = useFlyoutContext();
|
7
7
|
let childrenAttributes = {
|
8
8
|
ref: triggerElRef,
|
9
9
|
};
|
@@ -13,6 +13,7 @@ const FlyoutTrigger = (props) => {
|
|
13
13
|
if (triggerType === "hover") {
|
14
14
|
childrenAttributes.onMouseEnter = handleMouseEnter;
|
15
15
|
childrenAttributes.onMouseLeave = handleMouseLeave;
|
16
|
+
childrenAttributes.onTouchStart = handleTouchStart;
|
16
17
|
}
|
17
18
|
// Submenus open on keypress instead of hover
|
18
19
|
if ((triggerType === "hover" && !isSubmenu) || triggerType === "focus") {
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "reshaped",
|
3
3
|
"description": "Professionally crafted design system in React & Figma for building products of any scale and complexity",
|
4
|
-
"version": "3.1.
|
4
|
+
"version": "3.1.2",
|
5
5
|
"license": "MIT",
|
6
6
|
"email": "hello@reshaped.so",
|
7
7
|
"homepage": "https://reshaped.so",
|