reshaped 4.2.0 → 4.3.0-canary.0
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/dist/bundle.css +1 -1
- package/dist/bundle.js +2 -2
- package/dist/components/Autocomplete/Autocomplete.js +1 -1
- package/dist/components/ContextMenu/tests/ContextMenu.stories.d.ts +7 -0
- package/dist/components/ContextMenu/tests/ContextMenu.stories.js +99 -1
- package/dist/components/DropdownMenu/DropdownMenu.js +1 -1
- package/dist/components/Flyout/Flyout.module.css +1 -1
- package/dist/components/Flyout/Flyout.types.d.ts +1 -0
- package/dist/components/Flyout/FlyoutContent.js +3 -2
- package/dist/components/Flyout/FlyoutControlled.js +23 -4
- package/dist/components/Flyout/tests/Flyout.stories.d.ts +7 -0
- package/dist/components/Flyout/tests/Flyout.stories.js +142 -1
- package/dist/components/Modal/Modal.js +13 -171
- package/dist/components/Modal/Modal.module.css +1 -1
- package/dist/components/Modal/Modal.types.d.ts +2 -1
- package/dist/components/Modal/useModalDrag.d.ts +21 -0
- package/dist/components/Modal/useModalDrag.js +256 -0
- package/dist/components/Overlay/Overlay.js +5 -4
- package/dist/components/Overlay/Overlay.module.css +1 -1
- package/dist/components/ScrollArea/ScrollArea.js +29 -16
- package/dist/components/ScrollArea/ScrollArea.module.css +1 -1
- package/dist/components/ScrollArea/ScrollArea.types.d.ts +1 -1
- package/dist/components/Select/SelectCustomControlled.js +1 -1
- package/dist/components/Select/tests/Select.stories.js +12 -1
- package/dist/components/_private/Freeze/Freeze.d.ts +4 -0
- package/dist/components/_private/Freeze/Freeze.js +46 -0
- package/dist/components/_private/Freeze/Freeze.module.css +1 -0
- package/dist/components/_private/Freeze/Freeze.types.d.ts +14 -0
- package/dist/components/_private/Freeze/Freeze.types.js +1 -0
- package/dist/components/_private/Freeze/index.d.ts +1 -0
- package/dist/components/_private/Freeze/index.js +1 -0
- package/dist/components/_private/Freeze/tests/Freeze.stories.d.ts +7 -0
- package/dist/components/_private/Freeze/tests/Freeze.stories.js +88 -0
- package/dist/hooks/_internal/useSingletonHotkeys.js +4 -0
- package/dist/hooks/tests/useHotkeys.stories.d.ts +3 -0
- package/dist/hooks/tests/useHotkeys.stories.js +18 -1
- package/dist/hooks/tests/useOnClickOutside.stories.d.ts +6 -0
- package/dist/hooks/tests/useOnClickOutside.stories.js +39 -1
- package/dist/hooks/useOnClickOutside.js +12 -12
- package/dist/utilities/animation.d.ts +17 -0
- package/dist/utilities/animation.js +73 -0
- package/package.json +3 -3
|
@@ -128,7 +128,7 @@ const Autocomplete = (props) => {
|
|
|
128
128
|
highlightedId,
|
|
129
129
|
setHighlightedId,
|
|
130
130
|
}), [highlightedId, handleItemClick]);
|
|
131
|
-
return (_jsx(AutocompleteContext.Provider, { value: contextValue, children: _jsxs(DropdownMenu, { position: "bottom", width: "trigger", triggerType: "focus", trapFocusMode: false, active: isDropdownActive, onClose: handleClose, onOpen: handleOpen, onAfterOpen: onAfterOpen, onAfterClose: onAfterClose, containerRef: containerRef, fallbackAdjustLayout: fallbackAdjustLayout, fallbackMinHeight: fallbackMinHeight, contentMaxHeight: contentMaxHeight, contentZIndex: contentZIndex,
|
|
131
|
+
return (_jsx(AutocompleteContext.Provider, { value: contextValue, children: _jsxs(DropdownMenu, { position: "bottom", width: "trigger", triggerType: "focus", trapFocusMode: false, active: isDropdownActive, onClose: handleClose, onOpen: handleOpen, onAfterOpen: onAfterOpen, onAfterClose: onAfterClose, containerRef: containerRef, fallbackAdjustLayout: fallbackAdjustLayout, fallbackMinHeight: fallbackMinHeight, contentMaxHeight: contentMaxHeight, contentZIndex: contentZIndex, instanceRef: instanceRef, children: [_jsx(DropdownMenu.Trigger, { children: ({ ref, ...attributes }) => (_jsx(TextField, { ...textFieldProps, name: name, onChange: handleChange, focused: isDropdownActive, attributes: {
|
|
132
132
|
...textFieldProps.attributes,
|
|
133
133
|
// Ignoring the type check since TS can't infer the correct html element type
|
|
134
134
|
ref: ref,
|
|
@@ -28,4 +28,11 @@ export declare const handlers: StoryObj<{
|
|
|
28
28
|
export declare const triggerRef: StoryObj<{
|
|
29
29
|
handleOpen: ReturnType<typeof fn>;
|
|
30
30
|
}>;
|
|
31
|
+
export declare const sameMenuRightClick: StoryObj<{
|
|
32
|
+
handleOpen: ReturnType<typeof fn>;
|
|
33
|
+
handleClose: ReturnType<typeof fn>;
|
|
34
|
+
}>;
|
|
35
|
+
export declare const insideFlyoutRightClick: StoryObj<{
|
|
36
|
+
handlePopoverClose: ReturnType<typeof fn>;
|
|
37
|
+
}>;
|
|
31
38
|
export declare const menu: StoryObj;
|
|
@@ -3,10 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.menu = exports.triggerRef = exports.handlers = exports.base = void 0;
|
|
6
|
+
exports.menu = exports.insideFlyoutRightClick = exports.sameMenuRightClick = exports.triggerRef = exports.handlers = exports.base = void 0;
|
|
7
7
|
const react_1 = __importDefault(require("react"));
|
|
8
8
|
const test_1 = require("storybook/test");
|
|
9
|
+
const Button_1 = __importDefault(require("../../../components/Button"));
|
|
9
10
|
const ContextMenu_1 = __importDefault(require("../../../components/ContextMenu"));
|
|
11
|
+
const Popover_1 = __importDefault(require("../../../components/Popover"));
|
|
10
12
|
const View_1 = __importDefault(require("../../../components/View"));
|
|
11
13
|
const helpers_1 = require("../../../utilities/helpers");
|
|
12
14
|
const storybook_1 = require("../../../utilities/storybook");
|
|
@@ -91,6 +93,102 @@ exports.triggerRef = {
|
|
|
91
93
|
</>);
|
|
92
94
|
},
|
|
93
95
|
};
|
|
96
|
+
exports.sameMenuRightClick = {
|
|
97
|
+
name: "test: right click on the same context menu",
|
|
98
|
+
args: {
|
|
99
|
+
handleOpen: (0, test_1.fn)(),
|
|
100
|
+
handleClose: (0, test_1.fn)(),
|
|
101
|
+
},
|
|
102
|
+
render: (args) => {
|
|
103
|
+
const triggerRef = react_1.default.useRef(null);
|
|
104
|
+
return (<View_1.default gap={4}>
|
|
105
|
+
<ContextMenu_1.default onOpen={args.handleOpen} onClose={args.handleClose}>
|
|
106
|
+
<View_1.default height="60px" backgroundColor="neutral-faded" borderRadius="medium" attributes={{ "data-testid": "children" }}/>
|
|
107
|
+
|
|
108
|
+
<ContextMenu_1.default.Content>
|
|
109
|
+
<ContextMenu_1.default.Item>Children item</ContextMenu_1.default.Item>
|
|
110
|
+
</ContextMenu_1.default.Content>
|
|
111
|
+
</ContextMenu_1.default>
|
|
112
|
+
|
|
113
|
+
<View_1.default height="60px" backgroundColor="neutral-faded" borderRadius="medium" attributes={{ ref: triggerRef, "data-testid": "trigger-ref" }}/>
|
|
114
|
+
|
|
115
|
+
<ContextMenu_1.default triggerRef={triggerRef} onOpen={args.handleOpen} onClose={args.handleClose}>
|
|
116
|
+
<ContextMenu_1.default.Content>
|
|
117
|
+
<ContextMenu_1.default.Item>Trigger ref item</ContextMenu_1.default.Item>
|
|
118
|
+
</ContextMenu_1.default.Content>
|
|
119
|
+
</ContextMenu_1.default>
|
|
120
|
+
</View_1.default>);
|
|
121
|
+
},
|
|
122
|
+
play: async ({ canvasElement, args }) => {
|
|
123
|
+
const canvas = (0, test_1.within)(canvasElement.ownerDocument.body);
|
|
124
|
+
for (const [testId, text] of [
|
|
125
|
+
["children", "Children item"],
|
|
126
|
+
["trigger-ref", "Trigger ref item"],
|
|
127
|
+
]) {
|
|
128
|
+
const trigger = canvas.getByTestId(testId);
|
|
129
|
+
args.handleOpen.mockClear();
|
|
130
|
+
args.handleClose.mockClear();
|
|
131
|
+
const { left, top } = trigger.getBoundingClientRect();
|
|
132
|
+
await test_1.userEvent.pointer({
|
|
133
|
+
keys: "[MouseRight]",
|
|
134
|
+
target: trigger,
|
|
135
|
+
coords: { clientX: left + 10, clientY: top + 10 },
|
|
136
|
+
});
|
|
137
|
+
(0, test_1.expect)(args.handleOpen).toHaveBeenCalledTimes(1);
|
|
138
|
+
const menu = canvas.getByRole("menu");
|
|
139
|
+
const initialLeft = menu.getBoundingClientRect().left;
|
|
140
|
+
// Wait for the open animation to finish
|
|
141
|
+
await (0, helpers_1.sleep)(500);
|
|
142
|
+
await test_1.userEvent.pointer({
|
|
143
|
+
keys: "[MouseRight]",
|
|
144
|
+
target: trigger,
|
|
145
|
+
coords: { clientX: left + 210, clientY: top + 10 },
|
|
146
|
+
});
|
|
147
|
+
(0, test_1.expect)(args.handleClose).toHaveBeenCalledTimes(1);
|
|
148
|
+
(0, test_1.expect)(args.handleClose).toHaveBeenCalledWith({ reason: "outside-click" });
|
|
149
|
+
(0, test_1.expect)(args.handleOpen).toHaveBeenCalledTimes(2);
|
|
150
|
+
await (0, helpers_1.sleep)(500);
|
|
151
|
+
(0, test_1.expect)(canvas.getByText(text)).toBeInTheDocument();
|
|
152
|
+
(0, test_1.expect)(canvas.getByRole("menu").getBoundingClientRect().left).toBe(initialLeft + 200);
|
|
153
|
+
await test_1.userEvent.click(trigger);
|
|
154
|
+
(0, test_1.expect)(args.handleClose).toHaveBeenCalledTimes(2);
|
|
155
|
+
await (0, test_1.waitFor)(() => {
|
|
156
|
+
(0, test_1.expect)(canvas.queryByText(text)).not.toBeInTheDocument();
|
|
157
|
+
}, {
|
|
158
|
+
timeout: 1000,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
exports.insideFlyoutRightClick = {
|
|
164
|
+
name: "test: right click inside another flyout",
|
|
165
|
+
args: {
|
|
166
|
+
handlePopoverClose: (0, test_1.fn)(),
|
|
167
|
+
},
|
|
168
|
+
render: (args) => (<Popover_1.default onClose={args.handlePopoverClose}>
|
|
169
|
+
<Popover_1.default.Trigger>
|
|
170
|
+
{(attributes) => <Button_1.default attributes={attributes}>Open popover</Button_1.default>}
|
|
171
|
+
</Popover_1.default.Trigger>
|
|
172
|
+
<Popover_1.default.Content>
|
|
173
|
+
<ContextMenu_1.default>
|
|
174
|
+
<View_1.default width="200px" height="60px" backgroundColor="neutral-faded" borderRadius="medium" attributes={{ "data-testid": "popover-area" }}/>
|
|
175
|
+
|
|
176
|
+
<ContextMenu_1.default.Content>
|
|
177
|
+
<ContextMenu_1.default.Item>Context menu item</ContextMenu_1.default.Item>
|
|
178
|
+
</ContextMenu_1.default.Content>
|
|
179
|
+
</ContextMenu_1.default>
|
|
180
|
+
</Popover_1.default.Content>
|
|
181
|
+
</Popover_1.default>),
|
|
182
|
+
play: async ({ canvasElement, args }) => {
|
|
183
|
+
const canvas = (0, test_1.within)(canvasElement.ownerDocument.body);
|
|
184
|
+
await test_1.userEvent.click(canvas.getByText("Open popover"));
|
|
185
|
+
await (0, helpers_1.sleep)(500);
|
|
186
|
+
await test_1.userEvent.pointer({ keys: "[MouseRight]", target: canvas.getByTestId("popover-area") });
|
|
187
|
+
(0, test_1.expect)(canvas.getByText("Context menu item")).toBeInTheDocument();
|
|
188
|
+
(0, test_1.expect)(canvas.getByTestId("popover-area")).toBeInTheDocument();
|
|
189
|
+
(0, test_1.expect)(args.handlePopoverClose).not.toHaveBeenCalled();
|
|
190
|
+
},
|
|
191
|
+
};
|
|
94
192
|
const menuData = [
|
|
95
193
|
{ label: "Action 1" },
|
|
96
194
|
{ label: "Action 2" },
|
|
@@ -15,7 +15,7 @@ const DropdownMenuSubContext = React.createContext(null);
|
|
|
15
15
|
const DropdownMenuSubTriggerContext = React.createContext(false);
|
|
16
16
|
const DropdownMenu = (props) => {
|
|
17
17
|
const { children, position = "bottom-start", triggerType = "click", trapFocusMode = "action-menu", size, ...popoverProps } = props;
|
|
18
|
-
return (_jsx(DropdownMenuProvider, { size: size, children: _jsx(Popover, { ...popoverProps, position: position, padding: 1, trapFocusMode: trapFocusMode, triggerType: triggerType,
|
|
18
|
+
return (_jsx(DropdownMenuProvider, { size: size, children: _jsx(Popover, { ...popoverProps, position: position, padding: 1, trapFocusMode: trapFocusMode, triggerType: triggerType, children: children }) }));
|
|
19
19
|
};
|
|
20
20
|
export const DropdownMenuContent = (props) => {
|
|
21
21
|
const { children, attributes, className } = props;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
.positioner{--rs-flyout-max-h:100%;--rs-flyout-max-w:100%;--rs-flyout-z-index:var(--rs-z-index-absolute);--rs-flyout-origin-x:50%;--rs-flyout-origin-y:50%;position:absolute;isolation:isolate;z-index:var(--rs-flyout-z-index);display:flex;flex-direction:column;pointer-events:none}.positioner.--hover{pointer-events:all}.positioner.--hover-disabled,.positioner.--hover-disabled .content{pointer-events:none}.content{pointer-events:all;opacity:0;transform:scale(.92) translateY(0);transform-origin:var(--rs-flyout-origin-x) var(--rs-flyout-origin-y);outline:none;height:100%;max-width:var(--rs-flyout-max-w);max-height:var(--rs-flyout-max-h);backface-visibility:hidden;flex-grow:1;display:flex;flex-direction:column;transition:1ms var(--rs-easing-accelerate)}[data-rs-keyboard] .content:focus{box-shadow:var(--rs-outline-shadow)}.positioner.--width-trigger .content{transform:scale(1) translateY(var(--rs-unit-x2))}.positioner.--position-top,.positioner.--position-top-end,.positioner.--position-top-start{--rs-flyout-origin-y:100%}.positioner.--position-bottom,.positioner.--position-bottom-end,.positioner.--position-bottom-start{--rs-flyout-origin-y:0%}.positioner.--position-bottom-start,.positioner.--position-top-start{--rs-flyout-origin-x:0%}.positioner.--position-bottom-end,.positioner.--position-start,.positioner.--position-start-bottom,.positioner.--position-start-top,.positioner.--position-top-end{--rs-flyout-origin-x:100%}.positioner.--position-end,.positioner.--position-end-bottom,.positioner.--position-end-top{--rs-flyout-origin-x:0%}.positioner.--position-end-top,.positioner.--position-start-top{--rs-flyout-origin-y:0%}.positioner.--position-end-bottom,.positioner.--position-start-bottom{--rs-flyout-origin-y:100%}.positioner.--visible .content{opacity:1;transform:scale(1) translateY(0)}.positioner.--animated .content{transition-duration:var(--rs-duration-rapid);transition-property:opacity,transform}.positioner.--animated.--visible .content{transition-duration:var(--rs-duration-fast);transition-timing-function:var(--rs-easing-decelerate)}
|
|
1
|
+
.positioner{--rs-flyout-max-h:100%;--rs-flyout-max-w:100%;--rs-flyout-z-index:var(--rs-z-index-absolute);--rs-flyout-origin-x:50%;--rs-flyout-origin-y:50%;position:absolute;isolation:isolate;z-index:var(--rs-flyout-z-index);display:flex;flex-direction:column;pointer-events:none}.positioner.--hover{pointer-events:all}.positioner.--hover-disabled,.positioner.--hover-disabled .content{pointer-events:none}.content{pointer-events:all;opacity:0;transform:scale(.92) translateY(0);transform-origin:var(--rs-flyout-origin-x) var(--rs-flyout-origin-y);outline:none;height:100%;max-width:var(--rs-flyout-max-w);max-height:var(--rs-flyout-max-h);backface-visibility:hidden;flex-grow:1;display:flex;flex-direction:column;transition:1ms var(--rs-easing-accelerate)}[data-rs-keyboard] .content:focus{box-shadow:var(--rs-outline-shadow)}.positioner.--width-trigger .content{transform:scale(1) translateY(var(--rs-unit-x2))}.positioner.--position-top,.positioner.--position-top-end,.positioner.--position-top-start{--rs-flyout-origin-y:100%}.positioner.--position-bottom,.positioner.--position-bottom-end,.positioner.--position-bottom-start{--rs-flyout-origin-y:0%}.positioner.--position-bottom-start,.positioner.--position-top-start{--rs-flyout-origin-x:0%}.positioner.--position-bottom-end,.positioner.--position-start,.positioner.--position-start-bottom,.positioner.--position-start-top,.positioner.--position-top-end{--rs-flyout-origin-x:100%}.positioner.--position-end,.positioner.--position-end-bottom,.positioner.--position-end-top{--rs-flyout-origin-x:0%}.positioner.--position-end-top,.positioner.--position-start-top{--rs-flyout-origin-y:0%}.positioner.--position-end-bottom,.positioner.--position-start-bottom{--rs-flyout-origin-y:100%}.positioner.--visible .content{opacity:1;transform:scale(1) translateY(0);pointer-events:all}.positioner.--animated .content{transition-duration:var(--rs-duration-rapid);transition-property:opacity,transform}.positioner.--animated.--visible .content{transition-duration:var(--rs-duration-fast);transition-timing-function:var(--rs-easing-decelerate)}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { classNames } from "@reshaped/utilities";
|
|
5
|
+
import Freeze from "../../components/_private/Freeze";
|
|
5
6
|
import Portal from "../../components/_private/Portal";
|
|
6
7
|
import useIsomorphicLayoutEffect from "../../hooks/useIsomorphicLayoutEffect";
|
|
7
8
|
import ScrollArea from "../ScrollArea";
|
|
@@ -10,7 +11,7 @@ import cooldown from "./utilities/cooldown";
|
|
|
10
11
|
import s from "./Flyout.module.css";
|
|
11
12
|
const FlyoutContent = (props) => {
|
|
12
13
|
const { children, className, attributes, colorMode } = props;
|
|
13
|
-
const { flyout, id, triggerElRef, flyoutElRef, handleTransitionEnd, triggerType, handleContentMouseEnter, handleMouseLeave, handleContentMouseDown, handleContentMouseUp, contentClassName, contentAttributes, scrollableClassName, scrollableAttributes, contentMaxHeight, contentMaxWidth, contentZIndex, trapFocusMode, disableContentHover, autoFocus, width, isSubmenu, } = useFlyoutContext();
|
|
14
|
+
const { active, flyout, id, triggerElRef, flyoutElRef, handleTransitionEnd, triggerType, handleContentMouseEnter, handleMouseLeave, handleContentMouseDown, handleContentMouseUp, contentClassName, contentAttributes, scrollableClassName, scrollableAttributes, contentMaxHeight, contentMaxWidth, contentZIndex, trapFocusMode, disableContentHover, autoFocus, width, isSubmenu, } = useFlyoutContext();
|
|
14
15
|
const { status, position } = flyout;
|
|
15
16
|
const [mounted, setMounted] = React.useState(false);
|
|
16
17
|
const shadowRootRef = React.useRef(null);
|
|
@@ -48,7 +49,7 @@ const FlyoutContent = (props) => {
|
|
|
48
49
|
"--rs-flyout-max-h": contentMaxHeight,
|
|
49
50
|
"--rs-flyout-max-w": contentMaxWidth,
|
|
50
51
|
"--rs-flyout-z-index": contentZIndex,
|
|
51
|
-
}, ref: flyoutElRef, onTransitionEnd: handleTransitionEnd, onMouseEnter: triggerType === "hover" ? handleContentMouseEnter : undefined, onMouseLeave: triggerType === "hover" ? handleMouseLeave : undefined, onMouseDown: handleContentMouseDown, onTouchStart: handleContentMouseDown, onMouseUp: handleContentMouseUp, onTouchEnd: handleContentMouseUp, children: _jsx("div", { role: role, ...contentAttributes, ...attributes, id: id, tabIndex: !autoFocus ? -1 : undefined, "aria-modal": role === "dialog" ? true : undefined, style: { ...contentAttributes?.style, ...attributes?.style }, className: contentClassNames, children: _jsx(ScrollArea, { overscrollBehavior: "contain", scrollableAttributes: scrollableAttributes, scrollableClassName: scrollableClassName, children: children }) }) }) }));
|
|
52
|
+
}, ref: flyoutElRef, onTransitionEnd: handleTransitionEnd, onMouseEnter: triggerType === "hover" ? handleContentMouseEnter : undefined, onMouseLeave: triggerType === "hover" ? handleMouseLeave : undefined, onMouseDown: handleContentMouseDown, onTouchStart: handleContentMouseDown, onMouseUp: handleContentMouseUp, onTouchEnd: handleContentMouseUp, children: _jsx("div", { role: role, ...contentAttributes, ...attributes, id: id, tabIndex: !autoFocus ? -1 : undefined, "aria-modal": role === "dialog" ? true : undefined, style: { ...contentAttributes?.style, ...attributes?.style }, className: contentClassNames, children: _jsx(ScrollArea, { overscrollBehavior: "contain", scrollableAttributes: scrollableAttributes, scrollableClassName: scrollableClassName, children: _jsx(Freeze, { frozen: !active, children: children }) }) }) }) }));
|
|
52
53
|
return (_jsx(Portal, { targetRef: shadowRootRef.current ? shadowRootRef : undefined, colorMode: colorMode, children: content }));
|
|
53
54
|
};
|
|
54
55
|
FlyoutContent.displayName = "Flyout.Content";
|
|
@@ -271,6 +271,25 @@ const FlyoutControlled = (props) => {
|
|
|
271
271
|
if (status === "rendered")
|
|
272
272
|
show();
|
|
273
273
|
}, [status, show]);
|
|
274
|
+
// Content can be reopened at new coordinates without closing in between
|
|
275
|
+
useIsomorphicLayoutEffect(() => {
|
|
276
|
+
if (!originCoordinates || !isRendered)
|
|
277
|
+
return;
|
|
278
|
+
updatePosition();
|
|
279
|
+
// oxlint-disable-next-line react-hooks/exhaustive-deps
|
|
280
|
+
}, [originCoordinates?.x, originCoordinates?.y]);
|
|
281
|
+
/**
|
|
282
|
+
* Closing before the open transition starts cancels it instead of reversing,
|
|
283
|
+
* so there is no transitionend to wait for
|
|
284
|
+
*/
|
|
285
|
+
useIsomorphicLayoutEffect(() => {
|
|
286
|
+
if (status !== "hidden")
|
|
287
|
+
return;
|
|
288
|
+
if (flyoutElRef.current?.firstElementChild?.getAnimations?.().length)
|
|
289
|
+
return;
|
|
290
|
+
remove();
|
|
291
|
+
onAfterCloseRef.current?.();
|
|
292
|
+
}, [status, remove, onAfterCloseRef]);
|
|
274
293
|
/**
|
|
275
294
|
* Handle focus trap
|
|
276
295
|
*
|
|
@@ -301,9 +320,8 @@ const FlyoutControlled = (props) => {
|
|
|
301
320
|
});
|
|
302
321
|
}, [status, triggerType, trapFocusMode, autoFocus]);
|
|
303
322
|
React.useEffect(() => {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
if (disableHideAnimation && isRendered)
|
|
323
|
+
// Release when the hide animation starts or right away when the content is removed without it
|
|
324
|
+
if (status !== "hidden" && isRendered)
|
|
307
325
|
return;
|
|
308
326
|
if (trapFocusRef.current?.trapped) {
|
|
309
327
|
/* Locking the popover to not open it again on trigger focus */
|
|
@@ -318,7 +336,7 @@ const FlyoutControlled = (props) => {
|
|
|
318
336
|
trapFocusRef.current.release({ withoutFocusReturn: !shouldReturnFocusRef.current });
|
|
319
337
|
shouldReturnFocusRef.current = true;
|
|
320
338
|
}
|
|
321
|
-
}, [status, isRendered, triggerType
|
|
339
|
+
}, [status, isRendered, triggerType]);
|
|
322
340
|
/**
|
|
323
341
|
* Clean up safe polygon tracking on unmount or when flyout closes
|
|
324
342
|
*/
|
|
@@ -362,6 +380,7 @@ const FlyoutControlled = (props) => {
|
|
|
362
380
|
});
|
|
363
381
|
return (_jsx(Provider, { value: {
|
|
364
382
|
id,
|
|
383
|
+
active: !!active,
|
|
365
384
|
flyout,
|
|
366
385
|
width,
|
|
367
386
|
triggerElRef,
|
|
@@ -75,6 +75,13 @@ export declare const disableHideAnimation: {
|
|
|
75
75
|
name: string;
|
|
76
76
|
render: () => React.JSX.Element;
|
|
77
77
|
};
|
|
78
|
+
export declare const testFrozenContentOnClose: StoryObj<{
|
|
79
|
+
handleAfterClose: ReturnType<typeof fn>;
|
|
80
|
+
}>;
|
|
81
|
+
export declare const testCloseRightAfterOpen: StoryObj<{
|
|
82
|
+
handleAfterClose: ReturnType<typeof fn>;
|
|
83
|
+
}>;
|
|
84
|
+
export declare const testFocusReturnWithoutTransitions: StoryObj;
|
|
78
85
|
export declare const disabled: StoryObj<{
|
|
79
86
|
handleOpen: ReturnType<typeof fn>;
|
|
80
87
|
}>;
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.testChangeSize = exports.testWithoutFocusable = exports.testScopedTheming = exports.testDynamicBounds = exports.testInsideModal = exports.testInsideScrollable = exports.testInsideSticky = exports.testInsideFixed = exports.contentAttributes = exports.instanceRef = exports.initialFocusRef = exports.positionRef = exports.containerRef = exports.disabled = exports.disableHideAnimation = exports.disableCloseOnOutsideClick = exports.disableContentHover = exports.contentMaxHeight = exports.contentZIndex = exports.contentShift = exports.contentGap = exports.width = exports.originCoordinates = exports.fallbackAdjustLayoutSize = exports.fallbackAdjustLayoutShift = exports.fallbackMinHeight = exports.fallbackAdjustLayout = exports.positionFallbacks = exports.modes = exports.activeFalse = exports.active = exports.defaultActive = exports.position = void 0;
|
|
6
|
+
exports.testChangeSize = exports.testWithoutFocusable = exports.testScopedTheming = exports.testDynamicBounds = exports.testInsideModal = exports.testInsideScrollable = exports.testInsideSticky = exports.testInsideFixed = exports.contentAttributes = exports.instanceRef = exports.initialFocusRef = exports.positionRef = exports.containerRef = exports.disabled = exports.testFocusReturnWithoutTransitions = exports.testCloseRightAfterOpen = exports.testFrozenContentOnClose = exports.disableHideAnimation = exports.disableCloseOnOutsideClick = exports.disableContentHover = exports.contentMaxHeight = exports.contentZIndex = exports.contentShift = exports.contentGap = exports.width = exports.originCoordinates = exports.fallbackAdjustLayoutSize = exports.fallbackAdjustLayoutShift = exports.fallbackMinHeight = exports.fallbackAdjustLayout = exports.positionFallbacks = exports.modes = exports.activeFalse = exports.active = exports.defaultActive = exports.position = void 0;
|
|
7
7
|
const react_1 = __importDefault(require("react"));
|
|
8
8
|
const test_1 = require("storybook/test");
|
|
9
9
|
const Button_1 = __importDefault(require("../../../components/Button"));
|
|
@@ -448,6 +448,147 @@ exports.disableHideAnimation = {
|
|
|
448
448
|
name: "disableHideAnimation",
|
|
449
449
|
render: () => <Demo disableHideAnimation defaultActive/>,
|
|
450
450
|
};
|
|
451
|
+
exports.testFrozenContentOnClose = {
|
|
452
|
+
name: "test: content doesn't change while closing",
|
|
453
|
+
args: {
|
|
454
|
+
handleAfterClose: (0, test_1.fn)(),
|
|
455
|
+
},
|
|
456
|
+
render: (args) => {
|
|
457
|
+
const [active, setActive] = react_1.default.useState(false);
|
|
458
|
+
const [value, setValue] = react_1.default.useState("First");
|
|
459
|
+
return (<Flyout_1.default active={active} onOpen={() => setActive(true)} onClose={() => setActive(false)} onAfterClose={args.handleAfterClose}>
|
|
460
|
+
<Flyout_1.default.Trigger>
|
|
461
|
+
{(attributes) => <Button_1.default attributes={attributes}>{value}</Button_1.default>}
|
|
462
|
+
</Flyout_1.default.Trigger>
|
|
463
|
+
<Flyout_1.default.Content>
|
|
464
|
+
<Content>
|
|
465
|
+
<div data-testid="selected-value">Selected: {value}</div>
|
|
466
|
+
<Button_1.default onClick={() => {
|
|
467
|
+
setValue("Second");
|
|
468
|
+
setActive(false);
|
|
469
|
+
}}>
|
|
470
|
+
Select second
|
|
471
|
+
</Button_1.default>
|
|
472
|
+
</Content>
|
|
473
|
+
</Flyout_1.default.Content>
|
|
474
|
+
</Flyout_1.default>);
|
|
475
|
+
},
|
|
476
|
+
play: async ({ canvasElement, args }) => {
|
|
477
|
+
const canvas = (0, test_1.within)(canvasElement.ownerDocument.body);
|
|
478
|
+
// Transitions are disabled for the first frames after the color mode gets applied
|
|
479
|
+
await (0, test_1.waitFor)(() => {
|
|
480
|
+
(0, test_1.expect)(document.documentElement).not.toHaveAttribute("data-rs-no-transition");
|
|
481
|
+
});
|
|
482
|
+
await test_1.userEvent.click(canvas.getByRole("button", { name: "First" }));
|
|
483
|
+
await (0, test_1.waitFor)(() => {
|
|
484
|
+
(0, test_1.expect)(canvas.getByTestId("selected-value")).toBeVisible();
|
|
485
|
+
});
|
|
486
|
+
// Record the content state after every DOM update since the hide animation might end before click resolves
|
|
487
|
+
const snapshots = [];
|
|
488
|
+
const observer = new MutationObserver(() => {
|
|
489
|
+
const el = canvasElement.ownerDocument.querySelector("[data-testid=selected-value]");
|
|
490
|
+
if (!el)
|
|
491
|
+
return;
|
|
492
|
+
snapshots.push({ text: el.textContent, visible: el.checkVisibility() });
|
|
493
|
+
});
|
|
494
|
+
observer.observe(canvasElement.ownerDocument.body, {
|
|
495
|
+
subtree: true,
|
|
496
|
+
childList: true,
|
|
497
|
+
attributes: true,
|
|
498
|
+
characterData: true,
|
|
499
|
+
});
|
|
500
|
+
await test_1.userEvent.click(canvas.getByText("Select second"));
|
|
501
|
+
await (0, test_1.waitFor)(() => {
|
|
502
|
+
(0, test_1.expect)(canvas.queryByTestId("selected-value")).not.toBeInTheDocument();
|
|
503
|
+
});
|
|
504
|
+
observer.disconnect();
|
|
505
|
+
// Trigger is updated right away while the content keeps its last state during the hide animation
|
|
506
|
+
(0, test_1.expect)(canvas.getAllByRole("button")[0]).toHaveTextContent("Second");
|
|
507
|
+
(0, test_1.expect)(snapshots.length).toBeGreaterThan(0);
|
|
508
|
+
snapshots.forEach((snapshot) => {
|
|
509
|
+
(0, test_1.expect)(snapshot).toEqual({ text: "Selected: First", visible: true });
|
|
510
|
+
});
|
|
511
|
+
(0, test_1.expect)(args.handleAfterClose).toHaveBeenCalledTimes(1);
|
|
512
|
+
},
|
|
513
|
+
};
|
|
514
|
+
exports.testCloseRightAfterOpen = {
|
|
515
|
+
name: "test: close right after opening",
|
|
516
|
+
args: {
|
|
517
|
+
handleAfterClose: (0, test_1.fn)(),
|
|
518
|
+
},
|
|
519
|
+
render: (args) => {
|
|
520
|
+
const [active, setActive] = react_1.default.useState(false);
|
|
521
|
+
const [closeOnOpen, setCloseOnOpen] = react_1.default.useState(false);
|
|
522
|
+
// Closing in the same frame, before the open transition starts
|
|
523
|
+
react_1.default.useEffect(() => {
|
|
524
|
+
if (active && closeOnOpen)
|
|
525
|
+
setActive(false);
|
|
526
|
+
}, [active, closeOnOpen]);
|
|
527
|
+
return (<Flyout_1.default active={active} onAfterClose={args.handleAfterClose}>
|
|
528
|
+
<Flyout_1.default.Trigger>
|
|
529
|
+
{(attributes) => (<Button_1.default attributes={attributes} onClick={() => {
|
|
530
|
+
setCloseOnOpen(true);
|
|
531
|
+
setActive(true);
|
|
532
|
+
}}>
|
|
533
|
+
Trigger
|
|
534
|
+
</Button_1.default>)}
|
|
535
|
+
</Flyout_1.default.Trigger>
|
|
536
|
+
<Flyout_1.default.Content>
|
|
537
|
+
<Content />
|
|
538
|
+
</Flyout_1.default.Content>
|
|
539
|
+
</Flyout_1.default>);
|
|
540
|
+
},
|
|
541
|
+
play: async ({ canvasElement, args }) => {
|
|
542
|
+
const canvas = (0, test_1.within)(canvasElement.ownerDocument.body);
|
|
543
|
+
// Transitions are disabled for the first frames after the color mode gets applied
|
|
544
|
+
await (0, test_1.waitFor)(() => {
|
|
545
|
+
(0, test_1.expect)(document.documentElement).not.toHaveAttribute("data-rs-no-transition");
|
|
546
|
+
});
|
|
547
|
+
await test_1.userEvent.click(canvas.getByRole("button", { name: "Trigger" }));
|
|
548
|
+
await (0, test_1.waitFor)(() => {
|
|
549
|
+
(0, test_1.expect)(canvas.queryByText("Content")).not.toBeInTheDocument();
|
|
550
|
+
(0, test_1.expect)(args.handleAfterClose).toHaveBeenCalledTimes(1);
|
|
551
|
+
});
|
|
552
|
+
},
|
|
553
|
+
};
|
|
554
|
+
exports.testFocusReturnWithoutTransitions = {
|
|
555
|
+
name: "test: focus returns to the trigger with transitions disabled",
|
|
556
|
+
render: () => (<Flyout_1.default>
|
|
557
|
+
<Flyout_1.default.Trigger>
|
|
558
|
+
{(attributes) => <Button_1.default attributes={attributes}>Trigger</Button_1.default>}
|
|
559
|
+
</Flyout_1.default.Trigger>
|
|
560
|
+
<Flyout_1.default.Content>
|
|
561
|
+
<Content>
|
|
562
|
+
<Button_1.default onClick={() => { }}>Inside</Button_1.default>
|
|
563
|
+
</Content>
|
|
564
|
+
</Flyout_1.default.Content>
|
|
565
|
+
</Flyout_1.default>),
|
|
566
|
+
play: async ({ canvasElement }) => {
|
|
567
|
+
const canvas = (0, test_1.within)(canvasElement.ownerDocument.body);
|
|
568
|
+
const trigger = canvas.getByRole("button", { name: "Trigger" });
|
|
569
|
+
// Wait for the provider to enable transitions after mount before disabling them again
|
|
570
|
+
await (0, test_1.waitFor)(() => {
|
|
571
|
+
(0, test_1.expect)(document.documentElement).not.toHaveAttribute("data-rs-no-transition");
|
|
572
|
+
});
|
|
573
|
+
// Same as prefers-reduced-motion, content is removed without the hide animation
|
|
574
|
+
document.documentElement.setAttribute("data-rs-no-transition", "true");
|
|
575
|
+
try {
|
|
576
|
+
trigger.focus();
|
|
577
|
+
await test_1.userEvent.keyboard("{Enter}");
|
|
578
|
+
await (0, test_1.waitFor)(() => {
|
|
579
|
+
(0, test_1.expect)(canvas.getByRole("dialog")).toContainElement(document.activeElement);
|
|
580
|
+
});
|
|
581
|
+
await test_1.userEvent.keyboard("{Escape}");
|
|
582
|
+
await (0, test_1.waitFor)(() => {
|
|
583
|
+
(0, test_1.expect)(canvas.queryByRole("dialog")).not.toBeInTheDocument();
|
|
584
|
+
});
|
|
585
|
+
(0, test_1.expect)(trigger).toHaveFocus();
|
|
586
|
+
}
|
|
587
|
+
finally {
|
|
588
|
+
document.documentElement.removeAttribute("data-rs-no-transition");
|
|
589
|
+
}
|
|
590
|
+
},
|
|
591
|
+
};
|
|
451
592
|
exports.disabled = {
|
|
452
593
|
name: "disabled",
|
|
453
594
|
args: {
|
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
3
|
+
import { createContext, useContext, useEffect, useMemo, useState, useRef } from "react";
|
|
4
4
|
import { classNames } from "@reshaped/utilities";
|
|
5
|
-
import { enableScroll, disableScroll } from "@reshaped/utilities/internal";
|
|
6
5
|
import Overlay from "../../components/Overlay";
|
|
7
6
|
import Text from "../../components/Text";
|
|
8
7
|
import useElementId from "../../hooks/useElementId";
|
|
9
|
-
import useHandlerRef from "../../hooks/useHandlerRef";
|
|
10
8
|
import useResponsiveClientValue from "../../hooks/useResponsiveClientValue";
|
|
11
9
|
import { responsiveClassNames, responsiveVariables } from "../../utilities/props";
|
|
12
10
|
import { resolveMixin } from "../../styles/mixin";
|
|
11
|
+
import useModalDrag from "./useModalDrag";
|
|
13
12
|
import s from "./Modal.module.css";
|
|
14
|
-
const DRAG_THRESHOLD = 32;
|
|
15
|
-
const DRAG_OPPOSITE_THRESHOLD = 100;
|
|
16
|
-
const DRAG_EDGE_BOUNDARY = 32;
|
|
17
|
-
const DRAG_SCROLL_LOCK_THRESHOLD = 8;
|
|
18
13
|
const Context = createContext({
|
|
19
14
|
id: "",
|
|
20
15
|
titleMounted: false,
|
|
@@ -43,39 +38,25 @@ export const ModalSubtitle = (props) => {
|
|
|
43
38
|
};
|
|
44
39
|
const Modal = (props) => {
|
|
45
40
|
const { children, onClose, onAfterClose, onAfterOpen, active, size, padding = 4, position = "center", overflow, transparentOverlay, blurredOverlay, ariaLabel, autoFocus = true, disableSwipeGesture, disableCloseOnOutsideClick, containerRef, contained, overlayClassName, className, attributes, } = props;
|
|
46
|
-
const onCloseRef = useHandlerRef(onClose);
|
|
47
41
|
const id = useElementId();
|
|
48
42
|
const clientPosition = useResponsiveClientValue(position);
|
|
49
43
|
const [titleMounted, setTitleMounted] = useState(false);
|
|
50
44
|
const [subtitleMounted, setSubtitleMounted] = useState(false);
|
|
51
|
-
const [dragging, setDragging] = useState(false);
|
|
52
45
|
const internalRootRef = useRef(null);
|
|
53
46
|
const rootRef = attributes?.ref || internalRootRef;
|
|
54
|
-
const dragStartCoordinatesRef = useRef({ x: 0, y: 0 });
|
|
55
|
-
const dragLastCoordinateRef = useRef(0);
|
|
56
|
-
const dragDistanceRef = useRef(0);
|
|
57
|
-
const dragFrameRef = useRef(null);
|
|
58
|
-
const dragPendingDistanceRef = useRef(0);
|
|
59
|
-
const dragSizeRef = useRef(1);
|
|
60
47
|
const overlayRef = useRef(null);
|
|
61
|
-
const dragDirectionRef = useRef(0);
|
|
62
|
-
const dragShouldCloseRef = useRef(false);
|
|
63
48
|
const mixinStyles = resolveMixin({ padding });
|
|
64
49
|
const shouldBeContained = containerRef && contained !== false;
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const opacity = Math.max(0, 1 - progress * 0.5);
|
|
76
|
-
overlayRef.current.setOpacity(opacity);
|
|
77
|
-
}
|
|
78
|
-
}, [clientPosition, overlayRef, transparentOverlay, rootRef]);
|
|
50
|
+
const { dragging, handleTouchStart, handleTransitionEnd } = useModalDrag({
|
|
51
|
+
position: clientPosition,
|
|
52
|
+
rootRef,
|
|
53
|
+
overlayRef,
|
|
54
|
+
active,
|
|
55
|
+
containerRef,
|
|
56
|
+
transparentOverlay,
|
|
57
|
+
disabled: disableSwipeGesture,
|
|
58
|
+
onClose,
|
|
59
|
+
});
|
|
79
60
|
const value = useMemo(() => ({
|
|
80
61
|
titleMounted,
|
|
81
62
|
setTitleMounted,
|
|
@@ -83,147 +64,8 @@ const Modal = (props) => {
|
|
|
83
64
|
setSubtitleMounted,
|
|
84
65
|
id,
|
|
85
66
|
}), [id, subtitleMounted, titleMounted]);
|
|
86
|
-
const resetDragData = useCallback(() => {
|
|
87
|
-
dragDistanceRef.current = 0;
|
|
88
|
-
dragPendingDistanceRef.current = 0;
|
|
89
|
-
dragStartCoordinatesRef.current = { x: 0, y: 0 };
|
|
90
|
-
dragLastCoordinateRef.current = 0;
|
|
91
|
-
dragDirectionRef.current = 0;
|
|
92
|
-
dragShouldCloseRef.current = false;
|
|
93
|
-
setDragStyles(0);
|
|
94
|
-
}, [setDragStyles]);
|
|
95
|
-
const unlockDragScroll = useCallback(() => {
|
|
96
|
-
rootRef.current?.style.removeProperty("overflow");
|
|
97
|
-
}, [rootRef]);
|
|
98
|
-
const lockDragScroll = useCallback(() => {
|
|
99
|
-
rootRef.current?.style.setProperty("overflow", "hidden");
|
|
100
|
-
}, [rootRef]);
|
|
101
|
-
const handleDragStart = (e) => {
|
|
102
|
-
if (disableSwipeGesture)
|
|
103
|
-
return;
|
|
104
|
-
dragShouldCloseRef.current = false;
|
|
105
|
-
// Prevent swipe to close from happening when user is working with text selection
|
|
106
|
-
if (window.getSelection()?.toString())
|
|
107
|
-
return;
|
|
108
|
-
let currentEl = e.target;
|
|
109
|
-
const rootEl = rootRef.current;
|
|
110
|
-
while (currentEl && (currentEl === rootEl || rootEl?.contains(currentEl))) {
|
|
111
|
-
// Prioritize scrolling over modal swiping
|
|
112
|
-
if (currentEl.scrollTop !== 0 || currentEl.scrollLeft !== 0)
|
|
113
|
-
return;
|
|
114
|
-
// Start dragging only when starting on static elements
|
|
115
|
-
if (currentEl.matches("input,textarea"))
|
|
116
|
-
return;
|
|
117
|
-
currentEl = currentEl ? currentEl.parentElement : null;
|
|
118
|
-
}
|
|
119
|
-
// Prevent the drag handling when browser is trying to navigate to a previous page
|
|
120
|
-
if (clientPosition === "start" && e.targetTouches[0].clientX < DRAG_EDGE_BOUNDARY)
|
|
121
|
-
return;
|
|
122
|
-
if (rootEl) {
|
|
123
|
-
const isInline = ["start", "end"].includes(clientPosition);
|
|
124
|
-
dragSizeRef.current = Math.max(1, isInline ? rootEl.clientWidth : rootEl.clientHeight);
|
|
125
|
-
}
|
|
126
|
-
if (containerRef?.current)
|
|
127
|
-
disableScroll();
|
|
128
|
-
setDragging(true);
|
|
129
|
-
};
|
|
130
|
-
// Once modal is closed - reset its drag data
|
|
131
|
-
const handleTransitionEnd = (e) => {
|
|
132
|
-
if (active)
|
|
133
|
-
return;
|
|
134
|
-
if (e.propertyName !== "transform")
|
|
135
|
-
return;
|
|
136
|
-
if (e.currentTarget !== e.target)
|
|
137
|
-
return;
|
|
138
|
-
resetDragData();
|
|
139
|
-
unlockDragScroll();
|
|
140
|
-
};
|
|
141
|
-
useEffect(() => {
|
|
142
|
-
if (!dragging)
|
|
143
|
-
return;
|
|
144
|
-
const handleDragEnd = () => {
|
|
145
|
-
const shouldClose = clientPosition === "start" ? dragDirectionRef.current < 0 : dragDirectionRef.current > 0;
|
|
146
|
-
dragShouldCloseRef.current =
|
|
147
|
-
Math.abs(dragDistanceRef.current) > DRAG_THRESHOLD && shouldClose;
|
|
148
|
-
if (containerRef?.current)
|
|
149
|
-
enableScroll();
|
|
150
|
-
if (!dragShouldCloseRef.current)
|
|
151
|
-
unlockDragScroll();
|
|
152
|
-
setDragging(false);
|
|
153
|
-
if (dragShouldCloseRef.current) {
|
|
154
|
-
onCloseRef.current?.({ reason: "drag" });
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
resetDragData();
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
const handleDrag = (e) => {
|
|
161
|
-
if (!dragging || clientPosition === "center")
|
|
162
|
-
return;
|
|
163
|
-
if (rootRef.current?.scrollTop !== 0 || rootRef.current?.scrollLeft !== 0)
|
|
164
|
-
return;
|
|
165
|
-
const target = e.targetTouches[0];
|
|
166
|
-
const coordinate = { x: target.clientX, y: target.clientY };
|
|
167
|
-
const key = clientPosition === "bottom" ? "y" : "x";
|
|
168
|
-
const oppositeKey = clientPosition === "bottom" ? "x" : "y";
|
|
169
|
-
// Save the initial coordinates
|
|
170
|
-
if (!dragStartCoordinatesRef.current[key]) {
|
|
171
|
-
dragStartCoordinatesRef.current = coordinate;
|
|
172
|
-
dragLastCoordinateRef.current = coordinate[key];
|
|
173
|
-
}
|
|
174
|
-
const next = Math.abs(coordinate[key] - dragStartCoordinatesRef.current[key]);
|
|
175
|
-
const nextPerpendicular = Math.abs(coordinate[oppositeKey] - dragStartCoordinatesRef.current[oppositeKey]);
|
|
176
|
-
// For start/end drawers - ignore the swiping
|
|
177
|
-
// If user is scrolling vertically more than swiping
|
|
178
|
-
if (position !== "bottom" &&
|
|
179
|
-
(next < nextPerpendicular || nextPerpendicular > DRAG_OPPOSITE_THRESHOLD)) {
|
|
180
|
-
dragLastCoordinateRef.current = coordinate[key];
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
dragDirectionRef.current = coordinate[key] - dragLastCoordinateRef.current;
|
|
184
|
-
dragLastCoordinateRef.current = coordinate[key];
|
|
185
|
-
const isClosingDirection = clientPosition === "start" ? dragDirectionRef.current < 0 : dragDirectionRef.current > 0;
|
|
186
|
-
if (next > DRAG_SCROLL_LOCK_THRESHOLD && isClosingDirection) {
|
|
187
|
-
lockDragScroll();
|
|
188
|
-
}
|
|
189
|
-
dragDistanceRef.current =
|
|
190
|
-
clientPosition === "start"
|
|
191
|
-
? Math.min(0, dragDistanceRef.current + dragDirectionRef.current)
|
|
192
|
-
: Math.max(0, dragDistanceRef.current + dragDirectionRef.current);
|
|
193
|
-
dragPendingDistanceRef.current = dragDistanceRef.current;
|
|
194
|
-
if (dragFrameRef.current === null) {
|
|
195
|
-
dragFrameRef.current = window.requestAnimationFrame(() => {
|
|
196
|
-
setDragStyles(dragPendingDistanceRef.current);
|
|
197
|
-
dragFrameRef.current = null;
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
};
|
|
201
|
-
document.addEventListener("touchmove", handleDrag, { passive: true });
|
|
202
|
-
document.addEventListener("touchend", handleDragEnd, { passive: true });
|
|
203
|
-
return () => {
|
|
204
|
-
if (dragFrameRef.current !== null) {
|
|
205
|
-
window.cancelAnimationFrame(dragFrameRef.current);
|
|
206
|
-
dragFrameRef.current = null;
|
|
207
|
-
}
|
|
208
|
-
if (!dragShouldCloseRef.current)
|
|
209
|
-
unlockDragScroll();
|
|
210
|
-
document.removeEventListener("touchmove", handleDrag);
|
|
211
|
-
document.removeEventListener("touchend", handleDragEnd);
|
|
212
|
-
};
|
|
213
|
-
}, [
|
|
214
|
-
dragging,
|
|
215
|
-
clientPosition,
|
|
216
|
-
onCloseRef,
|
|
217
|
-
position,
|
|
218
|
-
rootRef,
|
|
219
|
-
containerRef,
|
|
220
|
-
setDragStyles,
|
|
221
|
-
resetDragData,
|
|
222
|
-
lockDragScroll,
|
|
223
|
-
unlockDragScroll,
|
|
224
|
-
]);
|
|
225
67
|
return (_jsx(Overlay, { instanceRef: overlayRef, onClose: onClose, onAfterClose: onAfterClose, onAfterOpen: onAfterOpen, disableCloseOnClick: disableCloseOnOutsideClick, active: active, transparent: transparentOverlay, blurred: blurredOverlay, overflow: clientPosition === "center" ? "auto" : "hidden", className: overlayClassName, contained: contained, containerRef: containerRef, attributes: {
|
|
226
|
-
onTouchStart:
|
|
68
|
+
onTouchStart: handleTouchStart,
|
|
227
69
|
}, children: ({ active }) => {
|
|
228
70
|
const rootClassNames = classNames(s.root, className, active && s["--active"], dragging && s["--dragging"], overflow && s[`--overflow-${overflow}`], shouldBeContained && s["--contained"], responsiveClassNames(s, "--position", position), mixinStyles.classNames);
|
|
229
71
|
return (_jsx(Context.Provider, { value: value, children: _jsx("div", { ...attributes, style: {
|