@purpurds/drawer 5.15.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/dist/LICENSE.txt +478 -0
- package/dist/drawer-container.d.ts +11 -0
- package/dist/drawer-container.d.ts.map +1 -0
- package/dist/drawer-content.d.ts +27 -0
- package/dist/drawer-content.d.ts.map +1 -0
- package/dist/drawer-frame.d.ts +23 -0
- package/dist/drawer-frame.d.ts.map +1 -0
- package/dist/drawer-handle.d.ts +13 -0
- package/dist/drawer-handle.d.ts.map +1 -0
- package/dist/drawer-header.d.ts +14 -0
- package/dist/drawer-header.d.ts.map +1 -0
- package/dist/drawer-scroll-area.d.ts +9 -0
- package/dist/drawer-scroll-area.d.ts.map +1 -0
- package/dist/drawer-trigger.d.ts +9 -0
- package/dist/drawer-trigger.d.ts.map +1 -0
- package/dist/drawer.cjs.js +62 -0
- package/dist/drawer.cjs.js.map +1 -0
- package/dist/drawer.context.d.ts +4 -0
- package/dist/drawer.context.d.ts.map +1 -0
- package/dist/drawer.d.ts +17 -0
- package/dist/drawer.d.ts.map +1 -0
- package/dist/drawer.es.js +2791 -0
- package/dist/drawer.es.js.map +1 -0
- package/dist/styles.css +1 -0
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/use-swipe-to-dismiss.hook.d.ts +13 -0
- package/dist/use-swipe-to-dismiss.hook.d.ts.map +1 -0
- package/dist/use-swipe-tracking.hook.d.ts +15 -0
- package/dist/use-swipe-tracking.hook.d.ts.map +1 -0
- package/package.json +67 -0
- package/src/drawer-container.module.scss +24 -0
- package/src/drawer-container.test.tsx +74 -0
- package/src/drawer-container.tsx +48 -0
- package/src/drawer-content.module.scss +101 -0
- package/src/drawer-content.test.tsx +80 -0
- package/src/drawer-content.tsx +124 -0
- package/src/drawer-frame.module.scss +44 -0
- package/src/drawer-frame.test.tsx +139 -0
- package/src/drawer-frame.tsx +140 -0
- package/src/drawer-handle.module.scss +23 -0
- package/src/drawer-handle.test.tsx +37 -0
- package/src/drawer-handle.tsx +59 -0
- package/src/drawer-header.module.scss +29 -0
- package/src/drawer-header.test.tsx +173 -0
- package/src/drawer-header.tsx +117 -0
- package/src/drawer-scroll-area.module.scss +42 -0
- package/src/drawer-scroll-area.test.tsx +28 -0
- package/src/drawer-scroll-area.tsx +45 -0
- package/src/drawer-trigger.test.tsx +33 -0
- package/src/drawer-trigger.tsx +34 -0
- package/src/drawer.context.ts +5 -0
- package/src/drawer.module.scss +3 -0
- package/src/drawer.stories.tsx +197 -0
- package/src/drawer.test.tsx +210 -0
- package/src/drawer.tsx +59 -0
- package/src/global.d.ts +4 -0
- package/src/types.ts +3 -0
- package/src/use-swipe-to-dismiss.hook.ts +60 -0
- package/src/use-swipe-tracking.hook.ts +78 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import * as RadixDialog from "@radix-ui/react-dialog";
|
|
3
|
+
import * as matchers from "@testing-library/jest-dom/matchers";
|
|
4
|
+
import { cleanup, render, screen } from "@testing-library/react";
|
|
5
|
+
import userEvent from "@testing-library/user-event";
|
|
6
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
7
|
+
|
|
8
|
+
import { Drawer } from "./drawer";
|
|
9
|
+
|
|
10
|
+
const ResizeObserverMock = vi.fn(() => ({
|
|
11
|
+
disconnect: vi.fn(),
|
|
12
|
+
observe: vi.fn(),
|
|
13
|
+
takeRecords: vi.fn(),
|
|
14
|
+
unobserve: vi.fn(),
|
|
15
|
+
}));
|
|
16
|
+
vi.stubGlobal("ResizeObserver", ResizeObserverMock);
|
|
17
|
+
|
|
18
|
+
expect.extend(matchers);
|
|
19
|
+
|
|
20
|
+
const handleOpenChange = vi.fn();
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
vi.clearAllMocks();
|
|
24
|
+
cleanup();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe("Drawer", () => {
|
|
28
|
+
it("should call the provided onOpenChange when open state changes", async () => {
|
|
29
|
+
render(
|
|
30
|
+
<Drawer open={true} onOpenChange={handleOpenChange}>
|
|
31
|
+
<Drawer.Trigger>
|
|
32
|
+
<button data-testid={Selectors.DRAWER_TRIGGER} type="button">
|
|
33
|
+
Open drawer
|
|
34
|
+
</button>
|
|
35
|
+
</Drawer.Trigger>
|
|
36
|
+
<Drawer.Content
|
|
37
|
+
data-testid={Selectors.DRAWER_CONTENT}
|
|
38
|
+
backButton
|
|
39
|
+
backButtonText="Back"
|
|
40
|
+
bodyText="This is the body text"
|
|
41
|
+
closeButtonText="Close"
|
|
42
|
+
onBackButtonClick={() => {}}
|
|
43
|
+
stickyFooter
|
|
44
|
+
title="Title"
|
|
45
|
+
>
|
|
46
|
+
<div>
|
|
47
|
+
<p>This is some content</p>
|
|
48
|
+
<RadixDialog.Close asChild>
|
|
49
|
+
<button data-testid={Selectors.CLOSE_BUTTON} type="button">
|
|
50
|
+
Close the drawer
|
|
51
|
+
</button>
|
|
52
|
+
</RadixDialog.Close>
|
|
53
|
+
</div>
|
|
54
|
+
</Drawer.Content>
|
|
55
|
+
</Drawer>
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
await userEvent.click(screen.getByTestId(Selectors.CLOSE_BUTTON));
|
|
59
|
+
expect(handleOpenChange).toHaveBeenCalledWith(false);
|
|
60
|
+
expect(handleOpenChange).toHaveBeenCalledOnce();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("should call the provided onOpenChange when clicking the Drawer.Trigger", async () => {
|
|
64
|
+
render(
|
|
65
|
+
<Drawer open={false} onOpenChange={handleOpenChange}>
|
|
66
|
+
<Drawer.Trigger>
|
|
67
|
+
<button data-testid={Selectors.DRAWER_TRIGGER} type="button">
|
|
68
|
+
Open drawer
|
|
69
|
+
</button>
|
|
70
|
+
</Drawer.Trigger>
|
|
71
|
+
<Drawer.Content
|
|
72
|
+
data-testid={Selectors.DRAWER_CONTENT}
|
|
73
|
+
backButton
|
|
74
|
+
backButtonText="Back"
|
|
75
|
+
bodyText="This is the body text"
|
|
76
|
+
closeButtonText="Close"
|
|
77
|
+
onBackButtonClick={() => {}}
|
|
78
|
+
stickyFooter
|
|
79
|
+
title="Title"
|
|
80
|
+
>
|
|
81
|
+
<div>
|
|
82
|
+
<p>This is some content</p>
|
|
83
|
+
<RadixDialog.Close asChild>
|
|
84
|
+
<button data-testid={Selectors.CLOSE_BUTTON} type="button">
|
|
85
|
+
Close the drawer
|
|
86
|
+
</button>
|
|
87
|
+
</RadixDialog.Close>
|
|
88
|
+
</div>
|
|
89
|
+
</Drawer.Content>
|
|
90
|
+
</Drawer>
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
await userEvent.click(screen.getByTestId(Selectors.DRAWER_TRIGGER));
|
|
94
|
+
expect(handleOpenChange).toHaveBeenCalledWith(true);
|
|
95
|
+
expect(handleOpenChange).toHaveBeenCalledOnce();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("should call the provided onOpenChange when pressing Esc key", async () => {
|
|
99
|
+
render(
|
|
100
|
+
<Drawer open={true} onOpenChange={handleOpenChange}>
|
|
101
|
+
<Drawer.Trigger>
|
|
102
|
+
<button data-testid={Selectors.DRAWER_TRIGGER} type="button">
|
|
103
|
+
Open drawer
|
|
104
|
+
</button>
|
|
105
|
+
</Drawer.Trigger>
|
|
106
|
+
<Drawer.Content
|
|
107
|
+
data-testid={Selectors.DRAWER_CONTENT}
|
|
108
|
+
backButton
|
|
109
|
+
backButtonText="Back"
|
|
110
|
+
bodyText="This is the body text"
|
|
111
|
+
closeButtonText="Close"
|
|
112
|
+
onBackButtonClick={() => {}}
|
|
113
|
+
stickyFooter
|
|
114
|
+
title="Title"
|
|
115
|
+
>
|
|
116
|
+
<div>
|
|
117
|
+
<p>This is some content</p>
|
|
118
|
+
<RadixDialog.Close asChild>
|
|
119
|
+
<button data-testid={Selectors.CLOSE_BUTTON} type="button">
|
|
120
|
+
Close the drawer
|
|
121
|
+
</button>
|
|
122
|
+
</RadixDialog.Close>
|
|
123
|
+
</div>
|
|
124
|
+
</Drawer.Content>
|
|
125
|
+
</Drawer>
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
await userEvent.keyboard("{Escape}");
|
|
129
|
+
expect(handleOpenChange).toHaveBeenCalledWith(false);
|
|
130
|
+
expect(handleOpenChange).toHaveBeenCalledOnce();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("should call the provided onOpenChange when pressing the overlay", async () => {
|
|
134
|
+
render(
|
|
135
|
+
<Drawer open={true} onOpenChange={handleOpenChange}>
|
|
136
|
+
<Drawer.Trigger>
|
|
137
|
+
<button data-testid={Selectors.DRAWER_TRIGGER} type="button">
|
|
138
|
+
Open drawer
|
|
139
|
+
</button>
|
|
140
|
+
</Drawer.Trigger>
|
|
141
|
+
<Drawer.Content
|
|
142
|
+
data-testid={Selectors.DRAWER_CONTENT}
|
|
143
|
+
backButton
|
|
144
|
+
backButtonText="Back"
|
|
145
|
+
bodyText="This is the body text"
|
|
146
|
+
closeButtonText="Close"
|
|
147
|
+
onBackButtonClick={() => {}}
|
|
148
|
+
stickyFooter
|
|
149
|
+
title="Title"
|
|
150
|
+
>
|
|
151
|
+
<div>
|
|
152
|
+
<p>This is some content</p>
|
|
153
|
+
<RadixDialog.Close asChild>
|
|
154
|
+
<button data-testid={Selectors.CLOSE_BUTTON} type="button">
|
|
155
|
+
Close the drawer
|
|
156
|
+
</button>
|
|
157
|
+
</RadixDialog.Close>
|
|
158
|
+
</div>
|
|
159
|
+
</Drawer.Content>
|
|
160
|
+
</Drawer>
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
await userEvent.click(screen.getByTestId(Selectors.DRAWER_CONTENT_OVERLAY));
|
|
164
|
+
expect(handleOpenChange).toHaveBeenCalledWith(false);
|
|
165
|
+
expect(handleOpenChange).toHaveBeenCalledOnce();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("should not call the provided onOpenChange when pressing the overlay if closeOnOverlayClick is set to false", async () => {
|
|
169
|
+
render(
|
|
170
|
+
<Drawer open={true} onOpenChange={handleOpenChange}>
|
|
171
|
+
<Drawer.Trigger>
|
|
172
|
+
<button data-testid={Selectors.DRAWER_TRIGGER} type="button">
|
|
173
|
+
Open drawer
|
|
174
|
+
</button>
|
|
175
|
+
</Drawer.Trigger>
|
|
176
|
+
<Drawer.Content
|
|
177
|
+
data-testid={Selectors.DRAWER_CONTENT}
|
|
178
|
+
backButton
|
|
179
|
+
backButtonText="Back"
|
|
180
|
+
bodyText="This is the body text"
|
|
181
|
+
closeButtonText="Close"
|
|
182
|
+
disableCloseOnClickOutside={true}
|
|
183
|
+
onBackButtonClick={() => {}}
|
|
184
|
+
stickyFooter
|
|
185
|
+
title="Title"
|
|
186
|
+
>
|
|
187
|
+
<div>
|
|
188
|
+
<p>This is some content</p>
|
|
189
|
+
<RadixDialog.Close asChild>
|
|
190
|
+
<button data-testid={Selectors.CLOSE_BUTTON} type="button">
|
|
191
|
+
Close the drawer
|
|
192
|
+
</button>
|
|
193
|
+
</RadixDialog.Close>
|
|
194
|
+
</div>
|
|
195
|
+
</Drawer.Content>
|
|
196
|
+
</Drawer>
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
await userEvent.click(screen.getByTestId(Selectors.DRAWER_CONTENT_OVERLAY));
|
|
200
|
+
expect(handleOpenChange).not.toHaveBeenCalled();
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const Selectors = {
|
|
205
|
+
DRAWER: "purpur-drawer",
|
|
206
|
+
DRAWER_TRIGGER: "purpur-drawer-trigger",
|
|
207
|
+
DRAWER_CONTENT: "purpur-drawer-content",
|
|
208
|
+
DRAWER_CONTENT_OVERLAY: "purpur-drawer-content-overlay",
|
|
209
|
+
CLOSE_BUTTON: "purpur-drawer-close-button",
|
|
210
|
+
};
|
package/src/drawer.tsx
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import React, { ReactNode, useEffect } from "react";
|
|
2
|
+
import * as RadixDialog from "@radix-ui/react-dialog";
|
|
3
|
+
import c from "classnames/bind";
|
|
4
|
+
|
|
5
|
+
import { DrawerContext } from "./drawer.context";
|
|
6
|
+
import styles from "./drawer.module.scss";
|
|
7
|
+
import { DrawerContent } from "./drawer-content";
|
|
8
|
+
import { DrawerTrigger } from "./drawer-trigger";
|
|
9
|
+
const cx = c.bind(styles);
|
|
10
|
+
|
|
11
|
+
export type DrawerProps = {
|
|
12
|
+
["data-testid"]?: string;
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
className?: string;
|
|
15
|
+
onOpenChange?: (open: boolean) => void;
|
|
16
|
+
open: boolean;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const rootClassName = "purpur-drawer";
|
|
20
|
+
|
|
21
|
+
export type DrawerComponent<P> = React.FunctionComponent<P> & {
|
|
22
|
+
Trigger: typeof DrawerTrigger;
|
|
23
|
+
Content: typeof DrawerContent;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const Drawer: DrawerComponent<DrawerProps> = ({
|
|
27
|
+
["data-testid"]: dataTestId = "purpur-drawer",
|
|
28
|
+
children,
|
|
29
|
+
className,
|
|
30
|
+
onOpenChange,
|
|
31
|
+
open = false,
|
|
32
|
+
...props
|
|
33
|
+
}: DrawerProps) => {
|
|
34
|
+
const [_open, _setOpen] = React.useState(open);
|
|
35
|
+
const classes = cx([className, rootClassName]);
|
|
36
|
+
|
|
37
|
+
const handleOpenChange = (newOpen: boolean) => {
|
|
38
|
+
_setOpen(newOpen);
|
|
39
|
+
onOpenChange?.(newOpen);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
_setOpen(open);
|
|
44
|
+
}, [open]);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<DrawerContext.Provider value={handleOpenChange}>
|
|
48
|
+
<div className={classes} data-testid={dataTestId} {...props}>
|
|
49
|
+
<RadixDialog.Root open={_open} onOpenChange={handleOpenChange}>
|
|
50
|
+
{children}
|
|
51
|
+
</RadixDialog.Root>
|
|
52
|
+
</div>
|
|
53
|
+
</DrawerContext.Provider>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
Drawer.Trigger = DrawerTrigger;
|
|
58
|
+
Drawer.Content = DrawerContent;
|
|
59
|
+
Drawer.displayName = "Drawer";
|
package/src/global.d.ts
ADDED
package/src/types.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { SwipeEvent } from "./types";
|
|
4
|
+
|
|
5
|
+
type UseSwipeToDismiss = {
|
|
6
|
+
onAnimationEnd(event: React.AnimationEvent<HTMLDivElement>): void;
|
|
7
|
+
onSwipeStart(): void;
|
|
8
|
+
onSwipeMove(event: SwipeEvent): void;
|
|
9
|
+
onSwipeCancel(): void;
|
|
10
|
+
onSwipeEnd(event: SwipeEvent): void;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const useSwipeToDismiss = <T extends HTMLElement>(
|
|
14
|
+
containerRef: React.MutableRefObject<T | null>,
|
|
15
|
+
handleOpenChange: (open: boolean) => void
|
|
16
|
+
): UseSwipeToDismiss => {
|
|
17
|
+
const onSwipeStart = () => {
|
|
18
|
+
if (!containerRef.current) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
containerRef.current.setAttribute("data-swipe", "start");
|
|
22
|
+
};
|
|
23
|
+
const onSwipeMove = (event: SwipeEvent) => {
|
|
24
|
+
if (!containerRef.current) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const { y } = event.delta;
|
|
28
|
+
containerRef.current.setAttribute("data-swipe", "move");
|
|
29
|
+
containerRef.current.style.setProperty("--purpur-drawer-swipe-move-y", `${y}px`);
|
|
30
|
+
};
|
|
31
|
+
const onSwipeCancel = () => {
|
|
32
|
+
if (!containerRef.current) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
containerRef.current.setAttribute("data-swipe", "cancel");
|
|
36
|
+
containerRef.current.style.removeProperty("--purpur-drawer-swipe-move-y");
|
|
37
|
+
containerRef.current.style.removeProperty("--purpur-drawer-swipe-end-y");
|
|
38
|
+
};
|
|
39
|
+
const onSwipeEnd = (event: SwipeEvent) => {
|
|
40
|
+
if (!containerRef.current) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const { y } = event.delta;
|
|
44
|
+
containerRef.current.setAttribute("data-swipe", "end");
|
|
45
|
+
containerRef.current.style.removeProperty("--purpur-drawer-swipe-move-y");
|
|
46
|
+
containerRef.current.style.setProperty("--purpur-drawer-swipe-end-y", `${y}px`);
|
|
47
|
+
};
|
|
48
|
+
const onAnimationEnd = (event: React.AnimationEvent<HTMLDivElement>) => {
|
|
49
|
+
if (event.currentTarget.getAttribute("data-swipe") === "end") {
|
|
50
|
+
handleOpenChange(false);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
onAnimationEnd,
|
|
55
|
+
onSwipeStart,
|
|
56
|
+
onSwipeMove,
|
|
57
|
+
onSwipeCancel,
|
|
58
|
+
onSwipeEnd,
|
|
59
|
+
};
|
|
60
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { SwipeEvent } from "./types";
|
|
4
|
+
|
|
5
|
+
type UseSwipeTracking = {
|
|
6
|
+
onPointerDown(event: React.PointerEvent): void;
|
|
7
|
+
onPointerMove(event: React.PointerEvent): void;
|
|
8
|
+
onPointerUp(event: React.PointerEvent): void;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const useSwipeTracking = (
|
|
12
|
+
pointerStartRef: React.MutableRefObject<{ y: number } | null>,
|
|
13
|
+
swipeDeltaRef: React.MutableRefObject<{ y: number } | null>,
|
|
14
|
+
onSwipeStart: () => void,
|
|
15
|
+
onSwipeMove: (event: SwipeEvent) => void,
|
|
16
|
+
onSwipeCancel: () => void,
|
|
17
|
+
onSwipeEnd: (event: SwipeEvent) => void
|
|
18
|
+
): UseSwipeTracking => {
|
|
19
|
+
const onPointerDown = (event: React.PointerEvent) => {
|
|
20
|
+
pointerStartRef.current = { y: event.clientY };
|
|
21
|
+
};
|
|
22
|
+
const onPointerMove = (event: React.PointerEvent) => {
|
|
23
|
+
if (!pointerStartRef.current) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const y = event.clientY - pointerStartRef.current.y;
|
|
27
|
+
const hasSwipeMoveStarted = Boolean(swipeDeltaRef.current);
|
|
28
|
+
const clampedY = Math.max(0, y);
|
|
29
|
+
const moveStartBuffer = event.pointerType === "touch" ? 10 : 2;
|
|
30
|
+
const delta = { y: clampedY };
|
|
31
|
+
const eventDetail = { originalEvent: event, delta };
|
|
32
|
+
if (hasSwipeMoveStarted) {
|
|
33
|
+
swipeDeltaRef.current = delta;
|
|
34
|
+
onSwipeMove(eventDetail);
|
|
35
|
+
} else if (isDeltaInDirection(delta, 0)) {
|
|
36
|
+
swipeDeltaRef.current = delta;
|
|
37
|
+
onSwipeStart();
|
|
38
|
+
(event.target as HTMLElement).setPointerCapture(event.pointerId);
|
|
39
|
+
} else if (Math.abs(y) > moveStartBuffer) {
|
|
40
|
+
// User is swiping in wrong direction so we disable swipe gesture
|
|
41
|
+
// for the current pointer down interaction
|
|
42
|
+
pointerStartRef.current = null;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const onPointerUp = (event: React.PointerEvent) => {
|
|
46
|
+
const delta = swipeDeltaRef.current;
|
|
47
|
+
const target = event.target as HTMLElement;
|
|
48
|
+
if (target.hasPointerCapture(event.pointerId)) {
|
|
49
|
+
target.releasePointerCapture(event.pointerId);
|
|
50
|
+
}
|
|
51
|
+
swipeDeltaRef.current = null;
|
|
52
|
+
pointerStartRef.current = null;
|
|
53
|
+
if (delta) {
|
|
54
|
+
const swipeHandle = event.currentTarget;
|
|
55
|
+
const eventDetail = { originalEvent: event, delta };
|
|
56
|
+
if (isDeltaInDirection(delta, 0) && delta.y > 200) {
|
|
57
|
+
onSwipeEnd(eventDetail);
|
|
58
|
+
} else {
|
|
59
|
+
onSwipeCancel();
|
|
60
|
+
}
|
|
61
|
+
// Prevent click event from triggering on items within the drawer when
|
|
62
|
+
// pointer up is part of a swipe gesture
|
|
63
|
+
swipeHandle.addEventListener("click", (event) => event.preventDefault(), {
|
|
64
|
+
once: true,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
return {
|
|
69
|
+
onPointerDown,
|
|
70
|
+
onPointerMove,
|
|
71
|
+
onPointerUp,
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const isDeltaInDirection = (delta: { y: number }, threshold = 0) => {
|
|
76
|
+
const deltaY = Math.abs(delta.y);
|
|
77
|
+
return deltaY > threshold;
|
|
78
|
+
};
|