@seed-design/react-drawer 1.0.7 → 1.0.9

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/lib/index.d.ts CHANGED
@@ -1,12 +1,35 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as DialogPrimitive from '@radix-ui/react-dialog';
3
3
  import { PrimitiveProps } from '@seed-design/react-primitive';
4
- import * as react from 'react';
4
+ import * as React$1 from 'react';
5
+ import React__default from 'react';
5
6
 
7
+ interface DrawerReasonToDetailMap {
8
+ closeButton: {
9
+ event: MouseEvent;
10
+ };
11
+ escapeKeyDown: {
12
+ event: KeyboardEvent;
13
+ };
14
+ interactOutside: {
15
+ event: PointerEvent | FocusEvent;
16
+ };
17
+ drag: {
18
+ event: PointerEvent;
19
+ };
20
+ handleClickOnLastSnapPoint: {
21
+ event: MouseEvent;
22
+ };
23
+ }
24
+ type DrawerChangeDetails = {
25
+ [R in keyof DrawerReasonToDetailMap]: {
26
+ reason?: R;
27
+ } & DrawerReasonToDetailMap[R];
28
+ }[keyof DrawerReasonToDetailMap];
6
29
  interface UseDrawerProps {
7
30
  activeSnapPoint?: number | string | null;
8
31
  setActiveSnapPoint?: (snapPoint: number | string | null) => void;
9
- children?: React.ReactNode;
32
+ children?: React__default.ReactNode;
10
33
  open?: boolean;
11
34
  /**
12
35
  * Number between 0 and 1 that determines when the drawer should be closed.
@@ -19,7 +42,7 @@ interface UseDrawerProps {
19
42
  * @default true
20
43
  */
21
44
  noBodyStyles?: boolean;
22
- onOpenChange?: (open: boolean) => void;
45
+ onOpenChange?: (open: boolean, details?: DrawerChangeDetails) => void;
23
46
  /**
24
47
  * Duration for which the drawer is not draggable after scrolling content inside of the drawer.
25
48
  * @default 500ms
@@ -40,8 +63,8 @@ interface UseDrawerProps {
40
63
  * @default true
41
64
  */
42
65
  dismissible?: boolean;
43
- onDrag?: (event: React.PointerEvent<HTMLDivElement>, percentageDragged: number) => void;
44
- onRelease?: (event: React.PointerEvent<HTMLDivElement>, open: boolean) => void;
66
+ onDrag?: (event: React__default.PointerEvent<HTMLDivElement>, percentageDragged: number) => void;
67
+ onRelease?: (event: React__default.PointerEvent<HTMLDivElement>, open: boolean) => void;
45
68
  /**
46
69
  * When `false` it allows to interact with elements outside of the drawer without closing it.
47
70
  * @default true
@@ -106,21 +129,21 @@ interface UseDrawerProps {
106
129
  declare function useDrawer(props: UseDrawerProps): {
107
130
  activeSnapPoint: string | number | null;
108
131
  snapPoints: (string | number)[] | undefined;
109
- setActiveSnapPoint: (value: react.SetStateAction<string | number | null>) => void;
110
- drawerRef: react.RefObject<HTMLDivElement | null>;
111
- overlayRef: react.RefObject<HTMLDivElement | null>;
132
+ setActiveSnapPoint: (value: string | number | ((prev: string | number | null) => string | number | null) | null, details?: undefined) => void;
133
+ drawerRef: React__default.RefObject<HTMLDivElement | null>;
134
+ overlayRef: React__default.RefObject<HTMLDivElement | null>;
112
135
  shouldOverlayAnimate: boolean;
113
- onOpenChange: ((open: boolean) => void) | undefined;
114
- onPress: (event: React.PointerEvent<HTMLDivElement>) => void;
115
- onRelease: (event: React.PointerEvent<HTMLDivElement> | null) => void;
116
- onDrag: (event: React.PointerEvent<HTMLDivElement>) => void;
136
+ onOpenChange: ((open: boolean, details?: DrawerChangeDetails) => void) | undefined;
137
+ onPress: (event: React__default.PointerEvent<HTMLDivElement>) => void;
138
+ onRelease: (event: React__default.PointerEvent<HTMLDivElement> | null) => void;
139
+ onDrag: (event: React__default.PointerEvent<HTMLDivElement>) => void;
117
140
  dismissible: boolean;
118
141
  handleOnly: boolean;
119
142
  isOpen: boolean;
120
143
  isDragging: boolean;
121
144
  shouldFade: boolean;
122
- closeDrawer: (fromWithin?: boolean) => void;
123
- keyboardIsOpen: react.RefObject<boolean>;
145
+ closeDrawer: (fromWithin?: boolean, details?: DrawerChangeDetails) => void;
146
+ keyboardIsOpen: React__default.RefObject<boolean>;
124
147
  modal: boolean;
125
148
  snapPointsOffset: number[];
126
149
  activeSnapPointIndex: number | null;
@@ -128,51 +151,77 @@ declare function useDrawer(props: UseDrawerProps): {
128
151
  noBodyStyles: boolean;
129
152
  container: HTMLElement | null | undefined;
130
153
  autoFocus: boolean;
131
- setHasBeenOpened: react.Dispatch<react.SetStateAction<boolean>>;
132
- setIsOpen: (value: react.SetStateAction<boolean>) => void;
154
+ setHasBeenOpened: React__default.Dispatch<React__default.SetStateAction<boolean>>;
155
+ setIsOpen: (value: boolean | ((prev: boolean) => boolean), details?: DrawerChangeDetails | undefined) => void;
133
156
  closeOnInteractOutside: boolean;
134
157
  closeOnEscape: boolean;
158
+ hasAnimationDone: boolean;
159
+ closeButtonRef: (node: HTMLButtonElement | null) => void;
160
+ isCloseButtonRendered: boolean;
135
161
  };
136
162
 
137
163
  interface DrawerRootProps extends UseDrawerProps {
138
- children?: react.ReactNode;
164
+ children?: React$1.ReactNode;
139
165
  }
140
166
  declare const DrawerRoot: (props: DrawerRootProps) => react_jsx_runtime.JSX.Element;
141
167
  interface DrawerTriggerProps extends DialogPrimitive.DialogTriggerProps {
142
168
  }
143
- declare const DrawerTrigger: react.ForwardRefExoticComponent<DialogPrimitive.DialogTriggerProps & react.RefAttributes<HTMLButtonElement>>;
144
- interface DrawerPositionerProps extends PrimitiveProps, react.HTMLAttributes<HTMLDivElement> {
169
+ declare const DrawerTrigger: React$1.ForwardRefExoticComponent<DialogPrimitive.DialogTriggerProps & React$1.RefAttributes<HTMLButtonElement>>;
170
+ interface DrawerPositionerProps extends PrimitiveProps, React$1.HTMLAttributes<HTMLDivElement> {
145
171
  }
146
- declare const DrawerPositioner: react.ForwardRefExoticComponent<DrawerPositionerProps & react.RefAttributes<HTMLDivElement>>;
147
- interface DrawerBackdropProps extends DialogPrimitive.DialogOverlayProps, react.HTMLAttributes<HTMLDivElement> {
172
+ declare const DrawerPositioner: React$1.ForwardRefExoticComponent<DrawerPositionerProps & React$1.RefAttributes<HTMLDivElement>>;
173
+ interface DrawerBackdropProps extends DialogPrimitive.DialogOverlayProps, React$1.HTMLAttributes<HTMLDivElement> {
148
174
  }
149
- declare const DrawerBackdrop: react.ForwardRefExoticComponent<DrawerBackdropProps & react.RefAttributes<HTMLDivElement>>;
150
- interface DrawerContentProps extends DialogPrimitive.DialogContentProps, react.HTMLAttributes<HTMLDivElement> {
175
+ declare const DrawerBackdrop: React$1.ForwardRefExoticComponent<DrawerBackdropProps & React$1.RefAttributes<HTMLDivElement>>;
176
+ interface DrawerContentProps extends DialogPrimitive.DialogContentProps, React$1.HTMLAttributes<HTMLDivElement> {
151
177
  }
152
- declare const DrawerContent: react.ForwardRefExoticComponent<DrawerContentProps & react.RefAttributes<HTMLDivElement>>;
178
+ declare const DrawerContent: React$1.ForwardRefExoticComponent<DrawerContentProps & React$1.RefAttributes<HTMLDivElement>>;
153
179
  interface DrawerTitleProps extends DialogPrimitive.DialogTitleProps {
154
180
  }
155
- declare const DrawerTitle: react.ForwardRefExoticComponent<DialogPrimitive.DialogTitleProps & react.RefAttributes<HTMLHeadingElement>>;
181
+ declare const DrawerTitle: React$1.ForwardRefExoticComponent<DialogPrimitive.DialogTitleProps & React$1.RefAttributes<HTMLHeadingElement>>;
156
182
  interface DrawerDescriptionProps extends DialogPrimitive.DialogDescriptionProps {
157
183
  }
158
- declare const DrawerDescription: react.ForwardRefExoticComponent<DialogPrimitive.DialogDescriptionProps & react.RefAttributes<HTMLParagraphElement>>;
184
+ declare const DrawerDescription: React$1.ForwardRefExoticComponent<DialogPrimitive.DialogDescriptionProps & React$1.RefAttributes<HTMLParagraphElement>>;
185
+ interface DrawerHeaderProps extends PrimitiveProps, React$1.HTMLAttributes<HTMLDivElement> {
186
+ }
187
+ declare const DrawerHeader: React$1.ForwardRefExoticComponent<DrawerHeaderProps & React$1.RefAttributes<HTMLDivElement>>;
159
188
  interface DrawerCloseButtonProps extends DialogPrimitive.DialogCloseProps {
160
189
  }
161
- declare const DrawerCloseButton: react.ForwardRefExoticComponent<DrawerCloseButtonProps & react.RefAttributes<HTMLButtonElement>>;
162
- interface DrawerHandleProps extends react.HTMLAttributes<HTMLDivElement> {
190
+ declare const DrawerCloseButton: React$1.ForwardRefExoticComponent<DrawerCloseButtonProps & React$1.RefAttributes<HTMLButtonElement>>;
191
+ interface DrawerHandleProps extends React$1.HTMLAttributes<HTMLDivElement> {
163
192
  preventCycle?: boolean;
164
193
  }
165
- declare const DrawerHandle: react.ForwardRefExoticComponent<DrawerHandleProps & react.RefAttributes<HTMLDivElement>>;
194
+ declare const DrawerHandle: React$1.ForwardRefExoticComponent<DrawerHandleProps & React$1.RefAttributes<HTMLDivElement>>;
166
195
 
167
196
  type DrawerContextValue = ReturnType<typeof useDrawer>;
168
197
  declare function useDrawerContext(): {
169
198
  activeSnapPoint: string | number | null;
170
199
  snapPoints: (string | number)[] | undefined;
171
- setActiveSnapPoint: (value: react.SetStateAction<string | number | null>) => void;
172
- drawerRef: react.RefObject<HTMLDivElement | null>;
173
- overlayRef: react.RefObject<HTMLDivElement | null>;
200
+ setActiveSnapPoint: (value: string | number | ((prev: string | number | null) => string | number | null) | null, details?: undefined) => void;
201
+ drawerRef: React$1.RefObject<HTMLDivElement | null>;
202
+ overlayRef: React$1.RefObject<HTMLDivElement | null>;
174
203
  shouldOverlayAnimate: boolean;
175
- onOpenChange: ((open: boolean) => void) | undefined;
204
+ onOpenChange: ((open: boolean, details?: ({
205
+ reason?: "closeButton" | undefined;
206
+ } & {
207
+ event: MouseEvent;
208
+ }) | ({
209
+ reason?: "escapeKeyDown" | undefined;
210
+ } & {
211
+ event: KeyboardEvent;
212
+ }) | ({
213
+ reason?: "interactOutside" | undefined;
214
+ } & {
215
+ event: PointerEvent | FocusEvent;
216
+ }) | ({
217
+ reason?: "drag" | undefined;
218
+ } & {
219
+ event: PointerEvent;
220
+ }) | ({
221
+ reason?: "handleClickOnLastSnapPoint" | undefined;
222
+ } & {
223
+ event: MouseEvent;
224
+ })) => void) | undefined;
176
225
  onPress: (event: React.PointerEvent<HTMLDivElement>) => void;
177
226
  onRelease: (event: React.PointerEvent<HTMLDivElement> | null) => void;
178
227
  onDrag: (event: React.PointerEvent<HTMLDivElement>) => void;
@@ -181,8 +230,28 @@ declare function useDrawerContext(): {
181
230
  isOpen: boolean;
182
231
  isDragging: boolean;
183
232
  shouldFade: boolean;
184
- closeDrawer: (fromWithin?: boolean) => void;
185
- keyboardIsOpen: react.RefObject<boolean>;
233
+ closeDrawer: (fromWithin?: boolean, details?: ({
234
+ reason?: "closeButton" | undefined;
235
+ } & {
236
+ event: MouseEvent;
237
+ }) | ({
238
+ reason?: "escapeKeyDown" | undefined;
239
+ } & {
240
+ event: KeyboardEvent;
241
+ }) | ({
242
+ reason?: "interactOutside" | undefined;
243
+ } & {
244
+ event: PointerEvent | FocusEvent;
245
+ }) | ({
246
+ reason?: "drag" | undefined;
247
+ } & {
248
+ event: PointerEvent;
249
+ }) | ({
250
+ reason?: "handleClickOnLastSnapPoint" | undefined;
251
+ } & {
252
+ event: MouseEvent;
253
+ })) => void;
254
+ keyboardIsOpen: React$1.RefObject<boolean>;
186
255
  modal: boolean;
187
256
  snapPointsOffset: number[];
188
257
  activeSnapPointIndex: number | null;
@@ -190,16 +259,39 @@ declare function useDrawerContext(): {
190
259
  noBodyStyles: boolean;
191
260
  container: HTMLElement | null | undefined;
192
261
  autoFocus: boolean;
193
- setHasBeenOpened: react.Dispatch<react.SetStateAction<boolean>>;
194
- setIsOpen: (value: react.SetStateAction<boolean>) => void;
262
+ setHasBeenOpened: React$1.Dispatch<React$1.SetStateAction<boolean>>;
263
+ setIsOpen: (value: boolean | ((prev: boolean) => boolean), details?: (({
264
+ reason?: "closeButton" | undefined;
265
+ } & {
266
+ event: MouseEvent;
267
+ }) | ({
268
+ reason?: "escapeKeyDown" | undefined;
269
+ } & {
270
+ event: KeyboardEvent;
271
+ }) | ({
272
+ reason?: "interactOutside" | undefined;
273
+ } & {
274
+ event: PointerEvent | FocusEvent;
275
+ }) | ({
276
+ reason?: "drag" | undefined;
277
+ } & {
278
+ event: PointerEvent;
279
+ }) | ({
280
+ reason?: "handleClickOnLastSnapPoint" | undefined;
281
+ } & {
282
+ event: MouseEvent;
283
+ })) | undefined) => void;
195
284
  closeOnInteractOutside: boolean;
196
285
  closeOnEscape: boolean;
286
+ hasAnimationDone: boolean;
287
+ closeButtonRef: (node: HTMLButtonElement | null) => void;
288
+ isCloseButtonRendered: boolean;
197
289
  };
198
290
 
199
291
  declare namespace Drawer_namespace {
200
- export { DrawerBackdrop as Backdrop, DrawerCloseButton as CloseButton, DrawerContent as Content, DrawerDescription as Description, DrawerHandle as Handle, DrawerPositioner as Positioner, DrawerRoot as Root, DrawerTitle as Title, DrawerTrigger as Trigger };
201
- export type { DrawerBackdropProps as BackdropProps, DrawerCloseButtonProps as CloseButtonProps, DrawerContentProps as ContentProps, DrawerDescriptionProps as DescriptionProps, DrawerHandleProps as HandleProps, DrawerPositionerProps as PositionerProps, DrawerRootProps as RootProps, DrawerTitleProps as TitleProps, DrawerTriggerProps as TriggerProps };
292
+ export { DrawerBackdrop as Backdrop, DrawerCloseButton as CloseButton, DrawerContent as Content, DrawerDescription as Description, DrawerHandle as Handle, DrawerHeader as Header, DrawerPositioner as Positioner, DrawerRoot as Root, DrawerTitle as Title, DrawerTrigger as Trigger };
293
+ export type { DrawerBackdropProps as BackdropProps, DrawerCloseButtonProps as CloseButtonProps, DrawerContentProps as ContentProps, DrawerDescriptionProps as DescriptionProps, DrawerHandleProps as HandleProps, DrawerHeaderProps as HeaderProps, DrawerPositionerProps as PositionerProps, DrawerRootProps as RootProps, DrawerTitleProps as TitleProps, DrawerTriggerProps as TriggerProps };
202
294
  }
203
295
 
204
- export { Drawer_namespace as Drawer, DrawerBackdrop, DrawerCloseButton, DrawerContent, DrawerDescription, DrawerHandle, DrawerPositioner, DrawerRoot, DrawerTitle, DrawerTrigger, useDrawer, useDrawerContext };
205
- export type { DrawerBackdropProps, DrawerCloseButtonProps, DrawerContentProps, DrawerContextValue, DrawerDescriptionProps, DrawerHandleProps, DrawerPositionerProps, DrawerRootProps, DrawerTitleProps, DrawerTriggerProps, UseDrawerProps };
296
+ export { Drawer_namespace as Drawer, DrawerBackdrop, DrawerCloseButton, DrawerContent, DrawerDescription, DrawerHandle, DrawerHeader, DrawerPositioner, DrawerRoot, DrawerTitle, DrawerTrigger, useDrawer, useDrawerContext };
297
+ export type { DrawerBackdropProps, DrawerCloseButtonProps, DrawerContentProps, DrawerContextValue, DrawerDescriptionProps, DrawerHandleProps, DrawerHeaderProps, DrawerPositionerProps, DrawerRootProps, DrawerTitleProps, DrawerTriggerProps, UseDrawerProps };
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { D as DrawerBackdrop, a as DrawerCloseButton, b as DrawerContent, c as DrawerDescription, d as DrawerHandle, e as DrawerPositioner, f as DrawerRoot, g as DrawerTitle, h as DrawerTrigger } from './Drawer-12s-1gup1cyj.js';
2
- export { u as useDrawer, i as useDrawerContext } from './Drawer-12s-1gup1cyj.js';
1
+ import { D as DrawerBackdrop, a as DrawerCloseButton, b as DrawerContent, c as DrawerDescription, d as DrawerHandle, e as DrawerHeader, f as DrawerPositioner, g as DrawerRoot, h as DrawerTitle, i as DrawerTrigger } from './Drawer-12s-B-JAOvwq.js';
2
+ export { u as useDrawer, j as useDrawerContext } from './Drawer-12s-B-JAOvwq.js';
3
3
 
4
4
  var Drawer_namespace = {
5
5
  __proto__: null,
@@ -8,10 +8,11 @@ var Drawer_namespace = {
8
8
  Content: DrawerContent,
9
9
  Description: DrawerDescription,
10
10
  Handle: DrawerHandle,
11
+ Header: DrawerHeader,
11
12
  Positioner: DrawerPositioner,
12
13
  Root: DrawerRoot,
13
14
  Title: DrawerTitle,
14
15
  Trigger: DrawerTrigger
15
16
  };
16
17
 
17
- export { Drawer_namespace as Drawer, DrawerBackdrop, DrawerCloseButton, DrawerContent, DrawerDescription, DrawerHandle, DrawerPositioner, DrawerRoot, DrawerTitle, DrawerTrigger };
18
+ export { Drawer_namespace as Drawer, DrawerBackdrop, DrawerCloseButton, DrawerContent, DrawerDescription, DrawerHandle, DrawerHeader, DrawerPositioner, DrawerRoot, DrawerTitle, DrawerTrigger };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seed-design/react-drawer",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/daangn/seed-design.git",
@@ -30,7 +30,7 @@
30
30
  "@radix-ui/react-compose-refs": "^1.1.2",
31
31
  "@radix-ui/react-dialog": "^1.1.15",
32
32
  "@radix-ui/react-use-callback-ref": "^1.1.0",
33
- "@radix-ui/react-use-controllable-state": "^1.2.2",
33
+ "@seed-design/react-use-controllable-state": "1.0.0",
34
34
  "@seed-design/dom-utils": "1.0.0",
35
35
  "@seed-design/react-primitive": "1.0.0"
36
36
  },
@@ -4,6 +4,7 @@ export {
4
4
  DrawerContent as Content,
5
5
  DrawerDescription as Description,
6
6
  DrawerHandle as Handle,
7
+ DrawerHeader as Header,
7
8
  DrawerPositioner as Positioner,
8
9
  DrawerRoot as Root,
9
10
  DrawerTitle as Title,
@@ -13,6 +14,7 @@ export {
13
14
  type DrawerContentProps as ContentProps,
14
15
  type DrawerDescriptionProps as DescriptionProps,
15
16
  type DrawerHandleProps as HandleProps,
17
+ type DrawerHeaderProps as HeaderProps,
16
18
  type DrawerPositionerProps as PositionerProps,
17
19
  type DrawerRootProps as RootProps,
18
20
  type DrawerTitleProps as TitleProps,
package/src/Drawer.tsx CHANGED
@@ -64,13 +64,20 @@ export interface DrawerBackdropProps
64
64
  React.HTMLAttributes<HTMLDivElement> {}
65
65
 
66
66
  export const DrawerBackdrop = forwardRef<HTMLDivElement, DrawerBackdropProps>((props, ref) => {
67
- const { overlayRef, onRelease, modal, snapPoints, isOpen, shouldFade, shouldOverlayAnimate } =
68
- useDrawerContext();
67
+ const {
68
+ overlayRef,
69
+ onRelease,
70
+ modal,
71
+ snapPoints,
72
+ isOpen,
73
+ shouldFade,
74
+ shouldOverlayAnimate,
75
+ hasAnimationDone,
76
+ } = useDrawerContext();
69
77
  const composedRef = useComposedRefs(ref, overlayRef);
70
78
  const hasSnapPoints = snapPoints && snapPoints.length > 0;
71
79
  const onMouseUp = useCallbackRef((event: React.PointerEvent<HTMLDivElement>) => onRelease(event));
72
80
 
73
- // Overlay is the component that is locking scroll, removing it will unlock the scroll without having to dig into Radix's Dialog library
74
81
  if (!modal) {
75
82
  return null;
76
83
  }
@@ -83,6 +90,7 @@ export const DrawerBackdrop = forwardRef<HTMLDivElement, DrawerBackdropProps>((p
83
90
  data-snap-points-overlay={isOpen && shouldFade ? "true" : "false"}
84
91
  data-should-overlay-animate={shouldOverlayAnimate ? "true" : "false"}
85
92
  data-open={dataAttr(isOpen)}
93
+ data-animation-done={hasAnimationDone ? "true" : "false"}
86
94
  {...props}
87
95
  />
88
96
  );
@@ -114,6 +122,7 @@ export const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>((pro
114
122
  closeOnInteractOutside,
115
123
  closeOnEscape,
116
124
  dismissible,
125
+ hasAnimationDone,
117
126
  } = useDrawerContext();
118
127
  // Needed to use transition instead of animations
119
128
  const [delayedSnapPoints, setDelayedSnapPoints] = useState(false);
@@ -170,6 +179,7 @@ export const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>((pro
170
179
  data-delayed-snap-points={delayedSnapPoints ? "true" : "false"}
171
180
  data-drawer-direction={direction}
172
181
  data-open={dataAttr(isOpen)}
182
+ data-animation-done={hasAnimationDone ? "true" : "false"}
173
183
  data-drawer=""
174
184
  data-snap-points={isOpen && hasSnapPoints ? "true" : "false"}
175
185
  data-custom-container={container ? "true" : "false"}
@@ -253,13 +263,13 @@ export const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>((pro
253
263
  onInteractOutside={(e) => {
254
264
  // Only close if event is not prevented (e.g., by onFocusOutside or onPointerDownOutside)
255
265
  if (dismissible && closeOnInteractOutside && !e.defaultPrevented) {
256
- closeDrawer();
266
+ closeDrawer(false, { reason: "interactOutside", event: e.detail.originalEvent });
257
267
  }
258
268
  props.onInteractOutside?.(e);
259
269
  }}
260
270
  onEscapeKeyDown={(e) => {
261
271
  if (dismissible && closeOnEscape) {
262
- closeDrawer();
272
+ closeDrawer(false, { reason: "escapeKeyDown", event: e });
263
273
  }
264
274
  props.onEscapeKeyDown?.(e);
265
275
  }}
@@ -276,19 +286,30 @@ export interface DrawerDescriptionProps extends DialogPrimitive.DialogDescriptio
276
286
 
277
287
  export const DrawerDescription = DialogPrimitive.Description;
278
288
 
289
+ export interface DrawerHeaderProps extends PrimitiveProps, React.HTMLAttributes<HTMLDivElement> {}
290
+
291
+ export const DrawerHeader = forwardRef<HTMLDivElement, DrawerHeaderProps>((props, ref) => {
292
+ const { isCloseButtonRendered } = useDrawerContext();
293
+ return (
294
+ <Primitive.div ref={ref} data-show-close-button={dataAttr(isCloseButtonRendered)} {...props} />
295
+ );
296
+ });
297
+ DrawerHeader.displayName = "DrawerHeader";
298
+
279
299
  export interface DrawerCloseButtonProps extends DialogPrimitive.DialogCloseProps {}
280
300
 
281
301
  export const DrawerCloseButton = forwardRef<HTMLButtonElement, DrawerCloseButtonProps>(
282
302
  (props, ref) => {
283
- const api = useDrawerContext();
303
+ const { closeButtonRef, setIsOpen } = useDrawerContext();
304
+ const composedRef = useComposedRefs(ref, closeButtonRef);
284
305
  return (
285
306
  <Primitive.button
286
- ref={ref}
307
+ ref={composedRef}
287
308
  {...props}
288
309
  onClick={(e) => {
289
310
  props.onClick?.(e);
290
311
  if (e.defaultPrevented) return;
291
- api.setIsOpen(false);
312
+ setIsOpen(false, { reason: "closeButton", event: e.nativeEvent });
292
313
  }}
293
314
  />
294
315
  );
@@ -321,18 +342,18 @@ export const DrawerHandle = forwardRef<HTMLDivElement, DrawerHandleProps>((props
321
342
  const closeTimeoutIdRef = useRef<number | null>(null);
322
343
  const shouldCancelInteractionRef = useRef(false);
323
344
 
324
- function handleStartCycle() {
345
+ function handleStartCycle(event: React.MouseEvent<HTMLDivElement>) {
325
346
  // Stop if this is the second click of a double click
326
347
  if (shouldCancelInteractionRef.current) {
327
348
  handleCancelInteraction();
328
349
  return;
329
350
  }
330
351
  window.setTimeout(() => {
331
- handleCycleSnapPoints();
352
+ handleCycleSnapPoints(event);
332
353
  }, DOUBLE_TAP_TIMEOUT);
333
354
  }
334
355
 
335
- function handleCycleSnapPoints() {
356
+ function handleCycleSnapPoints(event: React.MouseEvent<HTMLDivElement>) {
336
357
  // Prevent accidental taps while resizing drawer
337
358
  if (isDragging || preventCycle || shouldCancelInteractionRef.current) {
338
359
  handleCancelInteraction();
@@ -343,7 +364,7 @@ export const DrawerHandle = forwardRef<HTMLDivElement, DrawerHandleProps>((props
343
364
 
344
365
  if (!snapPoints || snapPoints.length === 0) {
345
366
  if (!dismissible) {
346
- closeDrawer();
367
+ closeDrawer(false, { reason: "handleClickOnLastSnapPoint", event: event.nativeEvent });
347
368
  }
348
369
  return;
349
370
  }
@@ -351,7 +372,7 @@ export const DrawerHandle = forwardRef<HTMLDivElement, DrawerHandleProps>((props
351
372
  const isLastSnapPoint = activeSnapPoint === snapPoints[snapPoints.length - 1];
352
373
 
353
374
  if (isLastSnapPoint && dismissible) {
354
- closeDrawer();
375
+ closeDrawer(false, { reason: "handleClickOnLastSnapPoint", event: event.nativeEvent });
355
376
  return;
356
377
  }
357
378
 
package/src/browser.ts CHANGED
@@ -30,6 +30,13 @@ export function isIOS(): boolean | undefined {
30
30
  return isIPhone() || isIPad();
31
31
  }
32
32
 
33
+ export function isAndroid(): boolean | undefined {
34
+ if (typeof window === "undefined" || typeof navigator === "undefined") return false;
35
+
36
+ return /Android/.test(navigator.userAgent);
37
+ }
38
+
39
+ // TODO: use userAgent instead?
33
40
  export function testPlatform(re: RegExp): boolean | undefined {
34
41
  return typeof window !== "undefined" && window.navigator != null
35
42
  ? re.test(window.navigator.platform)
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ export {
4
4
  DrawerContent,
5
5
  DrawerDescription,
6
6
  DrawerHandle,
7
+ DrawerHeader,
7
8
  DrawerPositioner,
8
9
  DrawerRoot,
9
10
  DrawerTitle,
@@ -13,6 +14,7 @@ export {
13
14
  type DrawerContentProps,
14
15
  type DrawerDescriptionProps,
15
16
  type DrawerHandleProps,
17
+ type DrawerHeaderProps,
16
18
  type DrawerPositionerProps,
17
19
  type DrawerRootProps,
18
20
  type DrawerTitleProps,
@@ -1,4 +1,4 @@
1
- import { useControllableState } from "@radix-ui/react-use-controllable-state";
1
+ import { useControllableState } from "@seed-design/react-use-controllable-state";
2
2
  import React from "react";
3
3
  import { TRANSITIONS, VELOCITY_THRESHOLD } from "./constants";
4
4
  import { isVertical, set } from "./helpers";
@@ -196,11 +196,13 @@ export function useSnapPoints({
196
196
  closeDrawer,
197
197
  velocity,
198
198
  dismissible,
199
+ event,
199
200
  }: {
200
201
  draggedDistance: number;
201
- closeDrawer: () => void;
202
+ closeDrawer: (fromWithin: boolean, details: { reason: "drag"; event: PointerEvent }) => void;
202
203
  velocity: number;
203
204
  dismissible: boolean;
205
+ event: PointerEvent;
204
206
  }) {
205
207
  if (fadeFromIndex === undefined) return;
206
208
 
@@ -219,7 +221,7 @@ export function useSnapPoints({
219
221
  }
220
222
 
221
223
  if (!snapToSequentialPoint && velocity > 2 && !hasDraggedUp) {
222
- if (dismissible) closeDrawer();
224
+ if (dismissible) closeDrawer(false, { reason: "drag", event });
223
225
  else snapToPoint(snapPointsOffset[0]); // snap to initial point
224
226
  return;
225
227
  }
@@ -247,7 +249,7 @@ export function useSnapPoints({
247
249
  }
248
250
 
249
251
  if (isFirst && dragDirection < 0 && dismissible) {
250
- closeDrawer();
252
+ closeDrawer(false, { reason: "drag", event });
251
253
  }
252
254
 
253
255
  if (activeSnapPointIndex === null) return;