@seed-design/react-drawer 1.0.7 → 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.
- package/lib/{Drawer-12s-L4KZ1h_g.cjs → Drawer-12s-DXUv7jE4.cjs} +96 -26
- package/lib/{Drawer-12s-1gup1cyj.js → Drawer-12s-DnXlK1K0.js} +97 -28
- 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/index.ts +2 -0
- package/src/use-snap-points.ts +6 -4
- package/src/useDrawer.ts +58 -9
|
@@ -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
|
|
|
@@ -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
|
}
|
|
@@ -505,8 +511,13 @@ function useDrawer(props) {
|
|
|
505
511
|
}
|
|
506
512
|
});
|
|
507
513
|
const [hasBeenOpened, setHasBeenOpened] = React.useState(false);
|
|
514
|
+
const [hasAnimationDone, setHasAnimationDone] = React.useState(false);
|
|
508
515
|
const [isDragging, setIsDragging] = React.useState(false);
|
|
509
516
|
const [shouldOverlayAnimate, setShouldOverlayAnimate] = React.useState(false);
|
|
517
|
+
const [isCloseButtonRendered, setIsCloseButtonRendered] = React.useState(false);
|
|
518
|
+
const closeButtonRef = React.useCallback((node)=>{
|
|
519
|
+
setIsCloseButtonRendered(!!node);
|
|
520
|
+
}, []);
|
|
510
521
|
const overlayRef = React.useRef(null);
|
|
511
522
|
const openTime = React.useRef(null);
|
|
512
523
|
const dragStartTime = React.useRef(null);
|
|
@@ -675,11 +686,11 @@ function useDrawer(props) {
|
|
|
675
686
|
}, [
|
|
676
687
|
isDragging
|
|
677
688
|
]);
|
|
678
|
-
const closeDrawer = React.useCallback((fromWithin)=>{
|
|
689
|
+
const closeDrawer = React.useCallback((fromWithin, details)=>{
|
|
679
690
|
cancelDrag();
|
|
680
691
|
onClose?.();
|
|
681
692
|
if (!fromWithin) {
|
|
682
|
-
setIsOpen(false);
|
|
693
|
+
setIsOpen(false, details);
|
|
683
694
|
}
|
|
684
695
|
if (fadeFromIndex !== undefined && fadeFromIndex > 0 && activeSnapPointIndex === 0) {
|
|
685
696
|
set(overlayRef.current, {
|
|
@@ -729,7 +740,8 @@ function useDrawer(props) {
|
|
|
729
740
|
draggedDistance: distMoved * directionMultiplier,
|
|
730
741
|
closeDrawer,
|
|
731
742
|
velocity,
|
|
732
|
-
dismissible
|
|
743
|
+
dismissible,
|
|
744
|
+
event: event.nativeEvent
|
|
733
745
|
});
|
|
734
746
|
onReleaseProp?.(event, true);
|
|
735
747
|
return;
|
|
@@ -740,7 +752,10 @@ function useDrawer(props) {
|
|
|
740
752
|
return;
|
|
741
753
|
}
|
|
742
754
|
if (velocity > VELOCITY_THRESHOLD) {
|
|
743
|
-
closeDrawer(
|
|
755
|
+
closeDrawer(false, {
|
|
756
|
+
reason: "drag",
|
|
757
|
+
event: event.nativeEvent
|
|
758
|
+
});
|
|
744
759
|
onReleaseProp?.(event, false);
|
|
745
760
|
return;
|
|
746
761
|
}
|
|
@@ -748,7 +763,10 @@ function useDrawer(props) {
|
|
|
748
763
|
const visibleDrawerWidth = Math.min(drawerRef.current.getBoundingClientRect().width ?? 0, window.innerWidth);
|
|
749
764
|
const isHorizontalSwipe = direction === "left" || direction === "right";
|
|
750
765
|
if (Math.abs(swipeAmount) >= (isHorizontalSwipe ? visibleDrawerWidth : visibleDrawerHeight) * closeThreshold) {
|
|
751
|
-
closeDrawer(
|
|
766
|
+
closeDrawer(false, {
|
|
767
|
+
reason: "drag",
|
|
768
|
+
event: event.nativeEvent
|
|
769
|
+
});
|
|
752
770
|
onReleaseProp?.(event, false);
|
|
753
771
|
return;
|
|
754
772
|
}
|
|
@@ -829,6 +847,7 @@ function useDrawer(props) {
|
|
|
829
847
|
}, [
|
|
830
848
|
modal
|
|
831
849
|
]);
|
|
850
|
+
// Effect 1: Track drawer open state
|
|
832
851
|
React.useEffect(()=>{
|
|
833
852
|
if (isOpen) {
|
|
834
853
|
setHasBeenOpened(true);
|
|
@@ -836,6 +855,24 @@ function useDrawer(props) {
|
|
|
836
855
|
}, [
|
|
837
856
|
isOpen
|
|
838
857
|
]);
|
|
858
|
+
// Effect 2: Handle animation state and timer
|
|
859
|
+
React.useEffect(()=>{
|
|
860
|
+
if (isOpen) {
|
|
861
|
+
// Only reset animation state if this is the first open
|
|
862
|
+
if (!hasBeenOpened) {
|
|
863
|
+
setHasAnimationDone(false);
|
|
864
|
+
}
|
|
865
|
+
const timeoutId = setTimeout(()=>{
|
|
866
|
+
setHasAnimationDone(true);
|
|
867
|
+
}, TRANSITIONS.ENTER_DURATION * 1000);
|
|
868
|
+
return ()=>clearTimeout(timeoutId);
|
|
869
|
+
}
|
|
870
|
+
// Reset animation state when drawer closes
|
|
871
|
+
setHasAnimationDone(false);
|
|
872
|
+
}, [
|
|
873
|
+
isOpen,
|
|
874
|
+
hasBeenOpened
|
|
875
|
+
]);
|
|
839
876
|
React.useEffect(()=>{
|
|
840
877
|
if (isOpen && snapPoints && fadeFromIndex === 0) {
|
|
841
878
|
setShouldOverlayAnimate(true);
|
|
@@ -878,7 +915,10 @@ function useDrawer(props) {
|
|
|
878
915
|
setHasBeenOpened,
|
|
879
916
|
setIsOpen,
|
|
880
917
|
closeOnInteractOutside,
|
|
881
|
-
closeOnEscape
|
|
918
|
+
closeOnEscape,
|
|
919
|
+
hasAnimationDone,
|
|
920
|
+
closeButtonRef,
|
|
921
|
+
isCloseButtonRendered
|
|
882
922
|
}), [
|
|
883
923
|
activeSnapPoint,
|
|
884
924
|
snapPoints,
|
|
@@ -903,7 +943,10 @@ function useDrawer(props) {
|
|
|
903
943
|
closeOnEscape,
|
|
904
944
|
onRelease,
|
|
905
945
|
onDrag,
|
|
906
|
-
onPress
|
|
946
|
+
onPress,
|
|
947
|
+
hasAnimationDone,
|
|
948
|
+
closeButtonRef,
|
|
949
|
+
isCloseButtonRendered
|
|
907
950
|
]);
|
|
908
951
|
}
|
|
909
952
|
|
|
@@ -953,11 +996,10 @@ const DrawerPositioner = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
953
996
|
});
|
|
954
997
|
DrawerPositioner.displayName = "DrawerPositioner";
|
|
955
998
|
const DrawerBackdrop = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
956
|
-
const { overlayRef, onRelease, modal, snapPoints, isOpen, shouldFade, shouldOverlayAnimate } = useDrawerContext();
|
|
999
|
+
const { overlayRef, onRelease, modal, snapPoints, isOpen, shouldFade, shouldOverlayAnimate, hasAnimationDone } = useDrawerContext();
|
|
957
1000
|
const composedRef = reactComposeRefs.useComposedRefs(ref, overlayRef);
|
|
958
1001
|
const hasSnapPoints = snapPoints && snapPoints.length > 0;
|
|
959
1002
|
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
1003
|
if (!modal) {
|
|
962
1004
|
return null;
|
|
963
1005
|
}
|
|
@@ -968,13 +1010,14 @@ const DrawerBackdrop = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
968
1010
|
"data-snap-points-overlay": isOpen && shouldFade ? "true" : "false",
|
|
969
1011
|
"data-should-overlay-animate": shouldOverlayAnimate ? "true" : "false",
|
|
970
1012
|
"data-open": domUtils.dataAttr(isOpen),
|
|
1013
|
+
"data-animation-done": hasAnimationDone ? "true" : "false",
|
|
971
1014
|
...props
|
|
972
1015
|
});
|
|
973
1016
|
});
|
|
974
1017
|
DrawerBackdrop.displayName = "DrawerBackdrop";
|
|
975
1018
|
const DrawerContent = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
976
1019
|
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();
|
|
1020
|
+
const { drawerRef, onPress, onRelease, onDrag, keyboardIsOpen, snapPointsOffset, activeSnapPointIndex, modal, isOpen, direction, snapPoints, container, handleOnly, autoFocus, closeDrawer, closeOnInteractOutside, closeOnEscape, dismissible, hasAnimationDone } = useDrawerContext();
|
|
978
1021
|
// Needed to use transition instead of animations
|
|
979
1022
|
const [delayedSnapPoints, setDelayedSnapPoints] = React.useState(false);
|
|
980
1023
|
const composedRef = reactComposeRefs.useComposedRefs(ref, drawerRef);
|
|
@@ -1021,6 +1064,7 @@ const DrawerContent = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
1021
1064
|
"data-delayed-snap-points": delayedSnapPoints ? "true" : "false",
|
|
1022
1065
|
"data-drawer-direction": direction,
|
|
1023
1066
|
"data-open": domUtils.dataAttr(isOpen),
|
|
1067
|
+
"data-animation-done": hasAnimationDone ? "true" : "false",
|
|
1024
1068
|
"data-drawer": "",
|
|
1025
1069
|
"data-snap-points": isOpen && hasSnapPoints ? "true" : "false",
|
|
1026
1070
|
"data-custom-container": container ? "true" : "false",
|
|
@@ -1098,13 +1142,19 @@ const DrawerContent = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
1098
1142
|
onInteractOutside: (e)=>{
|
|
1099
1143
|
// Only close if event is not prevented (e.g., by onFocusOutside or onPointerDownOutside)
|
|
1100
1144
|
if (dismissible && closeOnInteractOutside && !e.defaultPrevented) {
|
|
1101
|
-
closeDrawer(
|
|
1145
|
+
closeDrawer(false, {
|
|
1146
|
+
reason: "interactOutside",
|
|
1147
|
+
event: e.detail.originalEvent
|
|
1148
|
+
});
|
|
1102
1149
|
}
|
|
1103
1150
|
props.onInteractOutside?.(e);
|
|
1104
1151
|
},
|
|
1105
1152
|
onEscapeKeyDown: (e)=>{
|
|
1106
1153
|
if (dismissible && closeOnEscape) {
|
|
1107
|
-
closeDrawer(
|
|
1154
|
+
closeDrawer(false, {
|
|
1155
|
+
reason: "escapeKeyDown",
|
|
1156
|
+
event: e
|
|
1157
|
+
});
|
|
1108
1158
|
}
|
|
1109
1159
|
props.onEscapeKeyDown?.(e);
|
|
1110
1160
|
}
|
|
@@ -1113,15 +1163,28 @@ const DrawerContent = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
1113
1163
|
DrawerContent.displayName = "DrawerContent";
|
|
1114
1164
|
const DrawerTitle = DialogPrimitive__namespace.Title;
|
|
1115
1165
|
const DrawerDescription = DialogPrimitive__namespace.Description;
|
|
1166
|
+
const DrawerHeader = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
1167
|
+
const { isCloseButtonRendered } = useDrawerContext();
|
|
1168
|
+
return /*#__PURE__*/ jsxRuntime.jsx(reactPrimitive.Primitive.div, {
|
|
1169
|
+
ref: ref,
|
|
1170
|
+
"data-show-close-button": domUtils.dataAttr(isCloseButtonRendered),
|
|
1171
|
+
...props
|
|
1172
|
+
});
|
|
1173
|
+
});
|
|
1174
|
+
DrawerHeader.displayName = "DrawerHeader";
|
|
1116
1175
|
const DrawerCloseButton = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
1117
|
-
const
|
|
1176
|
+
const { closeButtonRef, setIsOpen } = useDrawerContext();
|
|
1177
|
+
const composedRef = reactComposeRefs.useComposedRefs(ref, closeButtonRef);
|
|
1118
1178
|
return /*#__PURE__*/ jsxRuntime.jsx(reactPrimitive.Primitive.button, {
|
|
1119
|
-
ref:
|
|
1179
|
+
ref: composedRef,
|
|
1120
1180
|
...props,
|
|
1121
1181
|
onClick: (e)=>{
|
|
1122
1182
|
props.onClick?.(e);
|
|
1123
1183
|
if (e.defaultPrevented) return;
|
|
1124
|
-
|
|
1184
|
+
setIsOpen(false, {
|
|
1185
|
+
reason: "closeButton",
|
|
1186
|
+
event: e.nativeEvent
|
|
1187
|
+
});
|
|
1125
1188
|
}
|
|
1126
1189
|
});
|
|
1127
1190
|
});
|
|
@@ -1132,17 +1195,17 @@ const DrawerHandle = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
1132
1195
|
const { closeDrawer, isDragging, snapPoints, activeSnapPoint, setActiveSnapPoint, dismissible, handleOnly, isOpen, onPress, onDrag, onRelease } = useDrawerContext();
|
|
1133
1196
|
const closeTimeoutIdRef = React.useRef(null);
|
|
1134
1197
|
const shouldCancelInteractionRef = React.useRef(false);
|
|
1135
|
-
function handleStartCycle() {
|
|
1198
|
+
function handleStartCycle(event) {
|
|
1136
1199
|
// Stop if this is the second click of a double click
|
|
1137
1200
|
if (shouldCancelInteractionRef.current) {
|
|
1138
1201
|
handleCancelInteraction();
|
|
1139
1202
|
return;
|
|
1140
1203
|
}
|
|
1141
1204
|
window.setTimeout(()=>{
|
|
1142
|
-
handleCycleSnapPoints();
|
|
1205
|
+
handleCycleSnapPoints(event);
|
|
1143
1206
|
}, DOUBLE_TAP_TIMEOUT);
|
|
1144
1207
|
}
|
|
1145
|
-
function handleCycleSnapPoints() {
|
|
1208
|
+
function handleCycleSnapPoints(event) {
|
|
1146
1209
|
// Prevent accidental taps while resizing drawer
|
|
1147
1210
|
if (isDragging || preventCycle || shouldCancelInteractionRef.current) {
|
|
1148
1211
|
handleCancelInteraction();
|
|
@@ -1152,13 +1215,19 @@ const DrawerHandle = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
1152
1215
|
handleCancelInteraction();
|
|
1153
1216
|
if (!snapPoints || snapPoints.length === 0) {
|
|
1154
1217
|
if (!dismissible) {
|
|
1155
|
-
closeDrawer(
|
|
1218
|
+
closeDrawer(false, {
|
|
1219
|
+
reason: "handleClickOnLastSnapPoint",
|
|
1220
|
+
event: event.nativeEvent
|
|
1221
|
+
});
|
|
1156
1222
|
}
|
|
1157
1223
|
return;
|
|
1158
1224
|
}
|
|
1159
1225
|
const isLastSnapPoint = activeSnapPoint === snapPoints[snapPoints.length - 1];
|
|
1160
1226
|
if (isLastSnapPoint && dismissible) {
|
|
1161
|
-
closeDrawer(
|
|
1227
|
+
closeDrawer(false, {
|
|
1228
|
+
reason: "handleClickOnLastSnapPoint",
|
|
1229
|
+
event: event.nativeEvent
|
|
1230
|
+
});
|
|
1162
1231
|
return;
|
|
1163
1232
|
}
|
|
1164
1233
|
const currentSnapIndex = snapPoints.findIndex((point)=>point === activeSnapPoint);
|
|
@@ -1207,6 +1276,7 @@ exports.DrawerCloseButton = DrawerCloseButton;
|
|
|
1207
1276
|
exports.DrawerContent = DrawerContent;
|
|
1208
1277
|
exports.DrawerDescription = DrawerDescription;
|
|
1209
1278
|
exports.DrawerHandle = DrawerHandle;
|
|
1279
|
+
exports.DrawerHeader = DrawerHeader;
|
|
1210
1280
|
exports.DrawerPositioner = DrawerPositioner;
|
|
1211
1281
|
exports.DrawerRoot = DrawerRoot;
|
|
1212
1282
|
exports.DrawerTitle = DrawerTitle;
|
|
@@ -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;
|
|
@@ -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
|
}
|
|
@@ -482,8 +488,13 @@ function useDrawer(props) {
|
|
|
482
488
|
}
|
|
483
489
|
});
|
|
484
490
|
const [hasBeenOpened, setHasBeenOpened] = useState(false);
|
|
491
|
+
const [hasAnimationDone, setHasAnimationDone] = useState(false);
|
|
485
492
|
const [isDragging, setIsDragging] = useState(false);
|
|
486
493
|
const [shouldOverlayAnimate, setShouldOverlayAnimate] = useState(false);
|
|
494
|
+
const [isCloseButtonRendered, setIsCloseButtonRendered] = useState(false);
|
|
495
|
+
const closeButtonRef = useCallback((node)=>{
|
|
496
|
+
setIsCloseButtonRendered(!!node);
|
|
497
|
+
}, []);
|
|
487
498
|
const overlayRef = useRef(null);
|
|
488
499
|
const openTime = useRef(null);
|
|
489
500
|
const dragStartTime = useRef(null);
|
|
@@ -652,11 +663,11 @@ function useDrawer(props) {
|
|
|
652
663
|
}, [
|
|
653
664
|
isDragging
|
|
654
665
|
]);
|
|
655
|
-
const closeDrawer = useCallback((fromWithin)=>{
|
|
666
|
+
const closeDrawer = useCallback((fromWithin, details)=>{
|
|
656
667
|
cancelDrag();
|
|
657
668
|
onClose?.();
|
|
658
669
|
if (!fromWithin) {
|
|
659
|
-
setIsOpen(false);
|
|
670
|
+
setIsOpen(false, details);
|
|
660
671
|
}
|
|
661
672
|
if (fadeFromIndex !== undefined && fadeFromIndex > 0 && activeSnapPointIndex === 0) {
|
|
662
673
|
set(overlayRef.current, {
|
|
@@ -706,7 +717,8 @@ function useDrawer(props) {
|
|
|
706
717
|
draggedDistance: distMoved * directionMultiplier,
|
|
707
718
|
closeDrawer,
|
|
708
719
|
velocity,
|
|
709
|
-
dismissible
|
|
720
|
+
dismissible,
|
|
721
|
+
event: event.nativeEvent
|
|
710
722
|
});
|
|
711
723
|
onReleaseProp?.(event, true);
|
|
712
724
|
return;
|
|
@@ -717,7 +729,10 @@ function useDrawer(props) {
|
|
|
717
729
|
return;
|
|
718
730
|
}
|
|
719
731
|
if (velocity > VELOCITY_THRESHOLD) {
|
|
720
|
-
closeDrawer(
|
|
732
|
+
closeDrawer(false, {
|
|
733
|
+
reason: "drag",
|
|
734
|
+
event: event.nativeEvent
|
|
735
|
+
});
|
|
721
736
|
onReleaseProp?.(event, false);
|
|
722
737
|
return;
|
|
723
738
|
}
|
|
@@ -725,7 +740,10 @@ function useDrawer(props) {
|
|
|
725
740
|
const visibleDrawerWidth = Math.min(drawerRef.current.getBoundingClientRect().width ?? 0, window.innerWidth);
|
|
726
741
|
const isHorizontalSwipe = direction === "left" || direction === "right";
|
|
727
742
|
if (Math.abs(swipeAmount) >= (isHorizontalSwipe ? visibleDrawerWidth : visibleDrawerHeight) * closeThreshold) {
|
|
728
|
-
closeDrawer(
|
|
743
|
+
closeDrawer(false, {
|
|
744
|
+
reason: "drag",
|
|
745
|
+
event: event.nativeEvent
|
|
746
|
+
});
|
|
729
747
|
onReleaseProp?.(event, false);
|
|
730
748
|
return;
|
|
731
749
|
}
|
|
@@ -806,6 +824,7 @@ function useDrawer(props) {
|
|
|
806
824
|
}, [
|
|
807
825
|
modal
|
|
808
826
|
]);
|
|
827
|
+
// Effect 1: Track drawer open state
|
|
809
828
|
useEffect(()=>{
|
|
810
829
|
if (isOpen) {
|
|
811
830
|
setHasBeenOpened(true);
|
|
@@ -813,6 +832,24 @@ function useDrawer(props) {
|
|
|
813
832
|
}, [
|
|
814
833
|
isOpen
|
|
815
834
|
]);
|
|
835
|
+
// Effect 2: Handle animation state and timer
|
|
836
|
+
useEffect(()=>{
|
|
837
|
+
if (isOpen) {
|
|
838
|
+
// Only reset animation state if this is the first open
|
|
839
|
+
if (!hasBeenOpened) {
|
|
840
|
+
setHasAnimationDone(false);
|
|
841
|
+
}
|
|
842
|
+
const timeoutId = setTimeout(()=>{
|
|
843
|
+
setHasAnimationDone(true);
|
|
844
|
+
}, TRANSITIONS.ENTER_DURATION * 1000);
|
|
845
|
+
return ()=>clearTimeout(timeoutId);
|
|
846
|
+
}
|
|
847
|
+
// Reset animation state when drawer closes
|
|
848
|
+
setHasAnimationDone(false);
|
|
849
|
+
}, [
|
|
850
|
+
isOpen,
|
|
851
|
+
hasBeenOpened
|
|
852
|
+
]);
|
|
816
853
|
useEffect(()=>{
|
|
817
854
|
if (isOpen && snapPoints && fadeFromIndex === 0) {
|
|
818
855
|
setShouldOverlayAnimate(true);
|
|
@@ -855,7 +892,10 @@ function useDrawer(props) {
|
|
|
855
892
|
setHasBeenOpened,
|
|
856
893
|
setIsOpen,
|
|
857
894
|
closeOnInteractOutside,
|
|
858
|
-
closeOnEscape
|
|
895
|
+
closeOnEscape,
|
|
896
|
+
hasAnimationDone,
|
|
897
|
+
closeButtonRef,
|
|
898
|
+
isCloseButtonRendered
|
|
859
899
|
}), [
|
|
860
900
|
activeSnapPoint,
|
|
861
901
|
snapPoints,
|
|
@@ -880,7 +920,10 @@ function useDrawer(props) {
|
|
|
880
920
|
closeOnEscape,
|
|
881
921
|
onRelease,
|
|
882
922
|
onDrag,
|
|
883
|
-
onPress
|
|
923
|
+
onPress,
|
|
924
|
+
hasAnimationDone,
|
|
925
|
+
closeButtonRef,
|
|
926
|
+
isCloseButtonRendered
|
|
884
927
|
]);
|
|
885
928
|
}
|
|
886
929
|
|
|
@@ -930,11 +973,10 @@ const DrawerPositioner = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
930
973
|
});
|
|
931
974
|
DrawerPositioner.displayName = "DrawerPositioner";
|
|
932
975
|
const DrawerBackdrop = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
933
|
-
const { overlayRef, onRelease, modal, snapPoints, isOpen, shouldFade, shouldOverlayAnimate } = useDrawerContext();
|
|
976
|
+
const { overlayRef, onRelease, modal, snapPoints, isOpen, shouldFade, shouldOverlayAnimate, hasAnimationDone } = useDrawerContext();
|
|
934
977
|
const composedRef = useComposedRefs(ref, overlayRef);
|
|
935
978
|
const hasSnapPoints = snapPoints && snapPoints.length > 0;
|
|
936
979
|
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
980
|
if (!modal) {
|
|
939
981
|
return null;
|
|
940
982
|
}
|
|
@@ -945,13 +987,14 @@ const DrawerBackdrop = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
945
987
|
"data-snap-points-overlay": isOpen && shouldFade ? "true" : "false",
|
|
946
988
|
"data-should-overlay-animate": shouldOverlayAnimate ? "true" : "false",
|
|
947
989
|
"data-open": dataAttr(isOpen),
|
|
990
|
+
"data-animation-done": hasAnimationDone ? "true" : "false",
|
|
948
991
|
...props
|
|
949
992
|
});
|
|
950
993
|
});
|
|
951
994
|
DrawerBackdrop.displayName = "DrawerBackdrop";
|
|
952
995
|
const DrawerContent = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
953
996
|
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();
|
|
997
|
+
const { drawerRef, onPress, onRelease, onDrag, keyboardIsOpen, snapPointsOffset, activeSnapPointIndex, modal, isOpen, direction, snapPoints, container, handleOnly, autoFocus, closeDrawer, closeOnInteractOutside, closeOnEscape, dismissible, hasAnimationDone } = useDrawerContext();
|
|
955
998
|
// Needed to use transition instead of animations
|
|
956
999
|
const [delayedSnapPoints, setDelayedSnapPoints] = useState(false);
|
|
957
1000
|
const composedRef = useComposedRefs(ref, drawerRef);
|
|
@@ -998,6 +1041,7 @@ const DrawerContent = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
998
1041
|
"data-delayed-snap-points": delayedSnapPoints ? "true" : "false",
|
|
999
1042
|
"data-drawer-direction": direction,
|
|
1000
1043
|
"data-open": dataAttr(isOpen),
|
|
1044
|
+
"data-animation-done": hasAnimationDone ? "true" : "false",
|
|
1001
1045
|
"data-drawer": "",
|
|
1002
1046
|
"data-snap-points": isOpen && hasSnapPoints ? "true" : "false",
|
|
1003
1047
|
"data-custom-container": container ? "true" : "false",
|
|
@@ -1075,13 +1119,19 @@ const DrawerContent = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
1075
1119
|
onInteractOutside: (e)=>{
|
|
1076
1120
|
// Only close if event is not prevented (e.g., by onFocusOutside or onPointerDownOutside)
|
|
1077
1121
|
if (dismissible && closeOnInteractOutside && !e.defaultPrevented) {
|
|
1078
|
-
closeDrawer(
|
|
1122
|
+
closeDrawer(false, {
|
|
1123
|
+
reason: "interactOutside",
|
|
1124
|
+
event: e.detail.originalEvent
|
|
1125
|
+
});
|
|
1079
1126
|
}
|
|
1080
1127
|
props.onInteractOutside?.(e);
|
|
1081
1128
|
},
|
|
1082
1129
|
onEscapeKeyDown: (e)=>{
|
|
1083
1130
|
if (dismissible && closeOnEscape) {
|
|
1084
|
-
closeDrawer(
|
|
1131
|
+
closeDrawer(false, {
|
|
1132
|
+
reason: "escapeKeyDown",
|
|
1133
|
+
event: e
|
|
1134
|
+
});
|
|
1085
1135
|
}
|
|
1086
1136
|
props.onEscapeKeyDown?.(e);
|
|
1087
1137
|
}
|
|
@@ -1090,15 +1140,28 @@ const DrawerContent = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
1090
1140
|
DrawerContent.displayName = "DrawerContent";
|
|
1091
1141
|
const DrawerTitle = DialogPrimitive.Title;
|
|
1092
1142
|
const DrawerDescription = DialogPrimitive.Description;
|
|
1143
|
+
const DrawerHeader = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
1144
|
+
const { isCloseButtonRendered } = useDrawerContext();
|
|
1145
|
+
return /*#__PURE__*/ jsx(Primitive.div, {
|
|
1146
|
+
ref: ref,
|
|
1147
|
+
"data-show-close-button": dataAttr(isCloseButtonRendered),
|
|
1148
|
+
...props
|
|
1149
|
+
});
|
|
1150
|
+
});
|
|
1151
|
+
DrawerHeader.displayName = "DrawerHeader";
|
|
1093
1152
|
const DrawerCloseButton = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
1094
|
-
const
|
|
1153
|
+
const { closeButtonRef, setIsOpen } = useDrawerContext();
|
|
1154
|
+
const composedRef = useComposedRefs(ref, closeButtonRef);
|
|
1095
1155
|
return /*#__PURE__*/ jsx(Primitive.button, {
|
|
1096
|
-
ref:
|
|
1156
|
+
ref: composedRef,
|
|
1097
1157
|
...props,
|
|
1098
1158
|
onClick: (e)=>{
|
|
1099
1159
|
props.onClick?.(e);
|
|
1100
1160
|
if (e.defaultPrevented) return;
|
|
1101
|
-
|
|
1161
|
+
setIsOpen(false, {
|
|
1162
|
+
reason: "closeButton",
|
|
1163
|
+
event: e.nativeEvent
|
|
1164
|
+
});
|
|
1102
1165
|
}
|
|
1103
1166
|
});
|
|
1104
1167
|
});
|
|
@@ -1109,17 +1172,17 @@ const DrawerHandle = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
1109
1172
|
const { closeDrawer, isDragging, snapPoints, activeSnapPoint, setActiveSnapPoint, dismissible, handleOnly, isOpen, onPress, onDrag, onRelease } = useDrawerContext();
|
|
1110
1173
|
const closeTimeoutIdRef = useRef(null);
|
|
1111
1174
|
const shouldCancelInteractionRef = useRef(false);
|
|
1112
|
-
function handleStartCycle() {
|
|
1175
|
+
function handleStartCycle(event) {
|
|
1113
1176
|
// Stop if this is the second click of a double click
|
|
1114
1177
|
if (shouldCancelInteractionRef.current) {
|
|
1115
1178
|
handleCancelInteraction();
|
|
1116
1179
|
return;
|
|
1117
1180
|
}
|
|
1118
1181
|
window.setTimeout(()=>{
|
|
1119
|
-
handleCycleSnapPoints();
|
|
1182
|
+
handleCycleSnapPoints(event);
|
|
1120
1183
|
}, DOUBLE_TAP_TIMEOUT);
|
|
1121
1184
|
}
|
|
1122
|
-
function handleCycleSnapPoints() {
|
|
1185
|
+
function handleCycleSnapPoints(event) {
|
|
1123
1186
|
// Prevent accidental taps while resizing drawer
|
|
1124
1187
|
if (isDragging || preventCycle || shouldCancelInteractionRef.current) {
|
|
1125
1188
|
handleCancelInteraction();
|
|
@@ -1129,13 +1192,19 @@ const DrawerHandle = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
1129
1192
|
handleCancelInteraction();
|
|
1130
1193
|
if (!snapPoints || snapPoints.length === 0) {
|
|
1131
1194
|
if (!dismissible) {
|
|
1132
|
-
closeDrawer(
|
|
1195
|
+
closeDrawer(false, {
|
|
1196
|
+
reason: "handleClickOnLastSnapPoint",
|
|
1197
|
+
event: event.nativeEvent
|
|
1198
|
+
});
|
|
1133
1199
|
}
|
|
1134
1200
|
return;
|
|
1135
1201
|
}
|
|
1136
1202
|
const isLastSnapPoint = activeSnapPoint === snapPoints[snapPoints.length - 1];
|
|
1137
1203
|
if (isLastSnapPoint && dismissible) {
|
|
1138
|
-
closeDrawer(
|
|
1204
|
+
closeDrawer(false, {
|
|
1205
|
+
reason: "handleClickOnLastSnapPoint",
|
|
1206
|
+
event: event.nativeEvent
|
|
1207
|
+
});
|
|
1139
1208
|
return;
|
|
1140
1209
|
}
|
|
1141
1210
|
const currentSnapIndex = snapPoints.findIndex((point)=>point === activeSnapPoint);
|
|
@@ -1179,4 +1248,4 @@ const DrawerHandle = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
1179
1248
|
});
|
|
1180
1249
|
DrawerHandle.displayName = "DrawerHandle";
|
|
1181
1250
|
|
|
1182
|
-
export { DrawerBackdrop as D, DrawerCloseButton as a, DrawerContent as b, DrawerDescription as c, DrawerHandle as d,
|
|
1251
|
+
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 };
|
package/lib/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var Drawer12s = require('./Drawer-12s-
|
|
1
|
+
var Drawer12s = require('./Drawer-12s-DXUv7jE4.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,12 +1,35 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
3
3
|
import { PrimitiveProps } from '@seed-design/react-primitive';
|
|
4
|
-
import * as
|
|
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?:
|
|
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:
|
|
44
|
-
onRelease?: (event:
|
|
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:
|
|
110
|
-
drawerRef:
|
|
111
|
-
overlayRef:
|
|
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:
|
|
115
|
-
onRelease: (event:
|
|
116
|
-
onDrag: (event:
|
|
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:
|
|
145
|
+
closeDrawer: (fromWithin?: boolean, details?: DrawerChangeDetails) => void;
|
|
146
|
+
keyboardIsOpen: React__default.RefObject<boolean>;
|
|
124
147
|
modal: boolean;
|
|
125
148
|
snapPointsOffset: number[];
|
|
126
149
|
activeSnapPointIndex: number | null;
|
|
@@ -128,51 +151,77 @@ declare function useDrawer(props: UseDrawerProps): {
|
|
|
128
151
|
noBodyStyles: boolean;
|
|
129
152
|
container: HTMLElement | null | undefined;
|
|
130
153
|
autoFocus: boolean;
|
|
131
|
-
setHasBeenOpened:
|
|
132
|
-
setIsOpen: (value:
|
|
154
|
+
setHasBeenOpened: React__default.Dispatch<React__default.SetStateAction<boolean>>;
|
|
155
|
+
setIsOpen: (value: boolean | ((prev: boolean) => boolean), details?: DrawerChangeDetails | undefined) => void;
|
|
133
156
|
closeOnInteractOutside: boolean;
|
|
134
157
|
closeOnEscape: boolean;
|
|
158
|
+
hasAnimationDone: boolean;
|
|
159
|
+
closeButtonRef: (node: HTMLButtonElement | null) => void;
|
|
160
|
+
isCloseButtonRendered: boolean;
|
|
135
161
|
};
|
|
136
162
|
|
|
137
163
|
interface DrawerRootProps extends UseDrawerProps {
|
|
138
|
-
children?:
|
|
164
|
+
children?: React$1.ReactNode;
|
|
139
165
|
}
|
|
140
166
|
declare const DrawerRoot: (props: DrawerRootProps) => react_jsx_runtime.JSX.Element;
|
|
141
167
|
interface DrawerTriggerProps extends DialogPrimitive.DialogTriggerProps {
|
|
142
168
|
}
|
|
143
|
-
declare const DrawerTrigger:
|
|
144
|
-
interface DrawerPositionerProps extends PrimitiveProps,
|
|
169
|
+
declare const DrawerTrigger: React$1.ForwardRefExoticComponent<DialogPrimitive.DialogTriggerProps & React$1.RefAttributes<HTMLButtonElement>>;
|
|
170
|
+
interface DrawerPositionerProps extends PrimitiveProps, React$1.HTMLAttributes<HTMLDivElement> {
|
|
145
171
|
}
|
|
146
|
-
declare const DrawerPositioner:
|
|
147
|
-
interface DrawerBackdropProps extends DialogPrimitive.DialogOverlayProps,
|
|
172
|
+
declare const DrawerPositioner: React$1.ForwardRefExoticComponent<DrawerPositionerProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
173
|
+
interface DrawerBackdropProps extends DialogPrimitive.DialogOverlayProps, React$1.HTMLAttributes<HTMLDivElement> {
|
|
148
174
|
}
|
|
149
|
-
declare const DrawerBackdrop:
|
|
150
|
-
interface DrawerContentProps extends DialogPrimitive.DialogContentProps,
|
|
175
|
+
declare const DrawerBackdrop: React$1.ForwardRefExoticComponent<DrawerBackdropProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
176
|
+
interface DrawerContentProps extends DialogPrimitive.DialogContentProps, React$1.HTMLAttributes<HTMLDivElement> {
|
|
151
177
|
}
|
|
152
|
-
declare const DrawerContent:
|
|
178
|
+
declare const DrawerContent: React$1.ForwardRefExoticComponent<DrawerContentProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
153
179
|
interface DrawerTitleProps extends DialogPrimitive.DialogTitleProps {
|
|
154
180
|
}
|
|
155
|
-
declare const DrawerTitle:
|
|
181
|
+
declare const DrawerTitle: React$1.ForwardRefExoticComponent<DialogPrimitive.DialogTitleProps & React$1.RefAttributes<HTMLHeadingElement>>;
|
|
156
182
|
interface DrawerDescriptionProps extends DialogPrimitive.DialogDescriptionProps {
|
|
157
183
|
}
|
|
158
|
-
declare const DrawerDescription:
|
|
184
|
+
declare const DrawerDescription: React$1.ForwardRefExoticComponent<DialogPrimitive.DialogDescriptionProps & React$1.RefAttributes<HTMLParagraphElement>>;
|
|
185
|
+
interface DrawerHeaderProps extends PrimitiveProps, React$1.HTMLAttributes<HTMLDivElement> {
|
|
186
|
+
}
|
|
187
|
+
declare const DrawerHeader: React$1.ForwardRefExoticComponent<DrawerHeaderProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
159
188
|
interface DrawerCloseButtonProps extends DialogPrimitive.DialogCloseProps {
|
|
160
189
|
}
|
|
161
|
-
declare const DrawerCloseButton:
|
|
162
|
-
interface DrawerHandleProps extends
|
|
190
|
+
declare const DrawerCloseButton: React$1.ForwardRefExoticComponent<DrawerCloseButtonProps & React$1.RefAttributes<HTMLButtonElement>>;
|
|
191
|
+
interface DrawerHandleProps extends React$1.HTMLAttributes<HTMLDivElement> {
|
|
163
192
|
preventCycle?: boolean;
|
|
164
193
|
}
|
|
165
|
-
declare const DrawerHandle:
|
|
194
|
+
declare const DrawerHandle: React$1.ForwardRefExoticComponent<DrawerHandleProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
166
195
|
|
|
167
196
|
type DrawerContextValue = ReturnType<typeof useDrawer>;
|
|
168
197
|
declare function useDrawerContext(): {
|
|
169
198
|
activeSnapPoint: string | number | null;
|
|
170
199
|
snapPoints: (string | number)[] | undefined;
|
|
171
|
-
setActiveSnapPoint: (value:
|
|
172
|
-
drawerRef:
|
|
173
|
-
overlayRef:
|
|
200
|
+
setActiveSnapPoint: (value: string | number | ((prev: string | number | null) => string | number | null) | null, details?: undefined) => void;
|
|
201
|
+
drawerRef: React$1.RefObject<HTMLDivElement | null>;
|
|
202
|
+
overlayRef: React$1.RefObject<HTMLDivElement | null>;
|
|
174
203
|
shouldOverlayAnimate: boolean;
|
|
175
|
-
onOpenChange: ((open: boolean
|
|
204
|
+
onOpenChange: ((open: boolean, details?: ({
|
|
205
|
+
reason?: "closeButton" | undefined;
|
|
206
|
+
} & {
|
|
207
|
+
event: MouseEvent;
|
|
208
|
+
}) | ({
|
|
209
|
+
reason?: "escapeKeyDown" | undefined;
|
|
210
|
+
} & {
|
|
211
|
+
event: KeyboardEvent;
|
|
212
|
+
}) | ({
|
|
213
|
+
reason?: "interactOutside" | undefined;
|
|
214
|
+
} & {
|
|
215
|
+
event: PointerEvent | FocusEvent;
|
|
216
|
+
}) | ({
|
|
217
|
+
reason?: "drag" | undefined;
|
|
218
|
+
} & {
|
|
219
|
+
event: PointerEvent;
|
|
220
|
+
}) | ({
|
|
221
|
+
reason?: "handleClickOnLastSnapPoint" | undefined;
|
|
222
|
+
} & {
|
|
223
|
+
event: MouseEvent;
|
|
224
|
+
})) => void) | undefined;
|
|
176
225
|
onPress: (event: React.PointerEvent<HTMLDivElement>) => void;
|
|
177
226
|
onRelease: (event: React.PointerEvent<HTMLDivElement> | null) => void;
|
|
178
227
|
onDrag: (event: React.PointerEvent<HTMLDivElement>) => void;
|
|
@@ -181,8 +230,28 @@ declare function useDrawerContext(): {
|
|
|
181
230
|
isOpen: boolean;
|
|
182
231
|
isDragging: boolean;
|
|
183
232
|
shouldFade: boolean;
|
|
184
|
-
closeDrawer: (fromWithin?: boolean
|
|
185
|
-
|
|
233
|
+
closeDrawer: (fromWithin?: boolean, details?: ({
|
|
234
|
+
reason?: "closeButton" | undefined;
|
|
235
|
+
} & {
|
|
236
|
+
event: MouseEvent;
|
|
237
|
+
}) | ({
|
|
238
|
+
reason?: "escapeKeyDown" | undefined;
|
|
239
|
+
} & {
|
|
240
|
+
event: KeyboardEvent;
|
|
241
|
+
}) | ({
|
|
242
|
+
reason?: "interactOutside" | undefined;
|
|
243
|
+
} & {
|
|
244
|
+
event: PointerEvent | FocusEvent;
|
|
245
|
+
}) | ({
|
|
246
|
+
reason?: "drag" | undefined;
|
|
247
|
+
} & {
|
|
248
|
+
event: PointerEvent;
|
|
249
|
+
}) | ({
|
|
250
|
+
reason?: "handleClickOnLastSnapPoint" | undefined;
|
|
251
|
+
} & {
|
|
252
|
+
event: MouseEvent;
|
|
253
|
+
})) => void;
|
|
254
|
+
keyboardIsOpen: React$1.RefObject<boolean>;
|
|
186
255
|
modal: boolean;
|
|
187
256
|
snapPointsOffset: number[];
|
|
188
257
|
activeSnapPointIndex: number | null;
|
|
@@ -190,16 +259,39 @@ declare function useDrawerContext(): {
|
|
|
190
259
|
noBodyStyles: boolean;
|
|
191
260
|
container: HTMLElement | null | undefined;
|
|
192
261
|
autoFocus: boolean;
|
|
193
|
-
setHasBeenOpened:
|
|
194
|
-
setIsOpen: (value:
|
|
262
|
+
setHasBeenOpened: React$1.Dispatch<React$1.SetStateAction<boolean>>;
|
|
263
|
+
setIsOpen: (value: boolean | ((prev: boolean) => boolean), details?: (({
|
|
264
|
+
reason?: "closeButton" | undefined;
|
|
265
|
+
} & {
|
|
266
|
+
event: MouseEvent;
|
|
267
|
+
}) | ({
|
|
268
|
+
reason?: "escapeKeyDown" | undefined;
|
|
269
|
+
} & {
|
|
270
|
+
event: KeyboardEvent;
|
|
271
|
+
}) | ({
|
|
272
|
+
reason?: "interactOutside" | undefined;
|
|
273
|
+
} & {
|
|
274
|
+
event: PointerEvent | FocusEvent;
|
|
275
|
+
}) | ({
|
|
276
|
+
reason?: "drag" | undefined;
|
|
277
|
+
} & {
|
|
278
|
+
event: PointerEvent;
|
|
279
|
+
}) | ({
|
|
280
|
+
reason?: "handleClickOnLastSnapPoint" | undefined;
|
|
281
|
+
} & {
|
|
282
|
+
event: MouseEvent;
|
|
283
|
+
})) | undefined) => void;
|
|
195
284
|
closeOnInteractOutside: boolean;
|
|
196
285
|
closeOnEscape: boolean;
|
|
286
|
+
hasAnimationDone: boolean;
|
|
287
|
+
closeButtonRef: (node: HTMLButtonElement | null) => void;
|
|
288
|
+
isCloseButtonRendered: boolean;
|
|
197
289
|
};
|
|
198
290
|
|
|
199
291
|
declare namespace Drawer_namespace {
|
|
200
|
-
export { DrawerBackdrop as Backdrop, DrawerCloseButton as CloseButton, DrawerContent as Content, DrawerDescription as Description, DrawerHandle as Handle, DrawerPositioner as Positioner, DrawerRoot as Root, DrawerTitle as Title, DrawerTrigger as Trigger };
|
|
201
|
-
export type { DrawerBackdropProps as BackdropProps, DrawerCloseButtonProps as CloseButtonProps, DrawerContentProps as ContentProps, DrawerDescriptionProps as DescriptionProps, DrawerHandleProps as HandleProps, DrawerPositionerProps as PositionerProps, DrawerRootProps as RootProps, DrawerTitleProps as TitleProps, DrawerTriggerProps as TriggerProps };
|
|
292
|
+
export { DrawerBackdrop as Backdrop, DrawerCloseButton as CloseButton, DrawerContent as Content, DrawerDescription as Description, DrawerHandle as Handle, DrawerHeader as Header, DrawerPositioner as Positioner, DrawerRoot as Root, DrawerTitle as Title, DrawerTrigger as Trigger };
|
|
293
|
+
export type { DrawerBackdropProps as BackdropProps, DrawerCloseButtonProps as CloseButtonProps, DrawerContentProps as ContentProps, DrawerDescriptionProps as DescriptionProps, DrawerHandleProps as HandleProps, DrawerHeaderProps as HeaderProps, DrawerPositionerProps as PositionerProps, DrawerRootProps as RootProps, DrawerTitleProps as TitleProps, DrawerTriggerProps as TriggerProps };
|
|
202
294
|
}
|
|
203
295
|
|
|
204
|
-
export { Drawer_namespace as Drawer, DrawerBackdrop, DrawerCloseButton, DrawerContent, DrawerDescription, DrawerHandle, DrawerPositioner, DrawerRoot, DrawerTitle, DrawerTrigger, useDrawer, useDrawerContext };
|
|
205
|
-
export type { DrawerBackdropProps, DrawerCloseButtonProps, DrawerContentProps, DrawerContextValue, DrawerDescriptionProps, DrawerHandleProps, DrawerPositionerProps, DrawerRootProps, DrawerTitleProps, DrawerTriggerProps, UseDrawerProps };
|
|
296
|
+
export { Drawer_namespace as Drawer, DrawerBackdrop, DrawerCloseButton, DrawerContent, DrawerDescription, DrawerHandle, DrawerHeader, DrawerPositioner, DrawerRoot, DrawerTitle, DrawerTrigger, useDrawer, useDrawerContext };
|
|
297
|
+
export type { DrawerBackdropProps, DrawerCloseButtonProps, DrawerContentProps, DrawerContextValue, DrawerDescriptionProps, DrawerHandleProps, DrawerHeaderProps, DrawerPositionerProps, DrawerRootProps, DrawerTitleProps, DrawerTriggerProps, UseDrawerProps };
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { D as DrawerBackdrop, a as DrawerCloseButton, b as DrawerContent, c as DrawerDescription, d as DrawerHandle, e as
|
|
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-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,
|
|
@@ -8,10 +8,11 @@ var Drawer_namespace = {
|
|
|
8
8
|
Content: DrawerContent,
|
|
9
9
|
Description: DrawerDescription,
|
|
10
10
|
Handle: DrawerHandle,
|
|
11
|
+
Header: DrawerHeader,
|
|
11
12
|
Positioner: DrawerPositioner,
|
|
12
13
|
Root: DrawerRoot,
|
|
13
14
|
Title: DrawerTitle,
|
|
14
15
|
Trigger: DrawerTrigger
|
|
15
16
|
};
|
|
16
17
|
|
|
17
|
-
export { Drawer_namespace as Drawer, DrawerBackdrop, DrawerCloseButton, DrawerContent, DrawerDescription, DrawerHandle, DrawerPositioner, DrawerRoot, DrawerTitle, DrawerTrigger };
|
|
18
|
+
export { Drawer_namespace as Drawer, DrawerBackdrop, DrawerCloseButton, DrawerContent, DrawerDescription, DrawerHandle, DrawerHeader, DrawerPositioner, DrawerRoot, DrawerTitle, DrawerTrigger };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seed-design/react-drawer",
|
|
3
|
-
"version": "1.0.
|
|
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
|
-
"@
|
|
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.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
|
@@ -64,13 +64,20 @@ export interface DrawerBackdropProps
|
|
|
64
64
|
React.HTMLAttributes<HTMLDivElement> {}
|
|
65
65
|
|
|
66
66
|
export const DrawerBackdrop = forwardRef<HTMLDivElement, DrawerBackdropProps>((props, ref) => {
|
|
67
|
-
const {
|
|
68
|
-
|
|
67
|
+
const {
|
|
68
|
+
overlayRef,
|
|
69
|
+
onRelease,
|
|
70
|
+
modal,
|
|
71
|
+
snapPoints,
|
|
72
|
+
isOpen,
|
|
73
|
+
shouldFade,
|
|
74
|
+
shouldOverlayAnimate,
|
|
75
|
+
hasAnimationDone,
|
|
76
|
+
} = useDrawerContext();
|
|
69
77
|
const composedRef = useComposedRefs(ref, overlayRef);
|
|
70
78
|
const hasSnapPoints = snapPoints && snapPoints.length > 0;
|
|
71
79
|
const onMouseUp = useCallbackRef((event: React.PointerEvent<HTMLDivElement>) => onRelease(event));
|
|
72
80
|
|
|
73
|
-
// Overlay is the component that is locking scroll, removing it will unlock the scroll without having to dig into Radix's Dialog library
|
|
74
81
|
if (!modal) {
|
|
75
82
|
return null;
|
|
76
83
|
}
|
|
@@ -83,6 +90,7 @@ export const DrawerBackdrop = forwardRef<HTMLDivElement, DrawerBackdropProps>((p
|
|
|
83
90
|
data-snap-points-overlay={isOpen && shouldFade ? "true" : "false"}
|
|
84
91
|
data-should-overlay-animate={shouldOverlayAnimate ? "true" : "false"}
|
|
85
92
|
data-open={dataAttr(isOpen)}
|
|
93
|
+
data-animation-done={hasAnimationDone ? "true" : "false"}
|
|
86
94
|
{...props}
|
|
87
95
|
/>
|
|
88
96
|
);
|
|
@@ -114,6 +122,7 @@ export const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>((pro
|
|
|
114
122
|
closeOnInteractOutside,
|
|
115
123
|
closeOnEscape,
|
|
116
124
|
dismissible,
|
|
125
|
+
hasAnimationDone,
|
|
117
126
|
} = useDrawerContext();
|
|
118
127
|
// Needed to use transition instead of animations
|
|
119
128
|
const [delayedSnapPoints, setDelayedSnapPoints] = useState(false);
|
|
@@ -170,6 +179,7 @@ export const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>((pro
|
|
|
170
179
|
data-delayed-snap-points={delayedSnapPoints ? "true" : "false"}
|
|
171
180
|
data-drawer-direction={direction}
|
|
172
181
|
data-open={dataAttr(isOpen)}
|
|
182
|
+
data-animation-done={hasAnimationDone ? "true" : "false"}
|
|
173
183
|
data-drawer=""
|
|
174
184
|
data-snap-points={isOpen && hasSnapPoints ? "true" : "false"}
|
|
175
185
|
data-custom-container={container ? "true" : "false"}
|
|
@@ -253,13 +263,13 @@ export const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>((pro
|
|
|
253
263
|
onInteractOutside={(e) => {
|
|
254
264
|
// Only close if event is not prevented (e.g., by onFocusOutside or onPointerDownOutside)
|
|
255
265
|
if (dismissible && closeOnInteractOutside && !e.defaultPrevented) {
|
|
256
|
-
closeDrawer();
|
|
266
|
+
closeDrawer(false, { reason: "interactOutside", event: e.detail.originalEvent });
|
|
257
267
|
}
|
|
258
268
|
props.onInteractOutside?.(e);
|
|
259
269
|
}}
|
|
260
270
|
onEscapeKeyDown={(e) => {
|
|
261
271
|
if (dismissible && closeOnEscape) {
|
|
262
|
-
closeDrawer();
|
|
272
|
+
closeDrawer(false, { reason: "escapeKeyDown", event: e });
|
|
263
273
|
}
|
|
264
274
|
props.onEscapeKeyDown?.(e);
|
|
265
275
|
}}
|
|
@@ -276,19 +286,30 @@ export interface DrawerDescriptionProps extends DialogPrimitive.DialogDescriptio
|
|
|
276
286
|
|
|
277
287
|
export const DrawerDescription = DialogPrimitive.Description;
|
|
278
288
|
|
|
289
|
+
export interface DrawerHeaderProps extends PrimitiveProps, React.HTMLAttributes<HTMLDivElement> {}
|
|
290
|
+
|
|
291
|
+
export const DrawerHeader = forwardRef<HTMLDivElement, DrawerHeaderProps>((props, ref) => {
|
|
292
|
+
const { isCloseButtonRendered } = useDrawerContext();
|
|
293
|
+
return (
|
|
294
|
+
<Primitive.div ref={ref} data-show-close-button={dataAttr(isCloseButtonRendered)} {...props} />
|
|
295
|
+
);
|
|
296
|
+
});
|
|
297
|
+
DrawerHeader.displayName = "DrawerHeader";
|
|
298
|
+
|
|
279
299
|
export interface DrawerCloseButtonProps extends DialogPrimitive.DialogCloseProps {}
|
|
280
300
|
|
|
281
301
|
export const DrawerCloseButton = forwardRef<HTMLButtonElement, DrawerCloseButtonProps>(
|
|
282
302
|
(props, ref) => {
|
|
283
|
-
const
|
|
303
|
+
const { closeButtonRef, setIsOpen } = useDrawerContext();
|
|
304
|
+
const composedRef = useComposedRefs(ref, closeButtonRef);
|
|
284
305
|
return (
|
|
285
306
|
<Primitive.button
|
|
286
|
-
ref={
|
|
307
|
+
ref={composedRef}
|
|
287
308
|
{...props}
|
|
288
309
|
onClick={(e) => {
|
|
289
310
|
props.onClick?.(e);
|
|
290
311
|
if (e.defaultPrevented) return;
|
|
291
|
-
|
|
312
|
+
setIsOpen(false, { reason: "closeButton", event: e.nativeEvent });
|
|
292
313
|
}}
|
|
293
314
|
/>
|
|
294
315
|
);
|
|
@@ -321,18 +342,18 @@ export const DrawerHandle = forwardRef<HTMLDivElement, DrawerHandleProps>((props
|
|
|
321
342
|
const closeTimeoutIdRef = useRef<number | null>(null);
|
|
322
343
|
const shouldCancelInteractionRef = useRef(false);
|
|
323
344
|
|
|
324
|
-
function handleStartCycle() {
|
|
345
|
+
function handleStartCycle(event: React.MouseEvent<HTMLDivElement>) {
|
|
325
346
|
// Stop if this is the second click of a double click
|
|
326
347
|
if (shouldCancelInteractionRef.current) {
|
|
327
348
|
handleCancelInteraction();
|
|
328
349
|
return;
|
|
329
350
|
}
|
|
330
351
|
window.setTimeout(() => {
|
|
331
|
-
handleCycleSnapPoints();
|
|
352
|
+
handleCycleSnapPoints(event);
|
|
332
353
|
}, DOUBLE_TAP_TIMEOUT);
|
|
333
354
|
}
|
|
334
355
|
|
|
335
|
-
function handleCycleSnapPoints() {
|
|
356
|
+
function handleCycleSnapPoints(event: React.MouseEvent<HTMLDivElement>) {
|
|
336
357
|
// Prevent accidental taps while resizing drawer
|
|
337
358
|
if (isDragging || preventCycle || shouldCancelInteractionRef.current) {
|
|
338
359
|
handleCancelInteraction();
|
|
@@ -343,7 +364,7 @@ export const DrawerHandle = forwardRef<HTMLDivElement, DrawerHandleProps>((props
|
|
|
343
364
|
|
|
344
365
|
if (!snapPoints || snapPoints.length === 0) {
|
|
345
366
|
if (!dismissible) {
|
|
346
|
-
closeDrawer();
|
|
367
|
+
closeDrawer(false, { reason: "handleClickOnLastSnapPoint", event: event.nativeEvent });
|
|
347
368
|
}
|
|
348
369
|
return;
|
|
349
370
|
}
|
|
@@ -351,7 +372,7 @@ export const DrawerHandle = forwardRef<HTMLDivElement, DrawerHandleProps>((props
|
|
|
351
372
|
const isLastSnapPoint = activeSnapPoint === snapPoints[snapPoints.length - 1];
|
|
352
373
|
|
|
353
374
|
if (isLastSnapPoint && dismissible) {
|
|
354
|
-
closeDrawer();
|
|
375
|
+
closeDrawer(false, { reason: "handleClickOnLastSnapPoint", event: event.nativeEvent });
|
|
355
376
|
return;
|
|
356
377
|
}
|
|
357
378
|
|
package/src/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/use-snap-points.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useControllableState } from "@
|
|
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 "@
|
|
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();
|
|
@@ -178,9 +194,15 @@ export function useDrawer(props: UseDrawerProps) {
|
|
|
178
194
|
});
|
|
179
195
|
|
|
180
196
|
const [hasBeenOpened, setHasBeenOpened] = useState<boolean>(false);
|
|
197
|
+
const [hasAnimationDone, setHasAnimationDone] = useState<boolean>(false);
|
|
181
198
|
const [isDragging, setIsDragging] = useState<boolean>(false);
|
|
182
199
|
const [shouldOverlayAnimate, setShouldOverlayAnimate] = useState<boolean>(false);
|
|
183
200
|
|
|
201
|
+
const [isCloseButtonRendered, setIsCloseButtonRendered] = useState<boolean>(false);
|
|
202
|
+
const closeButtonRef = useCallback((node: HTMLButtonElement | null) => {
|
|
203
|
+
setIsCloseButtonRendered(!!node);
|
|
204
|
+
}, []);
|
|
205
|
+
|
|
184
206
|
const overlayRef = useRef<HTMLDivElement>(null);
|
|
185
207
|
const openTime = useRef<Date | null>(null);
|
|
186
208
|
const dragStartTime = useRef<Date | null>(null);
|
|
@@ -419,12 +441,12 @@ export function useDrawer(props: UseDrawerProps) {
|
|
|
419
441
|
}, [isDragging]);
|
|
420
442
|
|
|
421
443
|
const closeDrawer = useCallback(
|
|
422
|
-
(fromWithin?: boolean) => {
|
|
444
|
+
(fromWithin?: boolean, details?: DrawerChangeDetails) => {
|
|
423
445
|
cancelDrag();
|
|
424
446
|
onClose?.();
|
|
425
447
|
|
|
426
448
|
if (!fromWithin) {
|
|
427
|
-
setIsOpen(false);
|
|
449
|
+
setIsOpen(false, details);
|
|
428
450
|
}
|
|
429
451
|
|
|
430
452
|
if (fadeFromIndex !== undefined && fadeFromIndex > 0 && activeSnapPointIndex === 0) {
|
|
@@ -489,6 +511,7 @@ export function useDrawer(props: UseDrawerProps) {
|
|
|
489
511
|
closeDrawer,
|
|
490
512
|
velocity,
|
|
491
513
|
dismissible,
|
|
514
|
+
event: event.nativeEvent,
|
|
492
515
|
});
|
|
493
516
|
onReleaseProp?.(event, true);
|
|
494
517
|
return;
|
|
@@ -501,7 +524,7 @@ export function useDrawer(props: UseDrawerProps) {
|
|
|
501
524
|
}
|
|
502
525
|
|
|
503
526
|
if (velocity > VELOCITY_THRESHOLD) {
|
|
504
|
-
closeDrawer();
|
|
527
|
+
closeDrawer(false, { reason: "drag", event: event.nativeEvent });
|
|
505
528
|
onReleaseProp?.(event, false);
|
|
506
529
|
return;
|
|
507
530
|
}
|
|
@@ -520,7 +543,7 @@ export function useDrawer(props: UseDrawerProps) {
|
|
|
520
543
|
Math.abs(swipeAmount) >=
|
|
521
544
|
(isHorizontalSwipe ? visibleDrawerWidth : visibleDrawerHeight) * closeThreshold
|
|
522
545
|
) {
|
|
523
|
-
closeDrawer();
|
|
546
|
+
closeDrawer(false, { reason: "drag", event: event.nativeEvent });
|
|
524
547
|
onReleaseProp?.(event, false);
|
|
525
548
|
return;
|
|
526
549
|
}
|
|
@@ -607,12 +630,32 @@ export function useDrawer(props: UseDrawerProps) {
|
|
|
607
630
|
}
|
|
608
631
|
}, [modal]);
|
|
609
632
|
|
|
633
|
+
// Effect 1: Track drawer open state
|
|
610
634
|
useEffect(() => {
|
|
611
635
|
if (isOpen) {
|
|
612
636
|
setHasBeenOpened(true);
|
|
613
637
|
}
|
|
614
638
|
}, [isOpen]);
|
|
615
639
|
|
|
640
|
+
// Effect 2: Handle animation state and timer
|
|
641
|
+
useEffect(() => {
|
|
642
|
+
if (isOpen) {
|
|
643
|
+
// Only reset animation state if this is the first open
|
|
644
|
+
if (!hasBeenOpened) {
|
|
645
|
+
setHasAnimationDone(false);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
const timeoutId = setTimeout(() => {
|
|
649
|
+
setHasAnimationDone(true);
|
|
650
|
+
}, TRANSITIONS.ENTER_DURATION * 1000);
|
|
651
|
+
|
|
652
|
+
return () => clearTimeout(timeoutId);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// Reset animation state when drawer closes
|
|
656
|
+
setHasAnimationDone(false);
|
|
657
|
+
}, [isOpen, hasBeenOpened]);
|
|
658
|
+
|
|
616
659
|
useEffect(() => {
|
|
617
660
|
if (isOpen && snapPoints && fadeFromIndex === 0) {
|
|
618
661
|
setShouldOverlayAnimate(true);
|
|
@@ -657,6 +700,9 @@ export function useDrawer(props: UseDrawerProps) {
|
|
|
657
700
|
setIsOpen,
|
|
658
701
|
closeOnInteractOutside,
|
|
659
702
|
closeOnEscape,
|
|
703
|
+
hasAnimationDone,
|
|
704
|
+
closeButtonRef,
|
|
705
|
+
isCloseButtonRendered,
|
|
660
706
|
}),
|
|
661
707
|
[
|
|
662
708
|
activeSnapPoint,
|
|
@@ -683,6 +729,9 @@ export function useDrawer(props: UseDrawerProps) {
|
|
|
683
729
|
onRelease,
|
|
684
730
|
onDrag,
|
|
685
731
|
onPress,
|
|
732
|
+
hasAnimationDone,
|
|
733
|
+
closeButtonRef,
|
|
734
|
+
isCloseButtonRendered,
|
|
686
735
|
],
|
|
687
736
|
);
|
|
688
737
|
}
|