@seed-design/react-drawer 1.0.4 → 1.0.6
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-BOMSoUDc.js → Drawer-12s-BdeYHia6.js} +57 -24
- package/lib/{Drawer-12s-Yr_NcCoF.cjs → Drawer-12s-DriAYAR2.cjs} +56 -22
- package/lib/index.cjs +3 -1
- package/lib/index.d.ts +40 -33
- package/lib/index.js +4 -3
- package/package.json +1 -1
- package/src/Drawer.namespace.ts +2 -0
- package/src/Drawer.tsx +24 -13
- package/src/index.ts +2 -0
- package/src/useDrawer.ts +33 -9
|
@@ -5,7 +5,7 @@ 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,
|
|
8
|
+
import React, { useState, useCallback, useRef, useEffect, useMemo, createContext, useContext, forwardRef } from 'react';
|
|
9
9
|
import { useControllableState } from '@radix-ui/react-use-controllable-state';
|
|
10
10
|
|
|
11
11
|
function isMobileFirefox() {
|
|
@@ -482,8 +482,13 @@ function useDrawer(props) {
|
|
|
482
482
|
}
|
|
483
483
|
});
|
|
484
484
|
const [hasBeenOpened, setHasBeenOpened] = useState(false);
|
|
485
|
+
const [hasAnimationDone, setHasAnimationDone] = useState(false);
|
|
485
486
|
const [isDragging, setIsDragging] = useState(false);
|
|
486
487
|
const [shouldOverlayAnimate, setShouldOverlayAnimate] = useState(false);
|
|
488
|
+
const [isCloseButtonRendered, setIsCloseButtonRendered] = useState(false);
|
|
489
|
+
const closeButtonRef = useCallback((node)=>{
|
|
490
|
+
setIsCloseButtonRendered(!!node);
|
|
491
|
+
}, []);
|
|
487
492
|
const overlayRef = useRef(null);
|
|
488
493
|
const openTime = useRef(null);
|
|
489
494
|
const dragStartTime = useRef(null);
|
|
@@ -492,7 +497,6 @@ function useDrawer(props) {
|
|
|
492
497
|
const isAllowedToDrag = useRef(false);
|
|
493
498
|
const pointerStart = useRef(0);
|
|
494
499
|
const keyboardIsOpen = useRef(false);
|
|
495
|
-
const shouldAnimate = useRef(!defaultOpen);
|
|
496
500
|
const previousDiffFromInitial = useRef(0);
|
|
497
501
|
const drawerRef = useRef(null);
|
|
498
502
|
const drawerHeightRef = useRef(drawerRef.current?.getBoundingClientRect().height || 0);
|
|
@@ -746,11 +750,6 @@ function useDrawer(props) {
|
|
|
746
750
|
}, [
|
|
747
751
|
isOpen
|
|
748
752
|
]);
|
|
749
|
-
useEffect(()=>{
|
|
750
|
-
window.requestAnimationFrame(()=>{
|
|
751
|
-
shouldAnimate.current = true;
|
|
752
|
-
});
|
|
753
|
-
}, []);
|
|
754
753
|
useEffect(()=>{
|
|
755
754
|
function onVisualViewportChange() {
|
|
756
755
|
if (!drawerRef.current || !repositionInputs) return;
|
|
@@ -812,6 +811,7 @@ function useDrawer(props) {
|
|
|
812
811
|
}, [
|
|
813
812
|
modal
|
|
814
813
|
]);
|
|
814
|
+
// Effect 1: Track drawer open state
|
|
815
815
|
useEffect(()=>{
|
|
816
816
|
if (isOpen) {
|
|
817
817
|
setHasBeenOpened(true);
|
|
@@ -819,6 +819,24 @@ function useDrawer(props) {
|
|
|
819
819
|
}, [
|
|
820
820
|
isOpen
|
|
821
821
|
]);
|
|
822
|
+
// Effect 2: Handle animation state and timer
|
|
823
|
+
useEffect(()=>{
|
|
824
|
+
if (isOpen) {
|
|
825
|
+
// Only reset animation state if this is the first open
|
|
826
|
+
if (!hasBeenOpened) {
|
|
827
|
+
setHasAnimationDone(false);
|
|
828
|
+
}
|
|
829
|
+
const timeoutId = setTimeout(()=>{
|
|
830
|
+
setHasAnimationDone(true);
|
|
831
|
+
}, TRANSITIONS.ENTER_DURATION * 1000);
|
|
832
|
+
return ()=>clearTimeout(timeoutId);
|
|
833
|
+
}
|
|
834
|
+
// Reset animation state when drawer closes
|
|
835
|
+
setHasAnimationDone(false);
|
|
836
|
+
}, [
|
|
837
|
+
isOpen,
|
|
838
|
+
hasBeenOpened
|
|
839
|
+
]);
|
|
822
840
|
useEffect(()=>{
|
|
823
841
|
if (isOpen && snapPoints && fadeFromIndex === 0) {
|
|
824
842
|
setShouldOverlayAnimate(true);
|
|
@@ -845,7 +863,6 @@ function useDrawer(props) {
|
|
|
845
863
|
onRelease,
|
|
846
864
|
onDrag,
|
|
847
865
|
dismissible,
|
|
848
|
-
shouldAnimate,
|
|
849
866
|
handleOnly,
|
|
850
867
|
isOpen,
|
|
851
868
|
isDragging,
|
|
@@ -862,7 +879,10 @@ function useDrawer(props) {
|
|
|
862
879
|
setHasBeenOpened,
|
|
863
880
|
setIsOpen,
|
|
864
881
|
closeOnInteractOutside,
|
|
865
|
-
closeOnEscape
|
|
882
|
+
closeOnEscape,
|
|
883
|
+
hasAnimationDone,
|
|
884
|
+
closeButtonRef,
|
|
885
|
+
isCloseButtonRendered
|
|
866
886
|
}), [
|
|
867
887
|
activeSnapPoint,
|
|
868
888
|
snapPoints,
|
|
@@ -887,7 +907,10 @@ function useDrawer(props) {
|
|
|
887
907
|
closeOnEscape,
|
|
888
908
|
onRelease,
|
|
889
909
|
onDrag,
|
|
890
|
-
onPress
|
|
910
|
+
onPress,
|
|
911
|
+
hasAnimationDone,
|
|
912
|
+
closeButtonRef,
|
|
913
|
+
isCloseButtonRendered
|
|
891
914
|
]);
|
|
892
915
|
}
|
|
893
916
|
|
|
@@ -937,11 +960,10 @@ const DrawerPositioner = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
937
960
|
});
|
|
938
961
|
DrawerPositioner.displayName = "DrawerPositioner";
|
|
939
962
|
const DrawerBackdrop = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
940
|
-
const { overlayRef, onRelease, modal, snapPoints, isOpen, shouldFade,
|
|
963
|
+
const { overlayRef, onRelease, modal, snapPoints, isOpen, shouldFade, shouldOverlayAnimate, hasAnimationDone } = useDrawerContext();
|
|
941
964
|
const composedRef = useComposedRefs(ref, overlayRef);
|
|
942
965
|
const hasSnapPoints = snapPoints && snapPoints.length > 0;
|
|
943
966
|
const onMouseUp = useCallbackRef((event)=>onRelease(event));
|
|
944
|
-
// Overlay is the component that is locking scroll, removing it will unlock the scroll without having to dig into Radix's Dialog library
|
|
945
967
|
if (!modal) {
|
|
946
968
|
return null;
|
|
947
969
|
}
|
|
@@ -950,16 +972,16 @@ const DrawerBackdrop = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
950
972
|
onMouseUp: onMouseUp,
|
|
951
973
|
"data-snap-points": isOpen && hasSnapPoints ? "true" : "false",
|
|
952
974
|
"data-snap-points-overlay": isOpen && shouldFade ? "true" : "false",
|
|
953
|
-
"data-animate": shouldAnimate?.current ? "true" : "false",
|
|
954
975
|
"data-should-overlay-animate": shouldOverlayAnimate ? "true" : "false",
|
|
955
976
|
"data-open": dataAttr(isOpen),
|
|
977
|
+
"data-animation-done": hasAnimationDone ? "true" : "false",
|
|
956
978
|
...props
|
|
957
979
|
});
|
|
958
980
|
});
|
|
959
981
|
DrawerBackdrop.displayName = "DrawerBackdrop";
|
|
960
982
|
const DrawerContent = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
961
983
|
const { onPointerDownOutside, style, onOpenAutoFocus, ...restProps } = props;
|
|
962
|
-
const { drawerRef, onPress, onRelease, onDrag, keyboardIsOpen, snapPointsOffset, activeSnapPointIndex, modal, isOpen, direction, snapPoints, container, handleOnly,
|
|
984
|
+
const { drawerRef, onPress, onRelease, onDrag, keyboardIsOpen, snapPointsOffset, activeSnapPointIndex, modal, isOpen, direction, snapPoints, container, handleOnly, autoFocus, closeDrawer, closeOnInteractOutside, closeOnEscape, dismissible, hasAnimationDone } = useDrawerContext();
|
|
963
985
|
// Needed to use transition instead of animations
|
|
964
986
|
const [delayedSnapPoints, setDelayedSnapPoints] = useState(false);
|
|
965
987
|
const composedRef = useComposedRefs(ref, drawerRef);
|
|
@@ -1006,10 +1028,10 @@ const DrawerContent = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
1006
1028
|
"data-delayed-snap-points": delayedSnapPoints ? "true" : "false",
|
|
1007
1029
|
"data-drawer-direction": direction,
|
|
1008
1030
|
"data-open": dataAttr(isOpen),
|
|
1031
|
+
"data-animation-done": hasAnimationDone ? "true" : "false",
|
|
1009
1032
|
"data-drawer": "",
|
|
1010
1033
|
"data-snap-points": isOpen && hasSnapPoints ? "true" : "false",
|
|
1011
1034
|
"data-custom-container": container ? "true" : "false",
|
|
1012
|
-
"data-animate": shouldAnimate?.current ? "true" : "false",
|
|
1013
1035
|
...restProps,
|
|
1014
1036
|
ref: composedRef,
|
|
1015
1037
|
style: snapPointsOffset && snapPointsOffset.length > 0 ? {
|
|
@@ -1042,10 +1064,10 @@ const DrawerContent = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
1042
1064
|
}
|
|
1043
1065
|
},
|
|
1044
1066
|
onFocusOutside: (e)=>{
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1067
|
+
props.onFocusOutside?.(e);
|
|
1068
|
+
// Always prevent focusOutside to avoid conflicts when focus moves between modals
|
|
1069
|
+
// (e.g., when Dialog closes and restores focus while BottomSheet is opening)
|
|
1070
|
+
e.preventDefault();
|
|
1049
1071
|
},
|
|
1050
1072
|
onPointerMove: (event)=>{
|
|
1051
1073
|
lastKnownPointerEventRef.current = event;
|
|
@@ -1082,7 +1104,8 @@ const DrawerContent = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
1082
1104
|
}
|
|
1083
1105
|
},
|
|
1084
1106
|
onInteractOutside: (e)=>{
|
|
1085
|
-
if (
|
|
1107
|
+
// Only close if event is not prevented (e.g., by onFocusOutside or onPointerDownOutside)
|
|
1108
|
+
if (dismissible && closeOnInteractOutside && !e.defaultPrevented) {
|
|
1086
1109
|
closeDrawer();
|
|
1087
1110
|
}
|
|
1088
1111
|
props.onInteractOutside?.(e);
|
|
@@ -1098,15 +1121,25 @@ const DrawerContent = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
1098
1121
|
DrawerContent.displayName = "DrawerContent";
|
|
1099
1122
|
const DrawerTitle = DialogPrimitive.Title;
|
|
1100
1123
|
const DrawerDescription = DialogPrimitive.Description;
|
|
1124
|
+
const DrawerHeader = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
1125
|
+
const { isCloseButtonRendered } = useDrawerContext();
|
|
1126
|
+
return /*#__PURE__*/ jsx(Primitive.div, {
|
|
1127
|
+
ref: ref,
|
|
1128
|
+
"data-show-close-button": dataAttr(isCloseButtonRendered),
|
|
1129
|
+
...props
|
|
1130
|
+
});
|
|
1131
|
+
});
|
|
1132
|
+
DrawerHeader.displayName = "DrawerHeader";
|
|
1101
1133
|
const DrawerCloseButton = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
1102
|
-
const
|
|
1134
|
+
const { closeButtonRef, setIsOpen } = useDrawerContext();
|
|
1135
|
+
const composedRef = useComposedRefs(ref, closeButtonRef);
|
|
1103
1136
|
return /*#__PURE__*/ jsx(Primitive.button, {
|
|
1104
|
-
ref:
|
|
1137
|
+
ref: composedRef,
|
|
1105
1138
|
...props,
|
|
1106
1139
|
onClick: (e)=>{
|
|
1107
1140
|
props.onClick?.(e);
|
|
1108
1141
|
if (e.defaultPrevented) return;
|
|
1109
|
-
|
|
1142
|
+
setIsOpen(false);
|
|
1110
1143
|
}
|
|
1111
1144
|
});
|
|
1112
1145
|
});
|
|
@@ -1187,4 +1220,4 @@ const DrawerHandle = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
1187
1220
|
});
|
|
1188
1221
|
DrawerHandle.displayName = "DrawerHandle";
|
|
1189
1222
|
|
|
1190
|
-
export { DrawerBackdrop as D, DrawerCloseButton as a, DrawerContent as b, DrawerDescription as c, DrawerHandle as d,
|
|
1223
|
+
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 };
|
|
@@ -505,8 +505,13 @@ function useDrawer(props) {
|
|
|
505
505
|
}
|
|
506
506
|
});
|
|
507
507
|
const [hasBeenOpened, setHasBeenOpened] = React.useState(false);
|
|
508
|
+
const [hasAnimationDone, setHasAnimationDone] = React.useState(false);
|
|
508
509
|
const [isDragging, setIsDragging] = React.useState(false);
|
|
509
510
|
const [shouldOverlayAnimate, setShouldOverlayAnimate] = React.useState(false);
|
|
511
|
+
const [isCloseButtonRendered, setIsCloseButtonRendered] = React.useState(false);
|
|
512
|
+
const closeButtonRef = React.useCallback((node)=>{
|
|
513
|
+
setIsCloseButtonRendered(!!node);
|
|
514
|
+
}, []);
|
|
510
515
|
const overlayRef = React.useRef(null);
|
|
511
516
|
const openTime = React.useRef(null);
|
|
512
517
|
const dragStartTime = React.useRef(null);
|
|
@@ -515,7 +520,6 @@ function useDrawer(props) {
|
|
|
515
520
|
const isAllowedToDrag = React.useRef(false);
|
|
516
521
|
const pointerStart = React.useRef(0);
|
|
517
522
|
const keyboardIsOpen = React.useRef(false);
|
|
518
|
-
const shouldAnimate = React.useRef(!defaultOpen);
|
|
519
523
|
const previousDiffFromInitial = React.useRef(0);
|
|
520
524
|
const drawerRef = React.useRef(null);
|
|
521
525
|
const drawerHeightRef = React.useRef(drawerRef.current?.getBoundingClientRect().height || 0);
|
|
@@ -769,11 +773,6 @@ function useDrawer(props) {
|
|
|
769
773
|
}, [
|
|
770
774
|
isOpen
|
|
771
775
|
]);
|
|
772
|
-
React.useEffect(()=>{
|
|
773
|
-
window.requestAnimationFrame(()=>{
|
|
774
|
-
shouldAnimate.current = true;
|
|
775
|
-
});
|
|
776
|
-
}, []);
|
|
777
776
|
React.useEffect(()=>{
|
|
778
777
|
function onVisualViewportChange() {
|
|
779
778
|
if (!drawerRef.current || !repositionInputs) return;
|
|
@@ -835,6 +834,7 @@ function useDrawer(props) {
|
|
|
835
834
|
}, [
|
|
836
835
|
modal
|
|
837
836
|
]);
|
|
837
|
+
// Effect 1: Track drawer open state
|
|
838
838
|
React.useEffect(()=>{
|
|
839
839
|
if (isOpen) {
|
|
840
840
|
setHasBeenOpened(true);
|
|
@@ -842,6 +842,24 @@ function useDrawer(props) {
|
|
|
842
842
|
}, [
|
|
843
843
|
isOpen
|
|
844
844
|
]);
|
|
845
|
+
// Effect 2: Handle animation state and timer
|
|
846
|
+
React.useEffect(()=>{
|
|
847
|
+
if (isOpen) {
|
|
848
|
+
// Only reset animation state if this is the first open
|
|
849
|
+
if (!hasBeenOpened) {
|
|
850
|
+
setHasAnimationDone(false);
|
|
851
|
+
}
|
|
852
|
+
const timeoutId = setTimeout(()=>{
|
|
853
|
+
setHasAnimationDone(true);
|
|
854
|
+
}, TRANSITIONS.ENTER_DURATION * 1000);
|
|
855
|
+
return ()=>clearTimeout(timeoutId);
|
|
856
|
+
}
|
|
857
|
+
// Reset animation state when drawer closes
|
|
858
|
+
setHasAnimationDone(false);
|
|
859
|
+
}, [
|
|
860
|
+
isOpen,
|
|
861
|
+
hasBeenOpened
|
|
862
|
+
]);
|
|
845
863
|
React.useEffect(()=>{
|
|
846
864
|
if (isOpen && snapPoints && fadeFromIndex === 0) {
|
|
847
865
|
setShouldOverlayAnimate(true);
|
|
@@ -868,7 +886,6 @@ function useDrawer(props) {
|
|
|
868
886
|
onRelease,
|
|
869
887
|
onDrag,
|
|
870
888
|
dismissible,
|
|
871
|
-
shouldAnimate,
|
|
872
889
|
handleOnly,
|
|
873
890
|
isOpen,
|
|
874
891
|
isDragging,
|
|
@@ -885,7 +902,10 @@ function useDrawer(props) {
|
|
|
885
902
|
setHasBeenOpened,
|
|
886
903
|
setIsOpen,
|
|
887
904
|
closeOnInteractOutside,
|
|
888
|
-
closeOnEscape
|
|
905
|
+
closeOnEscape,
|
|
906
|
+
hasAnimationDone,
|
|
907
|
+
closeButtonRef,
|
|
908
|
+
isCloseButtonRendered
|
|
889
909
|
}), [
|
|
890
910
|
activeSnapPoint,
|
|
891
911
|
snapPoints,
|
|
@@ -910,7 +930,10 @@ function useDrawer(props) {
|
|
|
910
930
|
closeOnEscape,
|
|
911
931
|
onRelease,
|
|
912
932
|
onDrag,
|
|
913
|
-
onPress
|
|
933
|
+
onPress,
|
|
934
|
+
hasAnimationDone,
|
|
935
|
+
closeButtonRef,
|
|
936
|
+
isCloseButtonRendered
|
|
914
937
|
]);
|
|
915
938
|
}
|
|
916
939
|
|
|
@@ -960,11 +983,10 @@ const DrawerPositioner = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
960
983
|
});
|
|
961
984
|
DrawerPositioner.displayName = "DrawerPositioner";
|
|
962
985
|
const DrawerBackdrop = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
963
|
-
const { overlayRef, onRelease, modal, snapPoints, isOpen, shouldFade,
|
|
986
|
+
const { overlayRef, onRelease, modal, snapPoints, isOpen, shouldFade, shouldOverlayAnimate, hasAnimationDone } = useDrawerContext();
|
|
964
987
|
const composedRef = reactComposeRefs.useComposedRefs(ref, overlayRef);
|
|
965
988
|
const hasSnapPoints = snapPoints && snapPoints.length > 0;
|
|
966
989
|
const onMouseUp = reactUseCallbackRef.useCallbackRef((event)=>onRelease(event));
|
|
967
|
-
// Overlay is the component that is locking scroll, removing it will unlock the scroll without having to dig into Radix's Dialog library
|
|
968
990
|
if (!modal) {
|
|
969
991
|
return null;
|
|
970
992
|
}
|
|
@@ -973,16 +995,16 @@ const DrawerBackdrop = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
973
995
|
onMouseUp: onMouseUp,
|
|
974
996
|
"data-snap-points": isOpen && hasSnapPoints ? "true" : "false",
|
|
975
997
|
"data-snap-points-overlay": isOpen && shouldFade ? "true" : "false",
|
|
976
|
-
"data-animate": shouldAnimate?.current ? "true" : "false",
|
|
977
998
|
"data-should-overlay-animate": shouldOverlayAnimate ? "true" : "false",
|
|
978
999
|
"data-open": domUtils.dataAttr(isOpen),
|
|
1000
|
+
"data-animation-done": hasAnimationDone ? "true" : "false",
|
|
979
1001
|
...props
|
|
980
1002
|
});
|
|
981
1003
|
});
|
|
982
1004
|
DrawerBackdrop.displayName = "DrawerBackdrop";
|
|
983
1005
|
const DrawerContent = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
984
1006
|
const { onPointerDownOutside, style, onOpenAutoFocus, ...restProps } = props;
|
|
985
|
-
const { drawerRef, onPress, onRelease, onDrag, keyboardIsOpen, snapPointsOffset, activeSnapPointIndex, modal, isOpen, direction, snapPoints, container, handleOnly,
|
|
1007
|
+
const { drawerRef, onPress, onRelease, onDrag, keyboardIsOpen, snapPointsOffset, activeSnapPointIndex, modal, isOpen, direction, snapPoints, container, handleOnly, autoFocus, closeDrawer, closeOnInteractOutside, closeOnEscape, dismissible, hasAnimationDone } = useDrawerContext();
|
|
986
1008
|
// Needed to use transition instead of animations
|
|
987
1009
|
const [delayedSnapPoints, setDelayedSnapPoints] = React.useState(false);
|
|
988
1010
|
const composedRef = reactComposeRefs.useComposedRefs(ref, drawerRef);
|
|
@@ -1029,10 +1051,10 @@ const DrawerContent = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
1029
1051
|
"data-delayed-snap-points": delayedSnapPoints ? "true" : "false",
|
|
1030
1052
|
"data-drawer-direction": direction,
|
|
1031
1053
|
"data-open": domUtils.dataAttr(isOpen),
|
|
1054
|
+
"data-animation-done": hasAnimationDone ? "true" : "false",
|
|
1032
1055
|
"data-drawer": "",
|
|
1033
1056
|
"data-snap-points": isOpen && hasSnapPoints ? "true" : "false",
|
|
1034
1057
|
"data-custom-container": container ? "true" : "false",
|
|
1035
|
-
"data-animate": shouldAnimate?.current ? "true" : "false",
|
|
1036
1058
|
...restProps,
|
|
1037
1059
|
ref: composedRef,
|
|
1038
1060
|
style: snapPointsOffset && snapPointsOffset.length > 0 ? {
|
|
@@ -1065,10 +1087,10 @@ const DrawerContent = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
1065
1087
|
}
|
|
1066
1088
|
},
|
|
1067
1089
|
onFocusOutside: (e)=>{
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1090
|
+
props.onFocusOutside?.(e);
|
|
1091
|
+
// Always prevent focusOutside to avoid conflicts when focus moves between modals
|
|
1092
|
+
// (e.g., when Dialog closes and restores focus while BottomSheet is opening)
|
|
1093
|
+
e.preventDefault();
|
|
1072
1094
|
},
|
|
1073
1095
|
onPointerMove: (event)=>{
|
|
1074
1096
|
lastKnownPointerEventRef.current = event;
|
|
@@ -1105,7 +1127,8 @@ const DrawerContent = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
1105
1127
|
}
|
|
1106
1128
|
},
|
|
1107
1129
|
onInteractOutside: (e)=>{
|
|
1108
|
-
if (
|
|
1130
|
+
// Only close if event is not prevented (e.g., by onFocusOutside or onPointerDownOutside)
|
|
1131
|
+
if (dismissible && closeOnInteractOutside && !e.defaultPrevented) {
|
|
1109
1132
|
closeDrawer();
|
|
1110
1133
|
}
|
|
1111
1134
|
props.onInteractOutside?.(e);
|
|
@@ -1121,15 +1144,25 @@ const DrawerContent = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
1121
1144
|
DrawerContent.displayName = "DrawerContent";
|
|
1122
1145
|
const DrawerTitle = DialogPrimitive__namespace.Title;
|
|
1123
1146
|
const DrawerDescription = DialogPrimitive__namespace.Description;
|
|
1147
|
+
const DrawerHeader = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
1148
|
+
const { isCloseButtonRendered } = useDrawerContext();
|
|
1149
|
+
return /*#__PURE__*/ jsxRuntime.jsx(reactPrimitive.Primitive.div, {
|
|
1150
|
+
ref: ref,
|
|
1151
|
+
"data-show-close-button": domUtils.dataAttr(isCloseButtonRendered),
|
|
1152
|
+
...props
|
|
1153
|
+
});
|
|
1154
|
+
});
|
|
1155
|
+
DrawerHeader.displayName = "DrawerHeader";
|
|
1124
1156
|
const DrawerCloseButton = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
1125
|
-
const
|
|
1157
|
+
const { closeButtonRef, setIsOpen } = useDrawerContext();
|
|
1158
|
+
const composedRef = reactComposeRefs.useComposedRefs(ref, closeButtonRef);
|
|
1126
1159
|
return /*#__PURE__*/ jsxRuntime.jsx(reactPrimitive.Primitive.button, {
|
|
1127
|
-
ref:
|
|
1160
|
+
ref: composedRef,
|
|
1128
1161
|
...props,
|
|
1129
1162
|
onClick: (e)=>{
|
|
1130
1163
|
props.onClick?.(e);
|
|
1131
1164
|
if (e.defaultPrevented) return;
|
|
1132
|
-
|
|
1165
|
+
setIsOpen(false);
|
|
1133
1166
|
}
|
|
1134
1167
|
});
|
|
1135
1168
|
});
|
|
@@ -1215,6 +1248,7 @@ exports.DrawerCloseButton = DrawerCloseButton;
|
|
|
1215
1248
|
exports.DrawerContent = DrawerContent;
|
|
1216
1249
|
exports.DrawerDescription = DrawerDescription;
|
|
1217
1250
|
exports.DrawerHandle = DrawerHandle;
|
|
1251
|
+
exports.DrawerHeader = DrawerHeader;
|
|
1218
1252
|
exports.DrawerPositioner = DrawerPositioner;
|
|
1219
1253
|
exports.DrawerRoot = DrawerRoot;
|
|
1220
1254
|
exports.DrawerTitle = DrawerTitle;
|
package/lib/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var Drawer12s = require('./Drawer-12s-
|
|
1
|
+
var Drawer12s = require('./Drawer-12s-DriAYAR2.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;
|
package/lib/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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
|
|
4
|
+
import * as React$1 from 'react';
|
|
5
5
|
|
|
6
6
|
interface UseDrawerProps {
|
|
7
7
|
activeSnapPoint?: number | string | null;
|
|
@@ -55,7 +55,7 @@ interface UseDrawerProps {
|
|
|
55
55
|
*/
|
|
56
56
|
direction?: "top" | "bottom" | "left" | "right";
|
|
57
57
|
/**
|
|
58
|
-
* Opened by default
|
|
58
|
+
* Opened by default. Still reacts to `open` state changes
|
|
59
59
|
* @default false
|
|
60
60
|
*/
|
|
61
61
|
defaultOpen?: boolean;
|
|
@@ -106,22 +106,21 @@ interface UseDrawerProps {
|
|
|
106
106
|
declare function useDrawer(props: UseDrawerProps): {
|
|
107
107
|
activeSnapPoint: string | number | null;
|
|
108
108
|
snapPoints: (string | number)[] | undefined;
|
|
109
|
-
setActiveSnapPoint: (value:
|
|
110
|
-
drawerRef:
|
|
111
|
-
overlayRef:
|
|
109
|
+
setActiveSnapPoint: (value: React$1.SetStateAction<string | number | null>) => void;
|
|
110
|
+
drawerRef: React$1.RefObject<HTMLDivElement | null>;
|
|
111
|
+
overlayRef: React$1.RefObject<HTMLDivElement | null>;
|
|
112
112
|
shouldOverlayAnimate: boolean;
|
|
113
113
|
onOpenChange: ((open: boolean) => void) | undefined;
|
|
114
114
|
onPress: (event: React.PointerEvent<HTMLDivElement>) => void;
|
|
115
115
|
onRelease: (event: React.PointerEvent<HTMLDivElement> | null) => void;
|
|
116
116
|
onDrag: (event: React.PointerEvent<HTMLDivElement>) => void;
|
|
117
117
|
dismissible: boolean;
|
|
118
|
-
shouldAnimate: react.RefObject<boolean>;
|
|
119
118
|
handleOnly: boolean;
|
|
120
119
|
isOpen: boolean;
|
|
121
120
|
isDragging: boolean;
|
|
122
121
|
shouldFade: boolean;
|
|
123
122
|
closeDrawer: (fromWithin?: boolean) => void;
|
|
124
|
-
keyboardIsOpen:
|
|
123
|
+
keyboardIsOpen: React$1.RefObject<boolean>;
|
|
125
124
|
modal: boolean;
|
|
126
125
|
snapPointsOffset: number[];
|
|
127
126
|
activeSnapPointIndex: number | null;
|
|
@@ -129,62 +128,67 @@ declare function useDrawer(props: UseDrawerProps): {
|
|
|
129
128
|
noBodyStyles: boolean;
|
|
130
129
|
container: HTMLElement | null | undefined;
|
|
131
130
|
autoFocus: boolean;
|
|
132
|
-
setHasBeenOpened:
|
|
133
|
-
setIsOpen: (value:
|
|
131
|
+
setHasBeenOpened: React$1.Dispatch<React$1.SetStateAction<boolean>>;
|
|
132
|
+
setIsOpen: (value: React$1.SetStateAction<boolean>) => void;
|
|
134
133
|
closeOnInteractOutside: boolean;
|
|
135
134
|
closeOnEscape: boolean;
|
|
135
|
+
hasAnimationDone: boolean;
|
|
136
|
+
closeButtonRef: (node: HTMLButtonElement | null) => void;
|
|
137
|
+
isCloseButtonRendered: boolean;
|
|
136
138
|
};
|
|
137
139
|
|
|
138
140
|
interface DrawerRootProps extends UseDrawerProps {
|
|
139
|
-
children?:
|
|
141
|
+
children?: React$1.ReactNode;
|
|
140
142
|
}
|
|
141
143
|
declare const DrawerRoot: (props: DrawerRootProps) => react_jsx_runtime.JSX.Element;
|
|
142
144
|
interface DrawerTriggerProps extends DialogPrimitive.DialogTriggerProps {
|
|
143
145
|
}
|
|
144
|
-
declare const DrawerTrigger:
|
|
145
|
-
interface DrawerPositionerProps extends PrimitiveProps,
|
|
146
|
+
declare const DrawerTrigger: React$1.ForwardRefExoticComponent<DialogPrimitive.DialogTriggerProps & React$1.RefAttributes<HTMLButtonElement>>;
|
|
147
|
+
interface DrawerPositionerProps extends PrimitiveProps, React$1.HTMLAttributes<HTMLDivElement> {
|
|
146
148
|
}
|
|
147
|
-
declare const DrawerPositioner:
|
|
148
|
-
interface DrawerBackdropProps extends DialogPrimitive.DialogOverlayProps,
|
|
149
|
+
declare const DrawerPositioner: React$1.ForwardRefExoticComponent<DrawerPositionerProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
150
|
+
interface DrawerBackdropProps extends DialogPrimitive.DialogOverlayProps, React$1.HTMLAttributes<HTMLDivElement> {
|
|
149
151
|
}
|
|
150
|
-
declare const DrawerBackdrop:
|
|
151
|
-
interface DrawerContentProps extends DialogPrimitive.DialogContentProps,
|
|
152
|
+
declare const DrawerBackdrop: React$1.ForwardRefExoticComponent<DrawerBackdropProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
153
|
+
interface DrawerContentProps extends DialogPrimitive.DialogContentProps, React$1.HTMLAttributes<HTMLDivElement> {
|
|
152
154
|
}
|
|
153
|
-
declare const DrawerContent:
|
|
155
|
+
declare const DrawerContent: React$1.ForwardRefExoticComponent<DrawerContentProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
154
156
|
interface DrawerTitleProps extends DialogPrimitive.DialogTitleProps {
|
|
155
157
|
}
|
|
156
|
-
declare const DrawerTitle:
|
|
158
|
+
declare const DrawerTitle: React$1.ForwardRefExoticComponent<DialogPrimitive.DialogTitleProps & React$1.RefAttributes<HTMLHeadingElement>>;
|
|
157
159
|
interface DrawerDescriptionProps extends DialogPrimitive.DialogDescriptionProps {
|
|
158
160
|
}
|
|
159
|
-
declare const DrawerDescription:
|
|
161
|
+
declare const DrawerDescription: React$1.ForwardRefExoticComponent<DialogPrimitive.DialogDescriptionProps & React$1.RefAttributes<HTMLParagraphElement>>;
|
|
162
|
+
interface DrawerHeaderProps extends PrimitiveProps, React$1.HTMLAttributes<HTMLDivElement> {
|
|
163
|
+
}
|
|
164
|
+
declare const DrawerHeader: React$1.ForwardRefExoticComponent<DrawerHeaderProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
160
165
|
interface DrawerCloseButtonProps extends DialogPrimitive.DialogCloseProps {
|
|
161
166
|
}
|
|
162
|
-
declare const DrawerCloseButton:
|
|
163
|
-
interface DrawerHandleProps extends
|
|
167
|
+
declare const DrawerCloseButton: React$1.ForwardRefExoticComponent<DrawerCloseButtonProps & React$1.RefAttributes<HTMLButtonElement>>;
|
|
168
|
+
interface DrawerHandleProps extends React$1.HTMLAttributes<HTMLDivElement> {
|
|
164
169
|
preventCycle?: boolean;
|
|
165
170
|
}
|
|
166
|
-
declare const DrawerHandle:
|
|
171
|
+
declare const DrawerHandle: React$1.ForwardRefExoticComponent<DrawerHandleProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
167
172
|
|
|
168
173
|
type DrawerContextValue = ReturnType<typeof useDrawer>;
|
|
169
174
|
declare function useDrawerContext(): {
|
|
170
175
|
activeSnapPoint: string | number | null;
|
|
171
176
|
snapPoints: (string | number)[] | undefined;
|
|
172
|
-
setActiveSnapPoint: (value:
|
|
173
|
-
drawerRef:
|
|
174
|
-
overlayRef:
|
|
177
|
+
setActiveSnapPoint: (value: React$1.SetStateAction<string | number | null>) => void;
|
|
178
|
+
drawerRef: React$1.RefObject<HTMLDivElement | null>;
|
|
179
|
+
overlayRef: React$1.RefObject<HTMLDivElement | null>;
|
|
175
180
|
shouldOverlayAnimate: boolean;
|
|
176
181
|
onOpenChange: ((open: boolean) => void) | undefined;
|
|
177
182
|
onPress: (event: React.PointerEvent<HTMLDivElement>) => void;
|
|
178
183
|
onRelease: (event: React.PointerEvent<HTMLDivElement> | null) => void;
|
|
179
184
|
onDrag: (event: React.PointerEvent<HTMLDivElement>) => void;
|
|
180
185
|
dismissible: boolean;
|
|
181
|
-
shouldAnimate: react.RefObject<boolean>;
|
|
182
186
|
handleOnly: boolean;
|
|
183
187
|
isOpen: boolean;
|
|
184
188
|
isDragging: boolean;
|
|
185
189
|
shouldFade: boolean;
|
|
186
190
|
closeDrawer: (fromWithin?: boolean) => void;
|
|
187
|
-
keyboardIsOpen:
|
|
191
|
+
keyboardIsOpen: React$1.RefObject<boolean>;
|
|
188
192
|
modal: boolean;
|
|
189
193
|
snapPointsOffset: number[];
|
|
190
194
|
activeSnapPointIndex: number | null;
|
|
@@ -192,16 +196,19 @@ declare function useDrawerContext(): {
|
|
|
192
196
|
noBodyStyles: boolean;
|
|
193
197
|
container: HTMLElement | null | undefined;
|
|
194
198
|
autoFocus: boolean;
|
|
195
|
-
setHasBeenOpened:
|
|
196
|
-
setIsOpen: (value:
|
|
199
|
+
setHasBeenOpened: React$1.Dispatch<React$1.SetStateAction<boolean>>;
|
|
200
|
+
setIsOpen: (value: React$1.SetStateAction<boolean>) => void;
|
|
197
201
|
closeOnInteractOutside: boolean;
|
|
198
202
|
closeOnEscape: boolean;
|
|
203
|
+
hasAnimationDone: boolean;
|
|
204
|
+
closeButtonRef: (node: HTMLButtonElement | null) => void;
|
|
205
|
+
isCloseButtonRendered: boolean;
|
|
199
206
|
};
|
|
200
207
|
|
|
201
208
|
declare namespace Drawer_namespace {
|
|
202
|
-
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 };
|
|
203
|
-
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 };
|
|
209
|
+
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 };
|
|
210
|
+
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 };
|
|
204
211
|
}
|
|
205
212
|
|
|
206
|
-
export { Drawer_namespace as Drawer, DrawerBackdrop, DrawerCloseButton, DrawerContent, DrawerDescription, DrawerHandle, DrawerPositioner, DrawerRoot, DrawerTitle, DrawerTrigger, useDrawer, useDrawerContext };
|
|
207
|
-
export type { DrawerBackdropProps, DrawerCloseButtonProps, DrawerContentProps, DrawerContextValue, DrawerDescriptionProps, DrawerHandleProps, DrawerPositionerProps, DrawerRootProps, DrawerTitleProps, DrawerTriggerProps, UseDrawerProps };
|
|
213
|
+
export { Drawer_namespace as Drawer, DrawerBackdrop, DrawerCloseButton, DrawerContent, DrawerDescription, DrawerHandle, DrawerHeader, DrawerPositioner, DrawerRoot, DrawerTitle, DrawerTrigger, useDrawer, useDrawerContext };
|
|
214
|
+
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
|
|
2
|
-
export { u as useDrawer,
|
|
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';
|
|
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
package/src/Drawer.namespace.ts
CHANGED
|
@@ -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
|
@@ -71,14 +71,13 @@ export const DrawerBackdrop = forwardRef<HTMLDivElement, DrawerBackdropProps>((p
|
|
|
71
71
|
snapPoints,
|
|
72
72
|
isOpen,
|
|
73
73
|
shouldFade,
|
|
74
|
-
shouldAnimate,
|
|
75
74
|
shouldOverlayAnimate,
|
|
75
|
+
hasAnimationDone,
|
|
76
76
|
} = useDrawerContext();
|
|
77
77
|
const composedRef = useComposedRefs(ref, overlayRef);
|
|
78
78
|
const hasSnapPoints = snapPoints && snapPoints.length > 0;
|
|
79
79
|
const onMouseUp = useCallbackRef((event: React.PointerEvent<HTMLDivElement>) => onRelease(event));
|
|
80
80
|
|
|
81
|
-
// Overlay is the component that is locking scroll, removing it will unlock the scroll without having to dig into Radix's Dialog library
|
|
82
81
|
if (!modal) {
|
|
83
82
|
return null;
|
|
84
83
|
}
|
|
@@ -89,9 +88,9 @@ export const DrawerBackdrop = forwardRef<HTMLDivElement, DrawerBackdropProps>((p
|
|
|
89
88
|
onMouseUp={onMouseUp}
|
|
90
89
|
data-snap-points={isOpen && hasSnapPoints ? "true" : "false"}
|
|
91
90
|
data-snap-points-overlay={isOpen && shouldFade ? "true" : "false"}
|
|
92
|
-
data-animate={shouldAnimate?.current ? "true" : "false"}
|
|
93
91
|
data-should-overlay-animate={shouldOverlayAnimate ? "true" : "false"}
|
|
94
92
|
data-open={dataAttr(isOpen)}
|
|
93
|
+
data-animation-done={hasAnimationDone ? "true" : "false"}
|
|
95
94
|
{...props}
|
|
96
95
|
/>
|
|
97
96
|
);
|
|
@@ -118,12 +117,12 @@ export const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>((pro
|
|
|
118
117
|
snapPoints,
|
|
119
118
|
container,
|
|
120
119
|
handleOnly,
|
|
121
|
-
shouldAnimate,
|
|
122
120
|
autoFocus,
|
|
123
121
|
closeDrawer,
|
|
124
122
|
closeOnInteractOutside,
|
|
125
123
|
closeOnEscape,
|
|
126
124
|
dismissible,
|
|
125
|
+
hasAnimationDone,
|
|
127
126
|
} = useDrawerContext();
|
|
128
127
|
// Needed to use transition instead of animations
|
|
129
128
|
const [delayedSnapPoints, setDelayedSnapPoints] = useState(false);
|
|
@@ -180,10 +179,10 @@ export const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>((pro
|
|
|
180
179
|
data-delayed-snap-points={delayedSnapPoints ? "true" : "false"}
|
|
181
180
|
data-drawer-direction={direction}
|
|
182
181
|
data-open={dataAttr(isOpen)}
|
|
182
|
+
data-animation-done={hasAnimationDone ? "true" : "false"}
|
|
183
183
|
data-drawer=""
|
|
184
184
|
data-snap-points={isOpen && hasSnapPoints ? "true" : "false"}
|
|
185
185
|
data-custom-container={container ? "true" : "false"}
|
|
186
|
-
data-animate={shouldAnimate?.current ? "true" : "false"}
|
|
187
186
|
{...restProps}
|
|
188
187
|
ref={composedRef}
|
|
189
188
|
style={
|
|
@@ -220,10 +219,10 @@ export const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>((pro
|
|
|
220
219
|
}
|
|
221
220
|
}}
|
|
222
221
|
onFocusOutside={(e) => {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
222
|
+
props.onFocusOutside?.(e);
|
|
223
|
+
// Always prevent focusOutside to avoid conflicts when focus moves between modals
|
|
224
|
+
// (e.g., when Dialog closes and restores focus while BottomSheet is opening)
|
|
225
|
+
e.preventDefault();
|
|
227
226
|
}}
|
|
228
227
|
onPointerMove={(event) => {
|
|
229
228
|
lastKnownPointerEventRef.current = event;
|
|
@@ -262,7 +261,8 @@ export const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>((pro
|
|
|
262
261
|
}
|
|
263
262
|
}}
|
|
264
263
|
onInteractOutside={(e) => {
|
|
265
|
-
if (
|
|
264
|
+
// Only close if event is not prevented (e.g., by onFocusOutside or onPointerDownOutside)
|
|
265
|
+
if (dismissible && closeOnInteractOutside && !e.defaultPrevented) {
|
|
266
266
|
closeDrawer();
|
|
267
267
|
}
|
|
268
268
|
props.onInteractOutside?.(e);
|
|
@@ -286,19 +286,30 @@ export interface DrawerDescriptionProps extends DialogPrimitive.DialogDescriptio
|
|
|
286
286
|
|
|
287
287
|
export const DrawerDescription = DialogPrimitive.Description;
|
|
288
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
|
+
|
|
289
299
|
export interface DrawerCloseButtonProps extends DialogPrimitive.DialogCloseProps {}
|
|
290
300
|
|
|
291
301
|
export const DrawerCloseButton = forwardRef<HTMLButtonElement, DrawerCloseButtonProps>(
|
|
292
302
|
(props, ref) => {
|
|
293
|
-
const
|
|
303
|
+
const { closeButtonRef, setIsOpen } = useDrawerContext();
|
|
304
|
+
const composedRef = useComposedRefs(ref, closeButtonRef);
|
|
294
305
|
return (
|
|
295
306
|
<Primitive.button
|
|
296
|
-
ref={
|
|
307
|
+
ref={composedRef}
|
|
297
308
|
{...props}
|
|
298
309
|
onClick={(e) => {
|
|
299
310
|
props.onClick?.(e);
|
|
300
311
|
if (e.defaultPrevented) return;
|
|
301
|
-
|
|
312
|
+
setIsOpen(false);
|
|
302
313
|
}}
|
|
303
314
|
/>
|
|
304
315
|
);
|
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,
|
package/src/useDrawer.ts
CHANGED
|
@@ -65,7 +65,7 @@ export interface UseDrawerProps {
|
|
|
65
65
|
*/
|
|
66
66
|
direction?: "top" | "bottom" | "left" | "right";
|
|
67
67
|
/**
|
|
68
|
-
* Opened by default
|
|
68
|
+
* Opened by default. Still reacts to `open` state changes
|
|
69
69
|
* @default false
|
|
70
70
|
*/
|
|
71
71
|
defaultOpen?: boolean;
|
|
@@ -178,9 +178,15 @@ export function useDrawer(props: UseDrawerProps) {
|
|
|
178
178
|
});
|
|
179
179
|
|
|
180
180
|
const [hasBeenOpened, setHasBeenOpened] = useState<boolean>(false);
|
|
181
|
+
const [hasAnimationDone, setHasAnimationDone] = useState<boolean>(false);
|
|
181
182
|
const [isDragging, setIsDragging] = useState<boolean>(false);
|
|
182
183
|
const [shouldOverlayAnimate, setShouldOverlayAnimate] = useState<boolean>(false);
|
|
183
184
|
|
|
185
|
+
const [isCloseButtonRendered, setIsCloseButtonRendered] = useState<boolean>(false);
|
|
186
|
+
const closeButtonRef = useCallback((node: HTMLButtonElement | null) => {
|
|
187
|
+
setIsCloseButtonRendered(!!node);
|
|
188
|
+
}, []);
|
|
189
|
+
|
|
184
190
|
const overlayRef = useRef<HTMLDivElement>(null);
|
|
185
191
|
const openTime = useRef<Date | null>(null);
|
|
186
192
|
const dragStartTime = useRef<Date | null>(null);
|
|
@@ -189,7 +195,6 @@ export function useDrawer(props: UseDrawerProps) {
|
|
|
189
195
|
const isAllowedToDrag = useRef<boolean>(false);
|
|
190
196
|
const pointerStart = useRef(0);
|
|
191
197
|
const keyboardIsOpen = useRef(false);
|
|
192
|
-
const shouldAnimate = useRef(!defaultOpen);
|
|
193
198
|
const previousDiffFromInitial = useRef(0);
|
|
194
199
|
const drawerRef = useRef<HTMLDivElement>(null);
|
|
195
200
|
const drawerHeightRef = useRef(drawerRef.current?.getBoundingClientRect().height || 0);
|
|
@@ -543,12 +548,6 @@ export function useDrawer(props: UseDrawerProps) {
|
|
|
543
548
|
};
|
|
544
549
|
}, [isOpen]);
|
|
545
550
|
|
|
546
|
-
useEffect(() => {
|
|
547
|
-
window.requestAnimationFrame(() => {
|
|
548
|
-
shouldAnimate.current = true;
|
|
549
|
-
});
|
|
550
|
-
}, []);
|
|
551
|
-
|
|
552
551
|
useEffect(() => {
|
|
553
552
|
function onVisualViewportChange() {
|
|
554
553
|
if (!drawerRef.current || !repositionInputs) return;
|
|
@@ -614,12 +613,32 @@ export function useDrawer(props: UseDrawerProps) {
|
|
|
614
613
|
}
|
|
615
614
|
}, [modal]);
|
|
616
615
|
|
|
616
|
+
// Effect 1: Track drawer open state
|
|
617
617
|
useEffect(() => {
|
|
618
618
|
if (isOpen) {
|
|
619
619
|
setHasBeenOpened(true);
|
|
620
620
|
}
|
|
621
621
|
}, [isOpen]);
|
|
622
622
|
|
|
623
|
+
// Effect 2: Handle animation state and timer
|
|
624
|
+
useEffect(() => {
|
|
625
|
+
if (isOpen) {
|
|
626
|
+
// Only reset animation state if this is the first open
|
|
627
|
+
if (!hasBeenOpened) {
|
|
628
|
+
setHasAnimationDone(false);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const timeoutId = setTimeout(() => {
|
|
632
|
+
setHasAnimationDone(true);
|
|
633
|
+
}, TRANSITIONS.ENTER_DURATION * 1000);
|
|
634
|
+
|
|
635
|
+
return () => clearTimeout(timeoutId);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// Reset animation state when drawer closes
|
|
639
|
+
setHasAnimationDone(false);
|
|
640
|
+
}, [isOpen, hasBeenOpened]);
|
|
641
|
+
|
|
623
642
|
useEffect(() => {
|
|
624
643
|
if (isOpen && snapPoints && fadeFromIndex === 0) {
|
|
625
644
|
setShouldOverlayAnimate(true);
|
|
@@ -647,7 +666,6 @@ export function useDrawer(props: UseDrawerProps) {
|
|
|
647
666
|
onRelease,
|
|
648
667
|
onDrag,
|
|
649
668
|
dismissible,
|
|
650
|
-
shouldAnimate,
|
|
651
669
|
handleOnly,
|
|
652
670
|
isOpen,
|
|
653
671
|
isDragging,
|
|
@@ -665,6 +683,9 @@ export function useDrawer(props: UseDrawerProps) {
|
|
|
665
683
|
setIsOpen,
|
|
666
684
|
closeOnInteractOutside,
|
|
667
685
|
closeOnEscape,
|
|
686
|
+
hasAnimationDone,
|
|
687
|
+
closeButtonRef,
|
|
688
|
+
isCloseButtonRendered,
|
|
668
689
|
}),
|
|
669
690
|
[
|
|
670
691
|
activeSnapPoint,
|
|
@@ -691,6 +712,9 @@ export function useDrawer(props: UseDrawerProps) {
|
|
|
691
712
|
onRelease,
|
|
692
713
|
onDrag,
|
|
693
714
|
onPress,
|
|
715
|
+
hasAnimationDone,
|
|
716
|
+
closeButtonRef,
|
|
717
|
+
isCloseButtonRendered,
|
|
694
718
|
],
|
|
695
719
|
);
|
|
696
720
|
}
|