@seed-design/react-drawer 1.0.6 → 1.0.8

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.
@@ -6,7 +6,7 @@ var reactUseCallbackRef = require('@radix-ui/react-use-callback-ref');
6
6
  var domUtils = require('@seed-design/dom-utils');
7
7
  var reactPrimitive = require('@seed-design/react-primitive');
8
8
  var React = require('react');
9
- var reactUseControllableState = require('@radix-ui/react-use-controllable-state');
9
+ var reactUseControllableState = require('@seed-design/react-use-controllable-state');
10
10
 
11
11
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
12
 
@@ -380,7 +380,7 @@ function useSnapPoints({ activeSnapPointProp, setActiveSnapPointProp, snapPoints
380
380
  snapPointsOffset,
381
381
  snapToPoint
382
382
  ]);
383
- function onRelease({ draggedDistance, closeDrawer, velocity, dismissible }) {
383
+ function onRelease({ draggedDistance, closeDrawer, velocity, dismissible, event }) {
384
384
  if (fadeFromIndex === undefined) return;
385
385
  const currentPosition = direction === "bottom" || direction === "right" ? (activeSnapPointOffset ?? 0) - draggedDistance : (activeSnapPointOffset ?? 0) + draggedDistance;
386
386
  const isOverlaySnapPoint = activeSnapPointIndex === fadeFromIndex - 1;
@@ -392,7 +392,10 @@ function useSnapPoints({ activeSnapPointProp, setActiveSnapPointProp, snapPoints
392
392
  });
393
393
  }
394
394
  if (!snapToSequentialPoint && velocity > 2 && !hasDraggedUp) {
395
- if (dismissible) closeDrawer();
395
+ if (dismissible) closeDrawer(false, {
396
+ reason: "drag",
397
+ event
398
+ });
396
399
  else snapToPoint(snapPointsOffset[0]); // snap to initial point
397
400
  return;
398
401
  }
@@ -414,7 +417,10 @@ function useSnapPoints({ activeSnapPointProp, setActiveSnapPointProp, snapPoints
414
417
  return;
415
418
  }
416
419
  if (isFirst && dragDirection < 0 && dismissible) {
417
- closeDrawer();
420
+ closeDrawer(false, {
421
+ reason: "drag",
422
+ event
423
+ });
418
424
  }
419
425
  if (activeSnapPointIndex === null) return;
420
426
  snapToPoint(snapPointsOffset[activeSnapPointIndex + dragDirection]);
@@ -484,8 +490,8 @@ function useDrawer(props) {
484
490
  const [isOpen = false, setIsOpen] = reactUseControllableState.useControllableState({
485
491
  defaultProp: defaultOpen,
486
492
  prop: openProp,
487
- onChange: (o)=>{
488
- onOpenChange?.(o);
493
+ onChange: (o, details)=>{
494
+ onOpenChange?.(o, details);
489
495
  if (!o && !nested) {
490
496
  restorePositionSetting();
491
497
  }
@@ -680,11 +686,11 @@ function useDrawer(props) {
680
686
  }, [
681
687
  isDragging
682
688
  ]);
683
- const closeDrawer = React.useCallback((fromWithin)=>{
689
+ const closeDrawer = React.useCallback((fromWithin, details)=>{
684
690
  cancelDrag();
685
691
  onClose?.();
686
692
  if (!fromWithin) {
687
- setIsOpen(false);
693
+ setIsOpen(false, details);
688
694
  }
689
695
  if (fadeFromIndex !== undefined && fadeFromIndex > 0 && activeSnapPointIndex === 0) {
690
696
  set(overlayRef.current, {
@@ -734,7 +740,8 @@ function useDrawer(props) {
734
740
  draggedDistance: distMoved * directionMultiplier,
735
741
  closeDrawer,
736
742
  velocity,
737
- dismissible
743
+ dismissible,
744
+ event: event.nativeEvent
738
745
  });
739
746
  onReleaseProp?.(event, true);
740
747
  return;
@@ -745,7 +752,10 @@ function useDrawer(props) {
745
752
  return;
746
753
  }
747
754
  if (velocity > VELOCITY_THRESHOLD) {
748
- closeDrawer();
755
+ closeDrawer(false, {
756
+ reason: "drag",
757
+ event: event.nativeEvent
758
+ });
749
759
  onReleaseProp?.(event, false);
750
760
  return;
751
761
  }
@@ -753,7 +763,10 @@ function useDrawer(props) {
753
763
  const visibleDrawerWidth = Math.min(drawerRef.current.getBoundingClientRect().width ?? 0, window.innerWidth);
754
764
  const isHorizontalSwipe = direction === "left" || direction === "right";
755
765
  if (Math.abs(swipeAmount) >= (isHorizontalSwipe ? visibleDrawerWidth : visibleDrawerHeight) * closeThreshold) {
756
- closeDrawer();
766
+ closeDrawer(false, {
767
+ reason: "drag",
768
+ event: event.nativeEvent
769
+ });
757
770
  onReleaseProp?.(event, false);
758
771
  return;
759
772
  }
@@ -1129,13 +1142,19 @@ const DrawerContent = /*#__PURE__*/ React.forwardRef((props, ref)=>{
1129
1142
  onInteractOutside: (e)=>{
1130
1143
  // Only close if event is not prevented (e.g., by onFocusOutside or onPointerDownOutside)
1131
1144
  if (dismissible && closeOnInteractOutside && !e.defaultPrevented) {
1132
- closeDrawer();
1145
+ closeDrawer(false, {
1146
+ reason: "interactOutside",
1147
+ event: e.detail.originalEvent
1148
+ });
1133
1149
  }
1134
1150
  props.onInteractOutside?.(e);
1135
1151
  },
1136
1152
  onEscapeKeyDown: (e)=>{
1137
1153
  if (dismissible && closeOnEscape) {
1138
- closeDrawer();
1154
+ closeDrawer(false, {
1155
+ reason: "escapeKeyDown",
1156
+ event: e
1157
+ });
1139
1158
  }
1140
1159
  props.onEscapeKeyDown?.(e);
1141
1160
  }
@@ -1162,7 +1181,10 @@ const DrawerCloseButton = /*#__PURE__*/ React.forwardRef((props, ref)=>{
1162
1181
  onClick: (e)=>{
1163
1182
  props.onClick?.(e);
1164
1183
  if (e.defaultPrevented) return;
1165
- setIsOpen(false);
1184
+ setIsOpen(false, {
1185
+ reason: "closeButton",
1186
+ event: e.nativeEvent
1187
+ });
1166
1188
  }
1167
1189
  });
1168
1190
  });
@@ -1173,17 +1195,17 @@ const DrawerHandle = /*#__PURE__*/ React.forwardRef((props, ref)=>{
1173
1195
  const { closeDrawer, isDragging, snapPoints, activeSnapPoint, setActiveSnapPoint, dismissible, handleOnly, isOpen, onPress, onDrag, onRelease } = useDrawerContext();
1174
1196
  const closeTimeoutIdRef = React.useRef(null);
1175
1197
  const shouldCancelInteractionRef = React.useRef(false);
1176
- function handleStartCycle() {
1198
+ function handleStartCycle(event) {
1177
1199
  // Stop if this is the second click of a double click
1178
1200
  if (shouldCancelInteractionRef.current) {
1179
1201
  handleCancelInteraction();
1180
1202
  return;
1181
1203
  }
1182
1204
  window.setTimeout(()=>{
1183
- handleCycleSnapPoints();
1205
+ handleCycleSnapPoints(event);
1184
1206
  }, DOUBLE_TAP_TIMEOUT);
1185
1207
  }
1186
- function handleCycleSnapPoints() {
1208
+ function handleCycleSnapPoints(event) {
1187
1209
  // Prevent accidental taps while resizing drawer
1188
1210
  if (isDragging || preventCycle || shouldCancelInteractionRef.current) {
1189
1211
  handleCancelInteraction();
@@ -1193,13 +1215,19 @@ const DrawerHandle = /*#__PURE__*/ React.forwardRef((props, ref)=>{
1193
1215
  handleCancelInteraction();
1194
1216
  if (!snapPoints || snapPoints.length === 0) {
1195
1217
  if (!dismissible) {
1196
- closeDrawer();
1218
+ closeDrawer(false, {
1219
+ reason: "handleClickOnLastSnapPoint",
1220
+ event: event.nativeEvent
1221
+ });
1197
1222
  }
1198
1223
  return;
1199
1224
  }
1200
1225
  const isLastSnapPoint = activeSnapPoint === snapPoints[snapPoints.length - 1];
1201
1226
  if (isLastSnapPoint && dismissible) {
1202
- closeDrawer();
1227
+ closeDrawer(false, {
1228
+ reason: "handleClickOnLastSnapPoint",
1229
+ event: event.nativeEvent
1230
+ });
1203
1231
  return;
1204
1232
  }
1205
1233
  const currentSnapIndex = snapPoints.findIndex((point)=>point === activeSnapPoint);
@@ -6,7 +6,7 @@ import { useCallbackRef } from '@radix-ui/react-use-callback-ref';
6
6
  import { dataAttr } from '@seed-design/dom-utils';
7
7
  import { Primitive } from '@seed-design/react-primitive';
8
8
  import React, { useState, useCallback, useRef, useEffect, useMemo, createContext, useContext, forwardRef } from 'react';
9
- import { useControllableState } from '@radix-ui/react-use-controllable-state';
9
+ import { useControllableState } from '@seed-design/react-use-controllable-state';
10
10
 
11
11
  function isMobileFirefox() {
12
12
  if (typeof window === "undefined" || typeof navigator === "undefined") return false;
@@ -357,7 +357,7 @@ function useSnapPoints({ activeSnapPointProp, setActiveSnapPointProp, snapPoints
357
357
  snapPointsOffset,
358
358
  snapToPoint
359
359
  ]);
360
- function onRelease({ draggedDistance, closeDrawer, velocity, dismissible }) {
360
+ function onRelease({ draggedDistance, closeDrawer, velocity, dismissible, event }) {
361
361
  if (fadeFromIndex === undefined) return;
362
362
  const currentPosition = direction === "bottom" || direction === "right" ? (activeSnapPointOffset ?? 0) - draggedDistance : (activeSnapPointOffset ?? 0) + draggedDistance;
363
363
  const isOverlaySnapPoint = activeSnapPointIndex === fadeFromIndex - 1;
@@ -369,7 +369,10 @@ function useSnapPoints({ activeSnapPointProp, setActiveSnapPointProp, snapPoints
369
369
  });
370
370
  }
371
371
  if (!snapToSequentialPoint && velocity > 2 && !hasDraggedUp) {
372
- if (dismissible) closeDrawer();
372
+ if (dismissible) closeDrawer(false, {
373
+ reason: "drag",
374
+ event
375
+ });
373
376
  else snapToPoint(snapPointsOffset[0]); // snap to initial point
374
377
  return;
375
378
  }
@@ -391,7 +394,10 @@ function useSnapPoints({ activeSnapPointProp, setActiveSnapPointProp, snapPoints
391
394
  return;
392
395
  }
393
396
  if (isFirst && dragDirection < 0 && dismissible) {
394
- closeDrawer();
397
+ closeDrawer(false, {
398
+ reason: "drag",
399
+ event
400
+ });
395
401
  }
396
402
  if (activeSnapPointIndex === null) return;
397
403
  snapToPoint(snapPointsOffset[activeSnapPointIndex + dragDirection]);
@@ -461,8 +467,8 @@ function useDrawer(props) {
461
467
  const [isOpen = false, setIsOpen] = useControllableState({
462
468
  defaultProp: defaultOpen,
463
469
  prop: openProp,
464
- onChange: (o)=>{
465
- onOpenChange?.(o);
470
+ onChange: (o, details)=>{
471
+ onOpenChange?.(o, details);
466
472
  if (!o && !nested) {
467
473
  restorePositionSetting();
468
474
  }
@@ -657,11 +663,11 @@ function useDrawer(props) {
657
663
  }, [
658
664
  isDragging
659
665
  ]);
660
- const closeDrawer = useCallback((fromWithin)=>{
666
+ const closeDrawer = useCallback((fromWithin, details)=>{
661
667
  cancelDrag();
662
668
  onClose?.();
663
669
  if (!fromWithin) {
664
- setIsOpen(false);
670
+ setIsOpen(false, details);
665
671
  }
666
672
  if (fadeFromIndex !== undefined && fadeFromIndex > 0 && activeSnapPointIndex === 0) {
667
673
  set(overlayRef.current, {
@@ -711,7 +717,8 @@ function useDrawer(props) {
711
717
  draggedDistance: distMoved * directionMultiplier,
712
718
  closeDrawer,
713
719
  velocity,
714
- dismissible
720
+ dismissible,
721
+ event: event.nativeEvent
715
722
  });
716
723
  onReleaseProp?.(event, true);
717
724
  return;
@@ -722,7 +729,10 @@ function useDrawer(props) {
722
729
  return;
723
730
  }
724
731
  if (velocity > VELOCITY_THRESHOLD) {
725
- closeDrawer();
732
+ closeDrawer(false, {
733
+ reason: "drag",
734
+ event: event.nativeEvent
735
+ });
726
736
  onReleaseProp?.(event, false);
727
737
  return;
728
738
  }
@@ -730,7 +740,10 @@ function useDrawer(props) {
730
740
  const visibleDrawerWidth = Math.min(drawerRef.current.getBoundingClientRect().width ?? 0, window.innerWidth);
731
741
  const isHorizontalSwipe = direction === "left" || direction === "right";
732
742
  if (Math.abs(swipeAmount) >= (isHorizontalSwipe ? visibleDrawerWidth : visibleDrawerHeight) * closeThreshold) {
733
- closeDrawer();
743
+ closeDrawer(false, {
744
+ reason: "drag",
745
+ event: event.nativeEvent
746
+ });
734
747
  onReleaseProp?.(event, false);
735
748
  return;
736
749
  }
@@ -1106,13 +1119,19 @@ const DrawerContent = /*#__PURE__*/ forwardRef((props, ref)=>{
1106
1119
  onInteractOutside: (e)=>{
1107
1120
  // Only close if event is not prevented (e.g., by onFocusOutside or onPointerDownOutside)
1108
1121
  if (dismissible && closeOnInteractOutside && !e.defaultPrevented) {
1109
- closeDrawer();
1122
+ closeDrawer(false, {
1123
+ reason: "interactOutside",
1124
+ event: e.detail.originalEvent
1125
+ });
1110
1126
  }
1111
1127
  props.onInteractOutside?.(e);
1112
1128
  },
1113
1129
  onEscapeKeyDown: (e)=>{
1114
1130
  if (dismissible && closeOnEscape) {
1115
- closeDrawer();
1131
+ closeDrawer(false, {
1132
+ reason: "escapeKeyDown",
1133
+ event: e
1134
+ });
1116
1135
  }
1117
1136
  props.onEscapeKeyDown?.(e);
1118
1137
  }
@@ -1139,7 +1158,10 @@ const DrawerCloseButton = /*#__PURE__*/ forwardRef((props, ref)=>{
1139
1158
  onClick: (e)=>{
1140
1159
  props.onClick?.(e);
1141
1160
  if (e.defaultPrevented) return;
1142
- setIsOpen(false);
1161
+ setIsOpen(false, {
1162
+ reason: "closeButton",
1163
+ event: e.nativeEvent
1164
+ });
1143
1165
  }
1144
1166
  });
1145
1167
  });
@@ -1150,17 +1172,17 @@ const DrawerHandle = /*#__PURE__*/ forwardRef((props, ref)=>{
1150
1172
  const { closeDrawer, isDragging, snapPoints, activeSnapPoint, setActiveSnapPoint, dismissible, handleOnly, isOpen, onPress, onDrag, onRelease } = useDrawerContext();
1151
1173
  const closeTimeoutIdRef = useRef(null);
1152
1174
  const shouldCancelInteractionRef = useRef(false);
1153
- function handleStartCycle() {
1175
+ function handleStartCycle(event) {
1154
1176
  // Stop if this is the second click of a double click
1155
1177
  if (shouldCancelInteractionRef.current) {
1156
1178
  handleCancelInteraction();
1157
1179
  return;
1158
1180
  }
1159
1181
  window.setTimeout(()=>{
1160
- handleCycleSnapPoints();
1182
+ handleCycleSnapPoints(event);
1161
1183
  }, DOUBLE_TAP_TIMEOUT);
1162
1184
  }
1163
- function handleCycleSnapPoints() {
1185
+ function handleCycleSnapPoints(event) {
1164
1186
  // Prevent accidental taps while resizing drawer
1165
1187
  if (isDragging || preventCycle || shouldCancelInteractionRef.current) {
1166
1188
  handleCancelInteraction();
@@ -1170,13 +1192,19 @@ const DrawerHandle = /*#__PURE__*/ forwardRef((props, ref)=>{
1170
1192
  handleCancelInteraction();
1171
1193
  if (!snapPoints || snapPoints.length === 0) {
1172
1194
  if (!dismissible) {
1173
- closeDrawer();
1195
+ closeDrawer(false, {
1196
+ reason: "handleClickOnLastSnapPoint",
1197
+ event: event.nativeEvent
1198
+ });
1174
1199
  }
1175
1200
  return;
1176
1201
  }
1177
1202
  const isLastSnapPoint = activeSnapPoint === snapPoints[snapPoints.length - 1];
1178
1203
  if (isLastSnapPoint && dismissible) {
1179
- closeDrawer();
1204
+ closeDrawer(false, {
1205
+ reason: "handleClickOnLastSnapPoint",
1206
+ event: event.nativeEvent
1207
+ });
1180
1208
  return;
1181
1209
  }
1182
1210
  const currentSnapIndex = snapPoints.findIndex((point)=>point === activeSnapPoint);
package/lib/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- var Drawer12s = require('./Drawer-12s-DriAYAR2.cjs');
1
+ var Drawer12s = require('./Drawer-12s-DXUv7jE4.cjs');
2
2
 
3
3
  var Drawer_namespace = {
4
4
  __proto__: null,
package/lib/index.d.ts CHANGED
@@ -2,11 +2,34 @@ 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
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$1.SetStateAction<string | number | null>) => void;
110
- drawerRef: React$1.RefObject<HTMLDivElement | null>;
111
- overlayRef: React$1.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$1.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,8 +151,8 @@ declare function useDrawer(props: UseDrawerProps): {
128
151
  noBodyStyles: boolean;
129
152
  container: HTMLElement | null | undefined;
130
153
  autoFocus: boolean;
131
- setHasBeenOpened: React$1.Dispatch<React$1.SetStateAction<boolean>>;
132
- setIsOpen: (value: React$1.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;
135
158
  hasAnimationDone: boolean;
@@ -174,11 +197,31 @@ type DrawerContextValue = ReturnType<typeof useDrawer>;
174
197
  declare function useDrawerContext(): {
175
198
  activeSnapPoint: string | number | null;
176
199
  snapPoints: (string | number)[] | undefined;
177
- setActiveSnapPoint: (value: React$1.SetStateAction<string | number | null>) => void;
200
+ setActiveSnapPoint: (value: string | number | ((prev: string | number | null) => string | number | null) | null, details?: undefined) => void;
178
201
  drawerRef: React$1.RefObject<HTMLDivElement | null>;
179
202
  overlayRef: React$1.RefObject<HTMLDivElement | null>;
180
203
  shouldOverlayAnimate: boolean;
181
- 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;
182
225
  onPress: (event: React.PointerEvent<HTMLDivElement>) => void;
183
226
  onRelease: (event: React.PointerEvent<HTMLDivElement> | null) => void;
184
227
  onDrag: (event: React.PointerEvent<HTMLDivElement>) => void;
@@ -187,7 +230,27 @@ declare function useDrawerContext(): {
187
230
  isOpen: boolean;
188
231
  isDragging: boolean;
189
232
  shouldFade: boolean;
190
- closeDrawer: (fromWithin?: boolean) => void;
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;
191
254
  keyboardIsOpen: React$1.RefObject<boolean>;
192
255
  modal: boolean;
193
256
  snapPointsOffset: number[];
@@ -197,7 +260,27 @@ declare function useDrawerContext(): {
197
260
  container: HTMLElement | null | undefined;
198
261
  autoFocus: boolean;
199
262
  setHasBeenOpened: React$1.Dispatch<React$1.SetStateAction<boolean>>;
200
- setIsOpen: (value: React$1.SetStateAction<boolean>) => void;
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;
201
284
  closeOnInteractOutside: boolean;
202
285
  closeOnEscape: boolean;
203
286
  hasAnimationDone: boolean;
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 DrawerHeader, f as DrawerPositioner, g as DrawerRoot, h as DrawerTitle, i as DrawerTrigger } from './Drawer-12s-BdeYHia6.js';
2
- export { u as useDrawer, j as useDrawerContext } from './Drawer-12s-BdeYHia6.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-DnXlK1K0.js';
2
+ export { u as useDrawer, j as useDrawerContext } from './Drawer-12s-DnXlK1K0.js';
3
3
 
4
4
  var Drawer_namespace = {
5
5
  __proto__: null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seed-design/react-drawer",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
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
  },
package/src/Drawer.tsx CHANGED
@@ -263,13 +263,13 @@ export const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>((pro
263
263
  onInteractOutside={(e) => {
264
264
  // Only close if event is not prevented (e.g., by onFocusOutside or onPointerDownOutside)
265
265
  if (dismissible && closeOnInteractOutside && !e.defaultPrevented) {
266
- closeDrawer();
266
+ closeDrawer(false, { reason: "interactOutside", event: e.detail.originalEvent });
267
267
  }
268
268
  props.onInteractOutside?.(e);
269
269
  }}
270
270
  onEscapeKeyDown={(e) => {
271
271
  if (dismissible && closeOnEscape) {
272
- closeDrawer();
272
+ closeDrawer(false, { reason: "escapeKeyDown", event: e });
273
273
  }
274
274
  props.onEscapeKeyDown?.(e);
275
275
  }}
@@ -309,7 +309,7 @@ export const DrawerCloseButton = forwardRef<HTMLButtonElement, DrawerCloseButton
309
309
  onClick={(e) => {
310
310
  props.onClick?.(e);
311
311
  if (e.defaultPrevented) return;
312
- setIsOpen(false);
312
+ setIsOpen(false, { reason: "closeButton", event: e.nativeEvent });
313
313
  }}
314
314
  />
315
315
  );
@@ -342,18 +342,18 @@ export const DrawerHandle = forwardRef<HTMLDivElement, DrawerHandleProps>((props
342
342
  const closeTimeoutIdRef = useRef<number | null>(null);
343
343
  const shouldCancelInteractionRef = useRef(false);
344
344
 
345
- function handleStartCycle() {
345
+ function handleStartCycle(event: React.MouseEvent<HTMLDivElement>) {
346
346
  // Stop if this is the second click of a double click
347
347
  if (shouldCancelInteractionRef.current) {
348
348
  handleCancelInteraction();
349
349
  return;
350
350
  }
351
351
  window.setTimeout(() => {
352
- handleCycleSnapPoints();
352
+ handleCycleSnapPoints(event);
353
353
  }, DOUBLE_TAP_TIMEOUT);
354
354
  }
355
355
 
356
- function handleCycleSnapPoints() {
356
+ function handleCycleSnapPoints(event: React.MouseEvent<HTMLDivElement>) {
357
357
  // Prevent accidental taps while resizing drawer
358
358
  if (isDragging || preventCycle || shouldCancelInteractionRef.current) {
359
359
  handleCancelInteraction();
@@ -364,7 +364,7 @@ export const DrawerHandle = forwardRef<HTMLDivElement, DrawerHandleProps>((props
364
364
 
365
365
  if (!snapPoints || snapPoints.length === 0) {
366
366
  if (!dismissible) {
367
- closeDrawer();
367
+ closeDrawer(false, { reason: "handleClickOnLastSnapPoint", event: event.nativeEvent });
368
368
  }
369
369
  return;
370
370
  }
@@ -372,7 +372,7 @@ export const DrawerHandle = forwardRef<HTMLDivElement, DrawerHandleProps>((props
372
372
  const isLastSnapPoint = activeSnapPoint === snapPoints[snapPoints.length - 1];
373
373
 
374
374
  if (isLastSnapPoint && dismissible) {
375
- closeDrawer();
375
+ closeDrawer(false, { reason: "handleClickOnLastSnapPoint", event: event.nativeEvent });
376
376
  return;
377
377
  }
378
378
 
@@ -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;
package/src/useDrawer.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { useControllableState } from "@radix-ui/react-use-controllable-state";
1
+ import { useControllableState } from "@seed-design/react-use-controllable-state";
2
+ import type React from "react";
2
3
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
4
  import { isIOS, isMobileFirefox } from "./browser";
4
5
  import {
@@ -13,6 +14,21 @@ import { dampenValue, getTranslate, isInput, isVertical, reset, set } from "./he
13
14
  import { usePositionFixed } from "./use-position-fixed";
14
15
  import { useSnapPoints } from "./use-snap-points";
15
16
 
17
+ interface DrawerReasonToDetailMap {
18
+ // we might add synthetic events later if needed; currently we aim consistency; DismissableLayer gives us native events
19
+ closeButton: { event: MouseEvent };
20
+ escapeKeyDown: { event: KeyboardEvent };
21
+ interactOutside: { event: PointerEvent | FocusEvent };
22
+ drag: { event: PointerEvent };
23
+ handleClickOnLastSnapPoint: { event: MouseEvent };
24
+ }
25
+
26
+ type DrawerChangeDetails = {
27
+ [R in keyof DrawerReasonToDetailMap]: {
28
+ reason?: R;
29
+ } & DrawerReasonToDetailMap[R];
30
+ }[keyof DrawerReasonToDetailMap];
31
+
16
32
  export interface UseDrawerProps {
17
33
  activeSnapPoint?: number | string | null;
18
34
  setActiveSnapPoint?: (snapPoint: number | string | null) => void;
@@ -29,7 +45,7 @@ export interface UseDrawerProps {
29
45
  * @default true
30
46
  */
31
47
  noBodyStyles?: boolean;
32
- onOpenChange?: (open: boolean) => void;
48
+ onOpenChange?: (open: boolean, details?: DrawerChangeDetails) => void;
33
49
  /**
34
50
  * Duration for which the drawer is not draggable after scrolling content inside of the drawer.
35
51
  * @default 500ms
@@ -149,11 +165,11 @@ export function useDrawer(props: UseDrawerProps) {
149
165
  closeOnEscape = true,
150
166
  } = props;
151
167
 
152
- const [isOpen = false, setIsOpen] = useControllableState({
168
+ const [isOpen = false, setIsOpen] = useControllableState<boolean, DrawerChangeDetails>({
153
169
  defaultProp: defaultOpen,
154
170
  prop: openProp,
155
- onChange: (o: boolean) => {
156
- onOpenChange?.(o);
171
+ onChange: (o: boolean, details?: DrawerChangeDetails) => {
172
+ onOpenChange?.(o, details);
157
173
 
158
174
  if (!o && !nested) {
159
175
  restorePositionSetting();
@@ -425,12 +441,12 @@ export function useDrawer(props: UseDrawerProps) {
425
441
  }, [isDragging]);
426
442
 
427
443
  const closeDrawer = useCallback(
428
- (fromWithin?: boolean) => {
444
+ (fromWithin?: boolean, details?: DrawerChangeDetails) => {
429
445
  cancelDrag();
430
446
  onClose?.();
431
447
 
432
448
  if (!fromWithin) {
433
- setIsOpen(false);
449
+ setIsOpen(false, details);
434
450
  }
435
451
 
436
452
  if (fadeFromIndex !== undefined && fadeFromIndex > 0 && activeSnapPointIndex === 0) {
@@ -495,6 +511,7 @@ export function useDrawer(props: UseDrawerProps) {
495
511
  closeDrawer,
496
512
  velocity,
497
513
  dismissible,
514
+ event: event.nativeEvent,
498
515
  });
499
516
  onReleaseProp?.(event, true);
500
517
  return;
@@ -507,7 +524,7 @@ export function useDrawer(props: UseDrawerProps) {
507
524
  }
508
525
 
509
526
  if (velocity > VELOCITY_THRESHOLD) {
510
- closeDrawer();
527
+ closeDrawer(false, { reason: "drag", event: event.nativeEvent });
511
528
  onReleaseProp?.(event, false);
512
529
  return;
513
530
  }
@@ -526,7 +543,7 @@ export function useDrawer(props: UseDrawerProps) {
526
543
  Math.abs(swipeAmount) >=
527
544
  (isHorizontalSwipe ? visibleDrawerWidth : visibleDrawerHeight) * closeThreshold
528
545
  ) {
529
- closeDrawer();
546
+ closeDrawer(false, { reason: "drag", event: event.nativeEvent });
530
547
  onReleaseProp?.(event, false);
531
548
  return;
532
549
  }