@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/{Drawer-12s-1gup1cyj.js → Drawer-12s-B-JAOvwq.js} +103 -29
- package/lib/{Drawer-12s-L4KZ1h_g.cjs → Drawer-12s-B35ZJcKm.cjs} +102 -27
- package/lib/index.cjs +3 -1
- package/lib/index.d.ts +133 -41
- package/lib/index.js +4 -3
- package/package.json +2 -2
- package/src/Drawer.namespace.ts +2 -0
- package/src/Drawer.tsx +34 -13
- package/src/browser.ts +7 -0
- package/src/index.ts +2 -0
- package/src/use-snap-points.ts +6 -4
- package/src/useDrawer.ts +60 -11
|
@@ -5,8 +5,8 @@ import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
|
5
5
|
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
|
-
import React, { useState,
|
|
9
|
-
import { useControllableState } from '@
|
|
8
|
+
import React, { useState, useCallback, useRef, useEffect, useMemo, createContext, useContext, forwardRef } from 'react';
|
|
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;
|
|
@@ -28,6 +28,11 @@ function isIPad() {
|
|
|
28
28
|
function isIOS() {
|
|
29
29
|
return isIPhone() || isIPad();
|
|
30
30
|
}
|
|
31
|
+
function isAndroid() {
|
|
32
|
+
if (typeof window === "undefined" || typeof navigator === "undefined") return false;
|
|
33
|
+
return /Android/.test(navigator.userAgent);
|
|
34
|
+
}
|
|
35
|
+
// TODO: use userAgent instead?
|
|
31
36
|
function testPlatform(re) {
|
|
32
37
|
return typeof window !== "undefined" && window.navigator != null ? re.test(window.navigator.platform) : undefined;
|
|
33
38
|
}
|
|
@@ -357,7 +362,7 @@ function useSnapPoints({ activeSnapPointProp, setActiveSnapPointProp, snapPoints
|
|
|
357
362
|
snapPointsOffset,
|
|
358
363
|
snapToPoint
|
|
359
364
|
]);
|
|
360
|
-
function onRelease({ draggedDistance, closeDrawer, velocity, dismissible }) {
|
|
365
|
+
function onRelease({ draggedDistance, closeDrawer, velocity, dismissible, event }) {
|
|
361
366
|
if (fadeFromIndex === undefined) return;
|
|
362
367
|
const currentPosition = direction === "bottom" || direction === "right" ? (activeSnapPointOffset ?? 0) - draggedDistance : (activeSnapPointOffset ?? 0) + draggedDistance;
|
|
363
368
|
const isOverlaySnapPoint = activeSnapPointIndex === fadeFromIndex - 1;
|
|
@@ -369,7 +374,10 @@ function useSnapPoints({ activeSnapPointProp, setActiveSnapPointProp, snapPoints
|
|
|
369
374
|
});
|
|
370
375
|
}
|
|
371
376
|
if (!snapToSequentialPoint && velocity > 2 && !hasDraggedUp) {
|
|
372
|
-
if (dismissible) closeDrawer(
|
|
377
|
+
if (dismissible) closeDrawer(false, {
|
|
378
|
+
reason: "drag",
|
|
379
|
+
event
|
|
380
|
+
});
|
|
373
381
|
else snapToPoint(snapPointsOffset[0]); // snap to initial point
|
|
374
382
|
return;
|
|
375
383
|
}
|
|
@@ -391,7 +399,10 @@ function useSnapPoints({ activeSnapPointProp, setActiveSnapPointProp, snapPoints
|
|
|
391
399
|
return;
|
|
392
400
|
}
|
|
393
401
|
if (isFirst && dragDirection < 0 && dismissible) {
|
|
394
|
-
closeDrawer(
|
|
402
|
+
closeDrawer(false, {
|
|
403
|
+
reason: "drag",
|
|
404
|
+
event
|
|
405
|
+
});
|
|
395
406
|
}
|
|
396
407
|
if (activeSnapPointIndex === null) return;
|
|
397
408
|
snapToPoint(snapPointsOffset[activeSnapPointIndex + dragDirection]);
|
|
@@ -461,8 +472,8 @@ function useDrawer(props) {
|
|
|
461
472
|
const [isOpen = false, setIsOpen] = useControllableState({
|
|
462
473
|
defaultProp: defaultOpen,
|
|
463
474
|
prop: openProp,
|
|
464
|
-
onChange: (o)=>{
|
|
465
|
-
onOpenChange?.(o);
|
|
475
|
+
onChange: (o, details)=>{
|
|
476
|
+
onOpenChange?.(o, details);
|
|
466
477
|
if (!o && !nested) {
|
|
467
478
|
restorePositionSetting();
|
|
468
479
|
}
|
|
@@ -482,8 +493,13 @@ function useDrawer(props) {
|
|
|
482
493
|
}
|
|
483
494
|
});
|
|
484
495
|
const [hasBeenOpened, setHasBeenOpened] = useState(false);
|
|
496
|
+
const [hasAnimationDone, setHasAnimationDone] = useState(false);
|
|
485
497
|
const [isDragging, setIsDragging] = useState(false);
|
|
486
498
|
const [shouldOverlayAnimate, setShouldOverlayAnimate] = useState(false);
|
|
499
|
+
const [isCloseButtonRendered, setIsCloseButtonRendered] = useState(false);
|
|
500
|
+
const closeButtonRef = useCallback((node)=>{
|
|
501
|
+
setIsCloseButtonRendered(!!node);
|
|
502
|
+
}, []);
|
|
487
503
|
const overlayRef = useRef(null);
|
|
488
504
|
const openTime = useRef(null);
|
|
489
505
|
const dragStartTime = useRef(null);
|
|
@@ -652,11 +668,11 @@ function useDrawer(props) {
|
|
|
652
668
|
}, [
|
|
653
669
|
isDragging
|
|
654
670
|
]);
|
|
655
|
-
const closeDrawer = useCallback((fromWithin)=>{
|
|
671
|
+
const closeDrawer = useCallback((fromWithin, details)=>{
|
|
656
672
|
cancelDrag();
|
|
657
673
|
onClose?.();
|
|
658
674
|
if (!fromWithin) {
|
|
659
|
-
setIsOpen(false);
|
|
675
|
+
setIsOpen(false, details);
|
|
660
676
|
}
|
|
661
677
|
if (fadeFromIndex !== undefined && fadeFromIndex > 0 && activeSnapPointIndex === 0) {
|
|
662
678
|
set(overlayRef.current, {
|
|
@@ -706,7 +722,8 @@ function useDrawer(props) {
|
|
|
706
722
|
draggedDistance: distMoved * directionMultiplier,
|
|
707
723
|
closeDrawer,
|
|
708
724
|
velocity,
|
|
709
|
-
dismissible
|
|
725
|
+
dismissible,
|
|
726
|
+
event: event.nativeEvent
|
|
710
727
|
});
|
|
711
728
|
onReleaseProp?.(event, true);
|
|
712
729
|
return;
|
|
@@ -717,7 +734,10 @@ function useDrawer(props) {
|
|
|
717
734
|
return;
|
|
718
735
|
}
|
|
719
736
|
if (velocity > VELOCITY_THRESHOLD) {
|
|
720
|
-
closeDrawer(
|
|
737
|
+
closeDrawer(false, {
|
|
738
|
+
reason: "drag",
|
|
739
|
+
event: event.nativeEvent
|
|
740
|
+
});
|
|
721
741
|
onReleaseProp?.(event, false);
|
|
722
742
|
return;
|
|
723
743
|
}
|
|
@@ -725,7 +745,10 @@ function useDrawer(props) {
|
|
|
725
745
|
const visibleDrawerWidth = Math.min(drawerRef.current.getBoundingClientRect().width ?? 0, window.innerWidth);
|
|
726
746
|
const isHorizontalSwipe = direction === "left" || direction === "right";
|
|
727
747
|
if (Math.abs(swipeAmount) >= (isHorizontalSwipe ? visibleDrawerWidth : visibleDrawerHeight) * closeThreshold) {
|
|
728
|
-
closeDrawer(
|
|
748
|
+
closeDrawer(false, {
|
|
749
|
+
reason: "drag",
|
|
750
|
+
event: event.nativeEvent
|
|
751
|
+
});
|
|
729
752
|
onReleaseProp?.(event, false);
|
|
730
753
|
return;
|
|
731
754
|
}
|
|
@@ -778,7 +801,7 @@ function useDrawer(props) {
|
|
|
778
801
|
} else {
|
|
779
802
|
drawerRef.current.style.height = `${Math.max(newDrawerHeight, visualViewportHeight - offsetFromTop)}px`;
|
|
780
803
|
}
|
|
781
|
-
} else if (!isMobileFirefox()) {
|
|
804
|
+
} else if (!isMobileFirefox() && !isAndroid()) {
|
|
782
805
|
drawerRef.current.style.height = `${initialDrawerHeight.current}px`;
|
|
783
806
|
}
|
|
784
807
|
if (snapPoints && snapPoints.length > 0 && !keyboardIsOpen.current) {
|
|
@@ -806,6 +829,7 @@ function useDrawer(props) {
|
|
|
806
829
|
}, [
|
|
807
830
|
modal
|
|
808
831
|
]);
|
|
832
|
+
// Effect 1: Track drawer open state
|
|
809
833
|
useEffect(()=>{
|
|
810
834
|
if (isOpen) {
|
|
811
835
|
setHasBeenOpened(true);
|
|
@@ -813,6 +837,24 @@ function useDrawer(props) {
|
|
|
813
837
|
}, [
|
|
814
838
|
isOpen
|
|
815
839
|
]);
|
|
840
|
+
// Effect 2: Handle animation state and timer
|
|
841
|
+
useEffect(()=>{
|
|
842
|
+
if (isOpen) {
|
|
843
|
+
// Only reset animation state if this is the first open
|
|
844
|
+
if (!hasBeenOpened) {
|
|
845
|
+
setHasAnimationDone(false);
|
|
846
|
+
}
|
|
847
|
+
const timeoutId = setTimeout(()=>{
|
|
848
|
+
setHasAnimationDone(true);
|
|
849
|
+
}, TRANSITIONS.ENTER_DURATION * 1000);
|
|
850
|
+
return ()=>clearTimeout(timeoutId);
|
|
851
|
+
}
|
|
852
|
+
// Reset animation state when drawer closes
|
|
853
|
+
setHasAnimationDone(false);
|
|
854
|
+
}, [
|
|
855
|
+
isOpen,
|
|
856
|
+
hasBeenOpened
|
|
857
|
+
]);
|
|
816
858
|
useEffect(()=>{
|
|
817
859
|
if (isOpen && snapPoints && fadeFromIndex === 0) {
|
|
818
860
|
setShouldOverlayAnimate(true);
|
|
@@ -855,7 +897,10 @@ function useDrawer(props) {
|
|
|
855
897
|
setHasBeenOpened,
|
|
856
898
|
setIsOpen,
|
|
857
899
|
closeOnInteractOutside,
|
|
858
|
-
closeOnEscape
|
|
900
|
+
closeOnEscape,
|
|
901
|
+
hasAnimationDone,
|
|
902
|
+
closeButtonRef,
|
|
903
|
+
isCloseButtonRendered
|
|
859
904
|
}), [
|
|
860
905
|
activeSnapPoint,
|
|
861
906
|
snapPoints,
|
|
@@ -880,7 +925,10 @@ function useDrawer(props) {
|
|
|
880
925
|
closeOnEscape,
|
|
881
926
|
onRelease,
|
|
882
927
|
onDrag,
|
|
883
|
-
onPress
|
|
928
|
+
onPress,
|
|
929
|
+
hasAnimationDone,
|
|
930
|
+
closeButtonRef,
|
|
931
|
+
isCloseButtonRendered
|
|
884
932
|
]);
|
|
885
933
|
}
|
|
886
934
|
|
|
@@ -930,11 +978,10 @@ const DrawerPositioner = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
930
978
|
});
|
|
931
979
|
DrawerPositioner.displayName = "DrawerPositioner";
|
|
932
980
|
const DrawerBackdrop = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
933
|
-
const { overlayRef, onRelease, modal, snapPoints, isOpen, shouldFade, shouldOverlayAnimate } = useDrawerContext();
|
|
981
|
+
const { overlayRef, onRelease, modal, snapPoints, isOpen, shouldFade, shouldOverlayAnimate, hasAnimationDone } = useDrawerContext();
|
|
934
982
|
const composedRef = useComposedRefs(ref, overlayRef);
|
|
935
983
|
const hasSnapPoints = snapPoints && snapPoints.length > 0;
|
|
936
984
|
const onMouseUp = useCallbackRef((event)=>onRelease(event));
|
|
937
|
-
// Overlay is the component that is locking scroll, removing it will unlock the scroll without having to dig into Radix's Dialog library
|
|
938
985
|
if (!modal) {
|
|
939
986
|
return null;
|
|
940
987
|
}
|
|
@@ -945,13 +992,14 @@ const DrawerBackdrop = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
945
992
|
"data-snap-points-overlay": isOpen && shouldFade ? "true" : "false",
|
|
946
993
|
"data-should-overlay-animate": shouldOverlayAnimate ? "true" : "false",
|
|
947
994
|
"data-open": dataAttr(isOpen),
|
|
995
|
+
"data-animation-done": hasAnimationDone ? "true" : "false",
|
|
948
996
|
...props
|
|
949
997
|
});
|
|
950
998
|
});
|
|
951
999
|
DrawerBackdrop.displayName = "DrawerBackdrop";
|
|
952
1000
|
const DrawerContent = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
953
1001
|
const { onPointerDownOutside, style, onOpenAutoFocus, ...restProps } = props;
|
|
954
|
-
const { drawerRef, onPress, onRelease, onDrag, keyboardIsOpen, snapPointsOffset, activeSnapPointIndex, modal, isOpen, direction, snapPoints, container, handleOnly, autoFocus, closeDrawer, closeOnInteractOutside, closeOnEscape, dismissible } = useDrawerContext();
|
|
1002
|
+
const { drawerRef, onPress, onRelease, onDrag, keyboardIsOpen, snapPointsOffset, activeSnapPointIndex, modal, isOpen, direction, snapPoints, container, handleOnly, autoFocus, closeDrawer, closeOnInteractOutside, closeOnEscape, dismissible, hasAnimationDone } = useDrawerContext();
|
|
955
1003
|
// Needed to use transition instead of animations
|
|
956
1004
|
const [delayedSnapPoints, setDelayedSnapPoints] = useState(false);
|
|
957
1005
|
const composedRef = useComposedRefs(ref, drawerRef);
|
|
@@ -998,6 +1046,7 @@ const DrawerContent = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
998
1046
|
"data-delayed-snap-points": delayedSnapPoints ? "true" : "false",
|
|
999
1047
|
"data-drawer-direction": direction,
|
|
1000
1048
|
"data-open": dataAttr(isOpen),
|
|
1049
|
+
"data-animation-done": hasAnimationDone ? "true" : "false",
|
|
1001
1050
|
"data-drawer": "",
|
|
1002
1051
|
"data-snap-points": isOpen && hasSnapPoints ? "true" : "false",
|
|
1003
1052
|
"data-custom-container": container ? "true" : "false",
|
|
@@ -1075,13 +1124,19 @@ const DrawerContent = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
1075
1124
|
onInteractOutside: (e)=>{
|
|
1076
1125
|
// Only close if event is not prevented (e.g., by onFocusOutside or onPointerDownOutside)
|
|
1077
1126
|
if (dismissible && closeOnInteractOutside && !e.defaultPrevented) {
|
|
1078
|
-
closeDrawer(
|
|
1127
|
+
closeDrawer(false, {
|
|
1128
|
+
reason: "interactOutside",
|
|
1129
|
+
event: e.detail.originalEvent
|
|
1130
|
+
});
|
|
1079
1131
|
}
|
|
1080
1132
|
props.onInteractOutside?.(e);
|
|
1081
1133
|
},
|
|
1082
1134
|
onEscapeKeyDown: (e)=>{
|
|
1083
1135
|
if (dismissible && closeOnEscape) {
|
|
1084
|
-
closeDrawer(
|
|
1136
|
+
closeDrawer(false, {
|
|
1137
|
+
reason: "escapeKeyDown",
|
|
1138
|
+
event: e
|
|
1139
|
+
});
|
|
1085
1140
|
}
|
|
1086
1141
|
props.onEscapeKeyDown?.(e);
|
|
1087
1142
|
}
|
|
@@ -1090,15 +1145,28 @@ const DrawerContent = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
1090
1145
|
DrawerContent.displayName = "DrawerContent";
|
|
1091
1146
|
const DrawerTitle = DialogPrimitive.Title;
|
|
1092
1147
|
const DrawerDescription = DialogPrimitive.Description;
|
|
1148
|
+
const DrawerHeader = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
1149
|
+
const { isCloseButtonRendered } = useDrawerContext();
|
|
1150
|
+
return /*#__PURE__*/ jsx(Primitive.div, {
|
|
1151
|
+
ref: ref,
|
|
1152
|
+
"data-show-close-button": dataAttr(isCloseButtonRendered),
|
|
1153
|
+
...props
|
|
1154
|
+
});
|
|
1155
|
+
});
|
|
1156
|
+
DrawerHeader.displayName = "DrawerHeader";
|
|
1093
1157
|
const DrawerCloseButton = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
1094
|
-
const
|
|
1158
|
+
const { closeButtonRef, setIsOpen } = useDrawerContext();
|
|
1159
|
+
const composedRef = useComposedRefs(ref, closeButtonRef);
|
|
1095
1160
|
return /*#__PURE__*/ jsx(Primitive.button, {
|
|
1096
|
-
ref:
|
|
1161
|
+
ref: composedRef,
|
|
1097
1162
|
...props,
|
|
1098
1163
|
onClick: (e)=>{
|
|
1099
1164
|
props.onClick?.(e);
|
|
1100
1165
|
if (e.defaultPrevented) return;
|
|
1101
|
-
|
|
1166
|
+
setIsOpen(false, {
|
|
1167
|
+
reason: "closeButton",
|
|
1168
|
+
event: e.nativeEvent
|
|
1169
|
+
});
|
|
1102
1170
|
}
|
|
1103
1171
|
});
|
|
1104
1172
|
});
|
|
@@ -1109,17 +1177,17 @@ const DrawerHandle = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
1109
1177
|
const { closeDrawer, isDragging, snapPoints, activeSnapPoint, setActiveSnapPoint, dismissible, handleOnly, isOpen, onPress, onDrag, onRelease } = useDrawerContext();
|
|
1110
1178
|
const closeTimeoutIdRef = useRef(null);
|
|
1111
1179
|
const shouldCancelInteractionRef = useRef(false);
|
|
1112
|
-
function handleStartCycle() {
|
|
1180
|
+
function handleStartCycle(event) {
|
|
1113
1181
|
// Stop if this is the second click of a double click
|
|
1114
1182
|
if (shouldCancelInteractionRef.current) {
|
|
1115
1183
|
handleCancelInteraction();
|
|
1116
1184
|
return;
|
|
1117
1185
|
}
|
|
1118
1186
|
window.setTimeout(()=>{
|
|
1119
|
-
handleCycleSnapPoints();
|
|
1187
|
+
handleCycleSnapPoints(event);
|
|
1120
1188
|
}, DOUBLE_TAP_TIMEOUT);
|
|
1121
1189
|
}
|
|
1122
|
-
function handleCycleSnapPoints() {
|
|
1190
|
+
function handleCycleSnapPoints(event) {
|
|
1123
1191
|
// Prevent accidental taps while resizing drawer
|
|
1124
1192
|
if (isDragging || preventCycle || shouldCancelInteractionRef.current) {
|
|
1125
1193
|
handleCancelInteraction();
|
|
@@ -1129,13 +1197,19 @@ const DrawerHandle = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
1129
1197
|
handleCancelInteraction();
|
|
1130
1198
|
if (!snapPoints || snapPoints.length === 0) {
|
|
1131
1199
|
if (!dismissible) {
|
|
1132
|
-
closeDrawer(
|
|
1200
|
+
closeDrawer(false, {
|
|
1201
|
+
reason: "handleClickOnLastSnapPoint",
|
|
1202
|
+
event: event.nativeEvent
|
|
1203
|
+
});
|
|
1133
1204
|
}
|
|
1134
1205
|
return;
|
|
1135
1206
|
}
|
|
1136
1207
|
const isLastSnapPoint = activeSnapPoint === snapPoints[snapPoints.length - 1];
|
|
1137
1208
|
if (isLastSnapPoint && dismissible) {
|
|
1138
|
-
closeDrawer(
|
|
1209
|
+
closeDrawer(false, {
|
|
1210
|
+
reason: "handleClickOnLastSnapPoint",
|
|
1211
|
+
event: event.nativeEvent
|
|
1212
|
+
});
|
|
1139
1213
|
return;
|
|
1140
1214
|
}
|
|
1141
1215
|
const currentSnapIndex = snapPoints.findIndex((point)=>point === activeSnapPoint);
|
|
@@ -1179,4 +1253,4 @@ const DrawerHandle = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
1179
1253
|
});
|
|
1180
1254
|
DrawerHandle.displayName = "DrawerHandle";
|
|
1181
1255
|
|
|
1182
|
-
export { DrawerBackdrop as D, DrawerCloseButton as a, DrawerContent as b, DrawerDescription as c, DrawerHandle as d,
|
|
1256
|
+
export { DrawerBackdrop as D, DrawerCloseButton as a, DrawerContent as b, DrawerDescription as c, DrawerHandle as d, DrawerHeader as e, DrawerPositioner as f, DrawerRoot as g, DrawerTitle as h, DrawerTrigger as i, useDrawerContext as j, useDrawer as u };
|
|
@@ -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('@
|
|
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
|
|
|
@@ -51,6 +51,11 @@ function isIPad() {
|
|
|
51
51
|
function isIOS() {
|
|
52
52
|
return isIPhone() || isIPad();
|
|
53
53
|
}
|
|
54
|
+
function isAndroid() {
|
|
55
|
+
if (typeof window === "undefined" || typeof navigator === "undefined") return false;
|
|
56
|
+
return /Android/.test(navigator.userAgent);
|
|
57
|
+
}
|
|
58
|
+
// TODO: use userAgent instead?
|
|
54
59
|
function testPlatform(re) {
|
|
55
60
|
return typeof window !== "undefined" && window.navigator != null ? re.test(window.navigator.platform) : undefined;
|
|
56
61
|
}
|
|
@@ -380,7 +385,7 @@ function useSnapPoints({ activeSnapPointProp, setActiveSnapPointProp, snapPoints
|
|
|
380
385
|
snapPointsOffset,
|
|
381
386
|
snapToPoint
|
|
382
387
|
]);
|
|
383
|
-
function onRelease({ draggedDistance, closeDrawer, velocity, dismissible }) {
|
|
388
|
+
function onRelease({ draggedDistance, closeDrawer, velocity, dismissible, event }) {
|
|
384
389
|
if (fadeFromIndex === undefined) return;
|
|
385
390
|
const currentPosition = direction === "bottom" || direction === "right" ? (activeSnapPointOffset ?? 0) - draggedDistance : (activeSnapPointOffset ?? 0) + draggedDistance;
|
|
386
391
|
const isOverlaySnapPoint = activeSnapPointIndex === fadeFromIndex - 1;
|
|
@@ -392,7 +397,10 @@ function useSnapPoints({ activeSnapPointProp, setActiveSnapPointProp, snapPoints
|
|
|
392
397
|
});
|
|
393
398
|
}
|
|
394
399
|
if (!snapToSequentialPoint && velocity > 2 && !hasDraggedUp) {
|
|
395
|
-
if (dismissible) closeDrawer(
|
|
400
|
+
if (dismissible) closeDrawer(false, {
|
|
401
|
+
reason: "drag",
|
|
402
|
+
event
|
|
403
|
+
});
|
|
396
404
|
else snapToPoint(snapPointsOffset[0]); // snap to initial point
|
|
397
405
|
return;
|
|
398
406
|
}
|
|
@@ -414,7 +422,10 @@ function useSnapPoints({ activeSnapPointProp, setActiveSnapPointProp, snapPoints
|
|
|
414
422
|
return;
|
|
415
423
|
}
|
|
416
424
|
if (isFirst && dragDirection < 0 && dismissible) {
|
|
417
|
-
closeDrawer(
|
|
425
|
+
closeDrawer(false, {
|
|
426
|
+
reason: "drag",
|
|
427
|
+
event
|
|
428
|
+
});
|
|
418
429
|
}
|
|
419
430
|
if (activeSnapPointIndex === null) return;
|
|
420
431
|
snapToPoint(snapPointsOffset[activeSnapPointIndex + dragDirection]);
|
|
@@ -484,8 +495,8 @@ function useDrawer(props) {
|
|
|
484
495
|
const [isOpen = false, setIsOpen] = reactUseControllableState.useControllableState({
|
|
485
496
|
defaultProp: defaultOpen,
|
|
486
497
|
prop: openProp,
|
|
487
|
-
onChange: (o)=>{
|
|
488
|
-
onOpenChange?.(o);
|
|
498
|
+
onChange: (o, details)=>{
|
|
499
|
+
onOpenChange?.(o, details);
|
|
489
500
|
if (!o && !nested) {
|
|
490
501
|
restorePositionSetting();
|
|
491
502
|
}
|
|
@@ -505,8 +516,13 @@ function useDrawer(props) {
|
|
|
505
516
|
}
|
|
506
517
|
});
|
|
507
518
|
const [hasBeenOpened, setHasBeenOpened] = React.useState(false);
|
|
519
|
+
const [hasAnimationDone, setHasAnimationDone] = React.useState(false);
|
|
508
520
|
const [isDragging, setIsDragging] = React.useState(false);
|
|
509
521
|
const [shouldOverlayAnimate, setShouldOverlayAnimate] = React.useState(false);
|
|
522
|
+
const [isCloseButtonRendered, setIsCloseButtonRendered] = React.useState(false);
|
|
523
|
+
const closeButtonRef = React.useCallback((node)=>{
|
|
524
|
+
setIsCloseButtonRendered(!!node);
|
|
525
|
+
}, []);
|
|
510
526
|
const overlayRef = React.useRef(null);
|
|
511
527
|
const openTime = React.useRef(null);
|
|
512
528
|
const dragStartTime = React.useRef(null);
|
|
@@ -675,11 +691,11 @@ function useDrawer(props) {
|
|
|
675
691
|
}, [
|
|
676
692
|
isDragging
|
|
677
693
|
]);
|
|
678
|
-
const closeDrawer = React.useCallback((fromWithin)=>{
|
|
694
|
+
const closeDrawer = React.useCallback((fromWithin, details)=>{
|
|
679
695
|
cancelDrag();
|
|
680
696
|
onClose?.();
|
|
681
697
|
if (!fromWithin) {
|
|
682
|
-
setIsOpen(false);
|
|
698
|
+
setIsOpen(false, details);
|
|
683
699
|
}
|
|
684
700
|
if (fadeFromIndex !== undefined && fadeFromIndex > 0 && activeSnapPointIndex === 0) {
|
|
685
701
|
set(overlayRef.current, {
|
|
@@ -729,7 +745,8 @@ function useDrawer(props) {
|
|
|
729
745
|
draggedDistance: distMoved * directionMultiplier,
|
|
730
746
|
closeDrawer,
|
|
731
747
|
velocity,
|
|
732
|
-
dismissible
|
|
748
|
+
dismissible,
|
|
749
|
+
event: event.nativeEvent
|
|
733
750
|
});
|
|
734
751
|
onReleaseProp?.(event, true);
|
|
735
752
|
return;
|
|
@@ -740,7 +757,10 @@ function useDrawer(props) {
|
|
|
740
757
|
return;
|
|
741
758
|
}
|
|
742
759
|
if (velocity > VELOCITY_THRESHOLD) {
|
|
743
|
-
closeDrawer(
|
|
760
|
+
closeDrawer(false, {
|
|
761
|
+
reason: "drag",
|
|
762
|
+
event: event.nativeEvent
|
|
763
|
+
});
|
|
744
764
|
onReleaseProp?.(event, false);
|
|
745
765
|
return;
|
|
746
766
|
}
|
|
@@ -748,7 +768,10 @@ function useDrawer(props) {
|
|
|
748
768
|
const visibleDrawerWidth = Math.min(drawerRef.current.getBoundingClientRect().width ?? 0, window.innerWidth);
|
|
749
769
|
const isHorizontalSwipe = direction === "left" || direction === "right";
|
|
750
770
|
if (Math.abs(swipeAmount) >= (isHorizontalSwipe ? visibleDrawerWidth : visibleDrawerHeight) * closeThreshold) {
|
|
751
|
-
closeDrawer(
|
|
771
|
+
closeDrawer(false, {
|
|
772
|
+
reason: "drag",
|
|
773
|
+
event: event.nativeEvent
|
|
774
|
+
});
|
|
752
775
|
onReleaseProp?.(event, false);
|
|
753
776
|
return;
|
|
754
777
|
}
|
|
@@ -801,7 +824,7 @@ function useDrawer(props) {
|
|
|
801
824
|
} else {
|
|
802
825
|
drawerRef.current.style.height = `${Math.max(newDrawerHeight, visualViewportHeight - offsetFromTop)}px`;
|
|
803
826
|
}
|
|
804
|
-
} else if (!isMobileFirefox()) {
|
|
827
|
+
} else if (!isMobileFirefox() && !isAndroid()) {
|
|
805
828
|
drawerRef.current.style.height = `${initialDrawerHeight.current}px`;
|
|
806
829
|
}
|
|
807
830
|
if (snapPoints && snapPoints.length > 0 && !keyboardIsOpen.current) {
|
|
@@ -829,6 +852,7 @@ function useDrawer(props) {
|
|
|
829
852
|
}, [
|
|
830
853
|
modal
|
|
831
854
|
]);
|
|
855
|
+
// Effect 1: Track drawer open state
|
|
832
856
|
React.useEffect(()=>{
|
|
833
857
|
if (isOpen) {
|
|
834
858
|
setHasBeenOpened(true);
|
|
@@ -836,6 +860,24 @@ function useDrawer(props) {
|
|
|
836
860
|
}, [
|
|
837
861
|
isOpen
|
|
838
862
|
]);
|
|
863
|
+
// Effect 2: Handle animation state and timer
|
|
864
|
+
React.useEffect(()=>{
|
|
865
|
+
if (isOpen) {
|
|
866
|
+
// Only reset animation state if this is the first open
|
|
867
|
+
if (!hasBeenOpened) {
|
|
868
|
+
setHasAnimationDone(false);
|
|
869
|
+
}
|
|
870
|
+
const timeoutId = setTimeout(()=>{
|
|
871
|
+
setHasAnimationDone(true);
|
|
872
|
+
}, TRANSITIONS.ENTER_DURATION * 1000);
|
|
873
|
+
return ()=>clearTimeout(timeoutId);
|
|
874
|
+
}
|
|
875
|
+
// Reset animation state when drawer closes
|
|
876
|
+
setHasAnimationDone(false);
|
|
877
|
+
}, [
|
|
878
|
+
isOpen,
|
|
879
|
+
hasBeenOpened
|
|
880
|
+
]);
|
|
839
881
|
React.useEffect(()=>{
|
|
840
882
|
if (isOpen && snapPoints && fadeFromIndex === 0) {
|
|
841
883
|
setShouldOverlayAnimate(true);
|
|
@@ -878,7 +920,10 @@ function useDrawer(props) {
|
|
|
878
920
|
setHasBeenOpened,
|
|
879
921
|
setIsOpen,
|
|
880
922
|
closeOnInteractOutside,
|
|
881
|
-
closeOnEscape
|
|
923
|
+
closeOnEscape,
|
|
924
|
+
hasAnimationDone,
|
|
925
|
+
closeButtonRef,
|
|
926
|
+
isCloseButtonRendered
|
|
882
927
|
}), [
|
|
883
928
|
activeSnapPoint,
|
|
884
929
|
snapPoints,
|
|
@@ -903,7 +948,10 @@ function useDrawer(props) {
|
|
|
903
948
|
closeOnEscape,
|
|
904
949
|
onRelease,
|
|
905
950
|
onDrag,
|
|
906
|
-
onPress
|
|
951
|
+
onPress,
|
|
952
|
+
hasAnimationDone,
|
|
953
|
+
closeButtonRef,
|
|
954
|
+
isCloseButtonRendered
|
|
907
955
|
]);
|
|
908
956
|
}
|
|
909
957
|
|
|
@@ -953,11 +1001,10 @@ const DrawerPositioner = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
953
1001
|
});
|
|
954
1002
|
DrawerPositioner.displayName = "DrawerPositioner";
|
|
955
1003
|
const DrawerBackdrop = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
956
|
-
const { overlayRef, onRelease, modal, snapPoints, isOpen, shouldFade, shouldOverlayAnimate } = useDrawerContext();
|
|
1004
|
+
const { overlayRef, onRelease, modal, snapPoints, isOpen, shouldFade, shouldOverlayAnimate, hasAnimationDone } = useDrawerContext();
|
|
957
1005
|
const composedRef = reactComposeRefs.useComposedRefs(ref, overlayRef);
|
|
958
1006
|
const hasSnapPoints = snapPoints && snapPoints.length > 0;
|
|
959
1007
|
const onMouseUp = reactUseCallbackRef.useCallbackRef((event)=>onRelease(event));
|
|
960
|
-
// Overlay is the component that is locking scroll, removing it will unlock the scroll without having to dig into Radix's Dialog library
|
|
961
1008
|
if (!modal) {
|
|
962
1009
|
return null;
|
|
963
1010
|
}
|
|
@@ -968,13 +1015,14 @@ const DrawerBackdrop = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
968
1015
|
"data-snap-points-overlay": isOpen && shouldFade ? "true" : "false",
|
|
969
1016
|
"data-should-overlay-animate": shouldOverlayAnimate ? "true" : "false",
|
|
970
1017
|
"data-open": domUtils.dataAttr(isOpen),
|
|
1018
|
+
"data-animation-done": hasAnimationDone ? "true" : "false",
|
|
971
1019
|
...props
|
|
972
1020
|
});
|
|
973
1021
|
});
|
|
974
1022
|
DrawerBackdrop.displayName = "DrawerBackdrop";
|
|
975
1023
|
const DrawerContent = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
976
1024
|
const { onPointerDownOutside, style, onOpenAutoFocus, ...restProps } = props;
|
|
977
|
-
const { drawerRef, onPress, onRelease, onDrag, keyboardIsOpen, snapPointsOffset, activeSnapPointIndex, modal, isOpen, direction, snapPoints, container, handleOnly, autoFocus, closeDrawer, closeOnInteractOutside, closeOnEscape, dismissible } = useDrawerContext();
|
|
1025
|
+
const { drawerRef, onPress, onRelease, onDrag, keyboardIsOpen, snapPointsOffset, activeSnapPointIndex, modal, isOpen, direction, snapPoints, container, handleOnly, autoFocus, closeDrawer, closeOnInteractOutside, closeOnEscape, dismissible, hasAnimationDone } = useDrawerContext();
|
|
978
1026
|
// Needed to use transition instead of animations
|
|
979
1027
|
const [delayedSnapPoints, setDelayedSnapPoints] = React.useState(false);
|
|
980
1028
|
const composedRef = reactComposeRefs.useComposedRefs(ref, drawerRef);
|
|
@@ -1021,6 +1069,7 @@ const DrawerContent = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
1021
1069
|
"data-delayed-snap-points": delayedSnapPoints ? "true" : "false",
|
|
1022
1070
|
"data-drawer-direction": direction,
|
|
1023
1071
|
"data-open": domUtils.dataAttr(isOpen),
|
|
1072
|
+
"data-animation-done": hasAnimationDone ? "true" : "false",
|
|
1024
1073
|
"data-drawer": "",
|
|
1025
1074
|
"data-snap-points": isOpen && hasSnapPoints ? "true" : "false",
|
|
1026
1075
|
"data-custom-container": container ? "true" : "false",
|
|
@@ -1098,13 +1147,19 @@ const DrawerContent = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
1098
1147
|
onInteractOutside: (e)=>{
|
|
1099
1148
|
// Only close if event is not prevented (e.g., by onFocusOutside or onPointerDownOutside)
|
|
1100
1149
|
if (dismissible && closeOnInteractOutside && !e.defaultPrevented) {
|
|
1101
|
-
closeDrawer(
|
|
1150
|
+
closeDrawer(false, {
|
|
1151
|
+
reason: "interactOutside",
|
|
1152
|
+
event: e.detail.originalEvent
|
|
1153
|
+
});
|
|
1102
1154
|
}
|
|
1103
1155
|
props.onInteractOutside?.(e);
|
|
1104
1156
|
},
|
|
1105
1157
|
onEscapeKeyDown: (e)=>{
|
|
1106
1158
|
if (dismissible && closeOnEscape) {
|
|
1107
|
-
closeDrawer(
|
|
1159
|
+
closeDrawer(false, {
|
|
1160
|
+
reason: "escapeKeyDown",
|
|
1161
|
+
event: e
|
|
1162
|
+
});
|
|
1108
1163
|
}
|
|
1109
1164
|
props.onEscapeKeyDown?.(e);
|
|
1110
1165
|
}
|
|
@@ -1113,15 +1168,28 @@ const DrawerContent = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
1113
1168
|
DrawerContent.displayName = "DrawerContent";
|
|
1114
1169
|
const DrawerTitle = DialogPrimitive__namespace.Title;
|
|
1115
1170
|
const DrawerDescription = DialogPrimitive__namespace.Description;
|
|
1171
|
+
const DrawerHeader = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
1172
|
+
const { isCloseButtonRendered } = useDrawerContext();
|
|
1173
|
+
return /*#__PURE__*/ jsxRuntime.jsx(reactPrimitive.Primitive.div, {
|
|
1174
|
+
ref: ref,
|
|
1175
|
+
"data-show-close-button": domUtils.dataAttr(isCloseButtonRendered),
|
|
1176
|
+
...props
|
|
1177
|
+
});
|
|
1178
|
+
});
|
|
1179
|
+
DrawerHeader.displayName = "DrawerHeader";
|
|
1116
1180
|
const DrawerCloseButton = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
1117
|
-
const
|
|
1181
|
+
const { closeButtonRef, setIsOpen } = useDrawerContext();
|
|
1182
|
+
const composedRef = reactComposeRefs.useComposedRefs(ref, closeButtonRef);
|
|
1118
1183
|
return /*#__PURE__*/ jsxRuntime.jsx(reactPrimitive.Primitive.button, {
|
|
1119
|
-
ref:
|
|
1184
|
+
ref: composedRef,
|
|
1120
1185
|
...props,
|
|
1121
1186
|
onClick: (e)=>{
|
|
1122
1187
|
props.onClick?.(e);
|
|
1123
1188
|
if (e.defaultPrevented) return;
|
|
1124
|
-
|
|
1189
|
+
setIsOpen(false, {
|
|
1190
|
+
reason: "closeButton",
|
|
1191
|
+
event: e.nativeEvent
|
|
1192
|
+
});
|
|
1125
1193
|
}
|
|
1126
1194
|
});
|
|
1127
1195
|
});
|
|
@@ -1132,17 +1200,17 @@ const DrawerHandle = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
1132
1200
|
const { closeDrawer, isDragging, snapPoints, activeSnapPoint, setActiveSnapPoint, dismissible, handleOnly, isOpen, onPress, onDrag, onRelease } = useDrawerContext();
|
|
1133
1201
|
const closeTimeoutIdRef = React.useRef(null);
|
|
1134
1202
|
const shouldCancelInteractionRef = React.useRef(false);
|
|
1135
|
-
function handleStartCycle() {
|
|
1203
|
+
function handleStartCycle(event) {
|
|
1136
1204
|
// Stop if this is the second click of a double click
|
|
1137
1205
|
if (shouldCancelInteractionRef.current) {
|
|
1138
1206
|
handleCancelInteraction();
|
|
1139
1207
|
return;
|
|
1140
1208
|
}
|
|
1141
1209
|
window.setTimeout(()=>{
|
|
1142
|
-
handleCycleSnapPoints();
|
|
1210
|
+
handleCycleSnapPoints(event);
|
|
1143
1211
|
}, DOUBLE_TAP_TIMEOUT);
|
|
1144
1212
|
}
|
|
1145
|
-
function handleCycleSnapPoints() {
|
|
1213
|
+
function handleCycleSnapPoints(event) {
|
|
1146
1214
|
// Prevent accidental taps while resizing drawer
|
|
1147
1215
|
if (isDragging || preventCycle || shouldCancelInteractionRef.current) {
|
|
1148
1216
|
handleCancelInteraction();
|
|
@@ -1152,13 +1220,19 @@ const DrawerHandle = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
1152
1220
|
handleCancelInteraction();
|
|
1153
1221
|
if (!snapPoints || snapPoints.length === 0) {
|
|
1154
1222
|
if (!dismissible) {
|
|
1155
|
-
closeDrawer(
|
|
1223
|
+
closeDrawer(false, {
|
|
1224
|
+
reason: "handleClickOnLastSnapPoint",
|
|
1225
|
+
event: event.nativeEvent
|
|
1226
|
+
});
|
|
1156
1227
|
}
|
|
1157
1228
|
return;
|
|
1158
1229
|
}
|
|
1159
1230
|
const isLastSnapPoint = activeSnapPoint === snapPoints[snapPoints.length - 1];
|
|
1160
1231
|
if (isLastSnapPoint && dismissible) {
|
|
1161
|
-
closeDrawer(
|
|
1232
|
+
closeDrawer(false, {
|
|
1233
|
+
reason: "handleClickOnLastSnapPoint",
|
|
1234
|
+
event: event.nativeEvent
|
|
1235
|
+
});
|
|
1162
1236
|
return;
|
|
1163
1237
|
}
|
|
1164
1238
|
const currentSnapIndex = snapPoints.findIndex((point)=>point === activeSnapPoint);
|
|
@@ -1207,6 +1281,7 @@ exports.DrawerCloseButton = DrawerCloseButton;
|
|
|
1207
1281
|
exports.DrawerContent = DrawerContent;
|
|
1208
1282
|
exports.DrawerDescription = DrawerDescription;
|
|
1209
1283
|
exports.DrawerHandle = DrawerHandle;
|
|
1284
|
+
exports.DrawerHeader = DrawerHeader;
|
|
1210
1285
|
exports.DrawerPositioner = DrawerPositioner;
|
|
1211
1286
|
exports.DrawerRoot = DrawerRoot;
|
|
1212
1287
|
exports.DrawerTitle = DrawerTitle;
|
package/lib/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var Drawer12s = require('./Drawer-12s-
|
|
1
|
+
var Drawer12s = require('./Drawer-12s-B35ZJcKm.cjs');
|
|
2
2
|
|
|
3
3
|
var Drawer_namespace = {
|
|
4
4
|
__proto__: null,
|
|
@@ -7,6 +7,7 @@ var Drawer_namespace = {
|
|
|
7
7
|
Content: Drawer12s.DrawerContent,
|
|
8
8
|
Description: Drawer12s.DrawerDescription,
|
|
9
9
|
Handle: Drawer12s.DrawerHandle,
|
|
10
|
+
Header: Drawer12s.DrawerHeader,
|
|
10
11
|
Positioner: Drawer12s.DrawerPositioner,
|
|
11
12
|
Root: Drawer12s.DrawerRoot,
|
|
12
13
|
Title: Drawer12s.DrawerTitle,
|
|
@@ -18,6 +19,7 @@ exports.DrawerCloseButton = Drawer12s.DrawerCloseButton;
|
|
|
18
19
|
exports.DrawerContent = Drawer12s.DrawerContent;
|
|
19
20
|
exports.DrawerDescription = Drawer12s.DrawerDescription;
|
|
20
21
|
exports.DrawerHandle = Drawer12s.DrawerHandle;
|
|
22
|
+
exports.DrawerHeader = Drawer12s.DrawerHeader;
|
|
21
23
|
exports.DrawerPositioner = Drawer12s.DrawerPositioner;
|
|
22
24
|
exports.DrawerRoot = Drawer12s.DrawerRoot;
|
|
23
25
|
exports.DrawerTitle = Drawer12s.DrawerTitle;
|