@trackunit/react-drawer 0.1.31 → 0.2.0

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/index.cjs.js CHANGED
@@ -98,7 +98,7 @@ const cvaOverlayContainer = cssClassVarianceUtilities.cvaMerge([
98
98
  variants: {
99
99
  open: {
100
100
  true: "z-popover opacity-100",
101
- false: "z-[-1] opacity-0",
101
+ false: "z-hidden pointer-events-none opacity-0",
102
102
  },
103
103
  },
104
104
  });
@@ -336,15 +336,19 @@ const getElementYTranslation = (element) => {
336
336
  const matrix = new DOMMatrix(transform);
337
337
  return matrix.m42;
338
338
  };
339
+ const isPassedThreshold = (dragDistance, threshold) => {
340
+ return dragDistance >= threshold;
341
+ };
339
342
  /**
340
343
  * Hook for handling swipe events on the drawer.
341
344
  */
342
- const useSwipeHandlers = ({ ref, closingThreshold, onCloseGesture, onOpenGesture }) => {
345
+ const useSwipeHandlers = ({ ref, closingThreshold, onCloseGesture, onOpenGesture, onDragEnd, }) => {
343
346
  const [isDragging, setIsDragging] = react.useState(false);
344
347
  const [dragDistance, setDragDistance] = react.useState(0);
345
348
  const currentY = react.useRef(0);
346
349
  const currentHeight = react.useRef(0);
347
350
  const geometry = reactComponents.useGeometry(ref);
351
+ const threshold = currentHeight.current * closingThreshold;
348
352
  const handlers = reactSwipeable.useSwipeable({
349
353
  onTouchStartOrOnMouseDown: () => {
350
354
  const y = getElementYTranslation(ref.current);
@@ -361,22 +365,22 @@ const useSwipeHandlers = ({ ref, closingThreshold, onCloseGesture, onOpenGesture
361
365
  }
362
366
  },
363
367
  onSwipedDown: (e) => {
364
- const threshold = currentHeight.current * closingThreshold;
365
- if (e.absY > threshold && onCloseGesture) {
368
+ if (isPassedThreshold(e.absY, threshold) && onCloseGesture) {
366
369
  onCloseGesture();
367
370
  }
368
371
  setIsDragging(false);
369
372
  setDragDistance(0);
370
373
  },
371
374
  onSwipedUp: (e) => {
372
- const threshold = currentHeight.current * closingThreshold;
373
- if (e.absY > threshold && onOpenGesture) {
375
+ if (isPassedThreshold(e.absY, threshold) && onOpenGesture) {
374
376
  onOpenGesture();
375
377
  }
376
378
  setIsDragging(false);
377
379
  setDragDistance(0);
378
380
  },
379
- onSwiped: () => void 0,
381
+ onSwiped: (e) => {
382
+ onDragEnd && onDragEnd(e.deltaY, e.velocity);
383
+ },
380
384
  trackMouse: true,
381
385
  trackTouch: true,
382
386
  preventScrollOnSwipe: true,
@@ -400,6 +404,8 @@ const cvaPuller = cssClassVarianceUtilities.cvaMerge(["pt-1", "pb-4", "flex", "i
400
404
  const cvaPullerIcon = cssClassVarianceUtilities.cvaMerge(["block", "h-1", "w-8", "rounded-full", "bg-gray-400"]);
401
405
 
402
406
  const CLOSING_THRESHOLD = 0.15;
407
+ const VELOCITY_THRESHOLD = 0.3;
408
+ const SNAP_POINT_TRANSITION = "transform 500ms cubic-bezier(0.32,0.72,0,1)";
403
409
  /**
404
410
  *
405
411
  * SwipeableDrawer is a component that wraps the Drawer component to add swipeable functionality.
@@ -407,52 +413,133 @@ const CLOSING_THRESHOLD = 0.15;
407
413
  * The component manages its docked state based on the open prop and keepMountedWhenClosed prop.
408
414
  * It also applies styles dynamically based on whether the drawer is being dragged.
409
415
  */
410
- const SwipeableDrawer = react.forwardRef(({ open, onClose, onOpenGesture, children, keepMountedWhenClosed, className, position = "bottom", ...others }, ref) => {
411
- const drawerRefs = react.useRef(null);
412
- react.useImperativeHandle(ref, () => drawerRefs.current);
413
- const [isDocked, setIsDocked] = react.useState(keepMountedWhenClosed && !open);
416
+ const SwipeableDrawer = react.forwardRef(({ open, onClose, onOpenGesture, onSnapPointChange, children, keepMountedWhenClosed, className, position = "bottom", snapPoints, activeSnapPoint: activeSnapPointProp = null, container, ...others }, ref) => {
417
+ const drawerRef = react.useRef(null);
418
+ const containerRef = react.useRef(container !== null && container !== void 0 ? container : null);
419
+ const containerGeometry = reactComponents.useGeometry(containerRef);
420
+ react.useImperativeHandle(ref, () => drawerRef.current);
421
+ const [activeSnapPoint, setActiveSnapPoint] = react.useState(activeSnapPointProp);
422
+ const [snapPointOffsets, setSnapPointOffsets] = react.useState([]);
423
+ const activeSnapPointIndex = react.useMemo(() => { var _a; return (_a = snapPoints === null || snapPoints === void 0 ? void 0 : snapPoints.findIndex(snapPoint => snapPoint === activeSnapPoint)) !== null && _a !== void 0 ? _a : -1; }, [snapPoints, activeSnapPoint]);
424
+ const onHandleDragEnd = react.useCallback((draggedBy, velocity) => {
425
+ var _a, _b, _c;
426
+ if (!snapPoints || !snapPoints.length) {
427
+ return;
428
+ }
429
+ const direction = draggedBy > 0 ? -1 : 1;
430
+ // If the velocity is greater than the threshold and the dragged distance is less than 40% of the container height, snap to the next snap point
431
+ if (velocity > VELOCITY_THRESHOLD && Math.abs(draggedBy) < containerGeometry.height * 0.4) {
432
+ const newSnapPoint = (_a = snapPoints[activeSnapPointIndex + direction]) !== null && _a !== void 0 ? _a : null;
433
+ if (newSnapPoint) {
434
+ setActiveSnapPoint(newSnapPoint);
435
+ onSnapPointChange === null || onSnapPointChange === void 0 ? void 0 : onSnapPointChange(newSnapPoint);
436
+ }
437
+ return;
438
+ }
439
+ // Otherwise, find the closest snap point
440
+ // This is when the user is dragging but not swiping.
441
+ // For example, when the user is dragging the drawer to the bottom but not fast enough to trigger a swipe.
442
+ const currentPosition = ((_b = snapPointOffsets[activeSnapPointIndex]) !== null && _b !== void 0 ? _b : 0) + draggedBy;
443
+ const closestSnapPoint = snapPointOffsets.reduce((prev, curr) => {
444
+ if (typeof prev !== "number" || typeof curr !== "number") {
445
+ return prev;
446
+ }
447
+ return Math.abs(curr - currentPosition) < Math.abs(prev - currentPosition) ? curr : prev;
448
+ });
449
+ const snapPointIndex = snapPointOffsets.findIndex(offset => offset === closestSnapPoint);
450
+ const nextSnapPoint = (_c = snapPoints[snapPointIndex]) !== null && _c !== void 0 ? _c : null;
451
+ if (!nextSnapPoint) {
452
+ return;
453
+ }
454
+ setActiveSnapPoint(nextSnapPoint);
455
+ onSnapPointChange === null || onSnapPointChange === void 0 ? void 0 : onSnapPointChange(nextSnapPoint);
456
+ }, [snapPoints, snapPointOffsets, activeSnapPointIndex, containerGeometry, onSnapPointChange]);
414
457
  const { handlers, isDragging, dragDistance } = useSwipeHandlers({
415
- ref: drawerRefs,
458
+ ref: drawerRef,
416
459
  closingThreshold: CLOSING_THRESHOLD,
417
- onCloseGesture: onClose,
418
- onOpenGesture,
460
+ onCloseGesture: () => {
461
+ if (!snapPoints) {
462
+ onClose && onClose();
463
+ }
464
+ },
465
+ onOpenGesture: () => {
466
+ if (!snapPoints) {
467
+ onOpenGesture && onOpenGesture();
468
+ }
469
+ },
470
+ onDragEnd: onHandleDragEnd,
419
471
  });
420
472
  useScrollBlock({ isDisabled: !open || isDragging });
421
- const drawerStyle = react.useMemo(() => isDragging
422
- ? {
423
- transform: `translateY(${dragDistance}px)`,
424
- transition: "none",
473
+ react.useEffect(() => {
474
+ if (!snapPoints || !drawerRef.current) {
475
+ return;
425
476
  }
426
- : {}, [isDragging, dragDistance]);
477
+ const containerSize = containerRef.current ? containerGeometry.height : window.innerHeight;
478
+ const newSnapPointOffsets = snapPoints.map(snapPoint => {
479
+ // If snapPoint is a number, it is a percentage of the container size
480
+ if (typeof snapPoint === "number") {
481
+ const height = Math.floor(containerSize * snapPoint);
482
+ return containerSize - height;
483
+ }
484
+ // If snapPoint is a string, it is a pixel value
485
+ if (snapPoint.endsWith("px")) {
486
+ return containerSize - parseInt(snapPoint.slice(0, -1), 10);
487
+ }
488
+ return 0;
489
+ });
490
+ setSnapPointOffsets(newSnapPointOffsets);
491
+ }, [snapPoints, drawerRef, containerRef, containerGeometry]);
492
+ react.useEffect(() => {
493
+ if (container) {
494
+ containerRef.current = container;
495
+ }
496
+ }, [container]);
427
497
  react.useEffect(() => {
428
- if (!keepMountedWhenClosed) {
498
+ var _a;
499
+ if (!snapPoints || snapPoints.length === 0) {
429
500
  return;
430
501
  }
431
- if (isDocked && open) {
432
- setIsDocked(false);
502
+ const snapPointIndex = snapPoints.findIndex(offset => offset === activeSnapPointProp);
503
+ const nextSnapPoint = (_a = snapPoints[snapPointIndex > -1 ? snapPointIndex : 0]) !== null && _a !== void 0 ? _a : null;
504
+ if (!nextSnapPoint) {
505
+ return;
506
+ }
507
+ setActiveSnapPoint(nextSnapPoint);
508
+ onSnapPointChange === null || onSnapPointChange === void 0 ? void 0 : onSnapPointChange(nextSnapPoint);
509
+ }, [snapPoints, activeSnapPointProp, onSnapPointChange]);
510
+ const drawerStyle = react.useMemo(() => {
511
+ if (isDragging) {
512
+ return {
513
+ transform: `translateY(${dragDistance}px)`,
514
+ transition: "none",
515
+ };
433
516
  }
434
- else if (!isDocked && !open) {
435
- setIsDocked(true);
517
+ if (snapPoints && snapPointOffsets.length > 0 && activeSnapPointIndex !== -1) {
518
+ return {
519
+ transform: `translateY(${snapPointOffsets[activeSnapPointIndex]}px)`,
520
+ transition: SNAP_POINT_TRANSITION,
521
+ };
436
522
  }
437
- }, [open, isDocked, keepMountedWhenClosed]);
523
+ return {};
524
+ }, [isDragging, dragDistance, snapPointOffsets, activeSnapPointIndex, snapPoints]);
438
525
  react.useEffect(() => {
439
526
  var _a, _b;
440
- if (drawerRefs.current) {
441
- drawerRefs.current.style.transform = (_a = drawerStyle.transform) !== null && _a !== void 0 ? _a : "";
442
- drawerRefs.current.style.transition = (_b = drawerStyle.transition) !== null && _b !== void 0 ? _b : "";
527
+ if (!drawerRef.current) {
528
+ return;
443
529
  }
444
- }, [drawerStyle, drawerRefs]);
445
- return (jsxRuntime.jsxs(Drawer, { className: cvaSwipeableDrawer({ docked: isDocked, className }), "data-docked": isDocked, keepMountedWhenClosed: keepMountedWhenClosed, onClose: onClose, open: open, position: position, ref: drawerRefs, ...others, children: [jsxRuntime.jsx(DrawerPuller, { ...handlers }), children] }));
530
+ if (open) {
531
+ drawerRef.current.style.transform = (_a = drawerStyle.transform) !== null && _a !== void 0 ? _a : "";
532
+ drawerRef.current.style.transition = (_b = drawerStyle.transition) !== null && _b !== void 0 ? _b : "";
533
+ }
534
+ else {
535
+ drawerRef.current.style.transform = "";
536
+ drawerRef.current.style.transition = "";
537
+ }
538
+ }, [drawerStyle, drawerRef, open]);
539
+ return (jsxRuntime.jsxs(Drawer, { className: cvaSwipeableDrawer({ className }), keepMountedWhenClosed: keepMountedWhenClosed, onClose: onClose, open: open, position: position, ref: drawerRef, ...others, children: [jsxRuntime.jsx(DrawerPuller, { ...handlers }), children] }));
446
540
  });
447
541
  SwipeableDrawer.displayName = "SwipeableDrawer";
448
- const cvaSwipeableDrawer = cssClassVarianceUtilities.cvaMerge([], {
449
- variants: {
450
- docked: {
451
- true: "translate-y-[calc(100%-30px)]",
452
- false: "",
453
- },
454
- },
455
- });
542
+ const cvaSwipeableDrawer = cssClassVarianceUtilities.cvaMerge([]);
456
543
 
457
544
  /*
458
545
  * ----------------------------
package/index.esm.js CHANGED
@@ -96,7 +96,7 @@ const cvaOverlayContainer = cvaMerge([
96
96
  variants: {
97
97
  open: {
98
98
  true: "z-popover opacity-100",
99
- false: "z-[-1] opacity-0",
99
+ false: "z-hidden pointer-events-none opacity-0",
100
100
  },
101
101
  },
102
102
  });
@@ -334,15 +334,19 @@ const getElementYTranslation = (element) => {
334
334
  const matrix = new DOMMatrix(transform);
335
335
  return matrix.m42;
336
336
  };
337
+ const isPassedThreshold = (dragDistance, threshold) => {
338
+ return dragDistance >= threshold;
339
+ };
337
340
  /**
338
341
  * Hook for handling swipe events on the drawer.
339
342
  */
340
- const useSwipeHandlers = ({ ref, closingThreshold, onCloseGesture, onOpenGesture }) => {
343
+ const useSwipeHandlers = ({ ref, closingThreshold, onCloseGesture, onOpenGesture, onDragEnd, }) => {
341
344
  const [isDragging, setIsDragging] = useState(false);
342
345
  const [dragDistance, setDragDistance] = useState(0);
343
346
  const currentY = useRef(0);
344
347
  const currentHeight = useRef(0);
345
348
  const geometry = useGeometry(ref);
349
+ const threshold = currentHeight.current * closingThreshold;
346
350
  const handlers = useSwipeable({
347
351
  onTouchStartOrOnMouseDown: () => {
348
352
  const y = getElementYTranslation(ref.current);
@@ -359,22 +363,22 @@ const useSwipeHandlers = ({ ref, closingThreshold, onCloseGesture, onOpenGesture
359
363
  }
360
364
  },
361
365
  onSwipedDown: (e) => {
362
- const threshold = currentHeight.current * closingThreshold;
363
- if (e.absY > threshold && onCloseGesture) {
366
+ if (isPassedThreshold(e.absY, threshold) && onCloseGesture) {
364
367
  onCloseGesture();
365
368
  }
366
369
  setIsDragging(false);
367
370
  setDragDistance(0);
368
371
  },
369
372
  onSwipedUp: (e) => {
370
- const threshold = currentHeight.current * closingThreshold;
371
- if (e.absY > threshold && onOpenGesture) {
373
+ if (isPassedThreshold(e.absY, threshold) && onOpenGesture) {
372
374
  onOpenGesture();
373
375
  }
374
376
  setIsDragging(false);
375
377
  setDragDistance(0);
376
378
  },
377
- onSwiped: () => void 0,
379
+ onSwiped: (e) => {
380
+ onDragEnd && onDragEnd(e.deltaY, e.velocity);
381
+ },
378
382
  trackMouse: true,
379
383
  trackTouch: true,
380
384
  preventScrollOnSwipe: true,
@@ -398,6 +402,8 @@ const cvaPuller = cvaMerge(["pt-1", "pb-4", "flex", "items-center", "justify-cen
398
402
  const cvaPullerIcon = cvaMerge(["block", "h-1", "w-8", "rounded-full", "bg-gray-400"]);
399
403
 
400
404
  const CLOSING_THRESHOLD = 0.15;
405
+ const VELOCITY_THRESHOLD = 0.3;
406
+ const SNAP_POINT_TRANSITION = "transform 500ms cubic-bezier(0.32,0.72,0,1)";
401
407
  /**
402
408
  *
403
409
  * SwipeableDrawer is a component that wraps the Drawer component to add swipeable functionality.
@@ -405,52 +411,133 @@ const CLOSING_THRESHOLD = 0.15;
405
411
  * The component manages its docked state based on the open prop and keepMountedWhenClosed prop.
406
412
  * It also applies styles dynamically based on whether the drawer is being dragged.
407
413
  */
408
- const SwipeableDrawer = forwardRef(({ open, onClose, onOpenGesture, children, keepMountedWhenClosed, className, position = "bottom", ...others }, ref) => {
409
- const drawerRefs = useRef(null);
410
- useImperativeHandle(ref, () => drawerRefs.current);
411
- const [isDocked, setIsDocked] = useState(keepMountedWhenClosed && !open);
414
+ const SwipeableDrawer = forwardRef(({ open, onClose, onOpenGesture, onSnapPointChange, children, keepMountedWhenClosed, className, position = "bottom", snapPoints, activeSnapPoint: activeSnapPointProp = null, container, ...others }, ref) => {
415
+ const drawerRef = useRef(null);
416
+ const containerRef = useRef(container !== null && container !== void 0 ? container : null);
417
+ const containerGeometry = useGeometry(containerRef);
418
+ useImperativeHandle(ref, () => drawerRef.current);
419
+ const [activeSnapPoint, setActiveSnapPoint] = useState(activeSnapPointProp);
420
+ const [snapPointOffsets, setSnapPointOffsets] = useState([]);
421
+ const activeSnapPointIndex = useMemo(() => { var _a; return (_a = snapPoints === null || snapPoints === void 0 ? void 0 : snapPoints.findIndex(snapPoint => snapPoint === activeSnapPoint)) !== null && _a !== void 0 ? _a : -1; }, [snapPoints, activeSnapPoint]);
422
+ const onHandleDragEnd = useCallback((draggedBy, velocity) => {
423
+ var _a, _b, _c;
424
+ if (!snapPoints || !snapPoints.length) {
425
+ return;
426
+ }
427
+ const direction = draggedBy > 0 ? -1 : 1;
428
+ // If the velocity is greater than the threshold and the dragged distance is less than 40% of the container height, snap to the next snap point
429
+ if (velocity > VELOCITY_THRESHOLD && Math.abs(draggedBy) < containerGeometry.height * 0.4) {
430
+ const newSnapPoint = (_a = snapPoints[activeSnapPointIndex + direction]) !== null && _a !== void 0 ? _a : null;
431
+ if (newSnapPoint) {
432
+ setActiveSnapPoint(newSnapPoint);
433
+ onSnapPointChange === null || onSnapPointChange === void 0 ? void 0 : onSnapPointChange(newSnapPoint);
434
+ }
435
+ return;
436
+ }
437
+ // Otherwise, find the closest snap point
438
+ // This is when the user is dragging but not swiping.
439
+ // For example, when the user is dragging the drawer to the bottom but not fast enough to trigger a swipe.
440
+ const currentPosition = ((_b = snapPointOffsets[activeSnapPointIndex]) !== null && _b !== void 0 ? _b : 0) + draggedBy;
441
+ const closestSnapPoint = snapPointOffsets.reduce((prev, curr) => {
442
+ if (typeof prev !== "number" || typeof curr !== "number") {
443
+ return prev;
444
+ }
445
+ return Math.abs(curr - currentPosition) < Math.abs(prev - currentPosition) ? curr : prev;
446
+ });
447
+ const snapPointIndex = snapPointOffsets.findIndex(offset => offset === closestSnapPoint);
448
+ const nextSnapPoint = (_c = snapPoints[snapPointIndex]) !== null && _c !== void 0 ? _c : null;
449
+ if (!nextSnapPoint) {
450
+ return;
451
+ }
452
+ setActiveSnapPoint(nextSnapPoint);
453
+ onSnapPointChange === null || onSnapPointChange === void 0 ? void 0 : onSnapPointChange(nextSnapPoint);
454
+ }, [snapPoints, snapPointOffsets, activeSnapPointIndex, containerGeometry, onSnapPointChange]);
412
455
  const { handlers, isDragging, dragDistance } = useSwipeHandlers({
413
- ref: drawerRefs,
456
+ ref: drawerRef,
414
457
  closingThreshold: CLOSING_THRESHOLD,
415
- onCloseGesture: onClose,
416
- onOpenGesture,
458
+ onCloseGesture: () => {
459
+ if (!snapPoints) {
460
+ onClose && onClose();
461
+ }
462
+ },
463
+ onOpenGesture: () => {
464
+ if (!snapPoints) {
465
+ onOpenGesture && onOpenGesture();
466
+ }
467
+ },
468
+ onDragEnd: onHandleDragEnd,
417
469
  });
418
470
  useScrollBlock({ isDisabled: !open || isDragging });
419
- const drawerStyle = useMemo(() => isDragging
420
- ? {
421
- transform: `translateY(${dragDistance}px)`,
422
- transition: "none",
471
+ useEffect(() => {
472
+ if (!snapPoints || !drawerRef.current) {
473
+ return;
423
474
  }
424
- : {}, [isDragging, dragDistance]);
475
+ const containerSize = containerRef.current ? containerGeometry.height : window.innerHeight;
476
+ const newSnapPointOffsets = snapPoints.map(snapPoint => {
477
+ // If snapPoint is a number, it is a percentage of the container size
478
+ if (typeof snapPoint === "number") {
479
+ const height = Math.floor(containerSize * snapPoint);
480
+ return containerSize - height;
481
+ }
482
+ // If snapPoint is a string, it is a pixel value
483
+ if (snapPoint.endsWith("px")) {
484
+ return containerSize - parseInt(snapPoint.slice(0, -1), 10);
485
+ }
486
+ return 0;
487
+ });
488
+ setSnapPointOffsets(newSnapPointOffsets);
489
+ }, [snapPoints, drawerRef, containerRef, containerGeometry]);
490
+ useEffect(() => {
491
+ if (container) {
492
+ containerRef.current = container;
493
+ }
494
+ }, [container]);
425
495
  useEffect(() => {
426
- if (!keepMountedWhenClosed) {
496
+ var _a;
497
+ if (!snapPoints || snapPoints.length === 0) {
427
498
  return;
428
499
  }
429
- if (isDocked && open) {
430
- setIsDocked(false);
500
+ const snapPointIndex = snapPoints.findIndex(offset => offset === activeSnapPointProp);
501
+ const nextSnapPoint = (_a = snapPoints[snapPointIndex > -1 ? snapPointIndex : 0]) !== null && _a !== void 0 ? _a : null;
502
+ if (!nextSnapPoint) {
503
+ return;
504
+ }
505
+ setActiveSnapPoint(nextSnapPoint);
506
+ onSnapPointChange === null || onSnapPointChange === void 0 ? void 0 : onSnapPointChange(nextSnapPoint);
507
+ }, [snapPoints, activeSnapPointProp, onSnapPointChange]);
508
+ const drawerStyle = useMemo(() => {
509
+ if (isDragging) {
510
+ return {
511
+ transform: `translateY(${dragDistance}px)`,
512
+ transition: "none",
513
+ };
431
514
  }
432
- else if (!isDocked && !open) {
433
- setIsDocked(true);
515
+ if (snapPoints && snapPointOffsets.length > 0 && activeSnapPointIndex !== -1) {
516
+ return {
517
+ transform: `translateY(${snapPointOffsets[activeSnapPointIndex]}px)`,
518
+ transition: SNAP_POINT_TRANSITION,
519
+ };
434
520
  }
435
- }, [open, isDocked, keepMountedWhenClosed]);
521
+ return {};
522
+ }, [isDragging, dragDistance, snapPointOffsets, activeSnapPointIndex, snapPoints]);
436
523
  useEffect(() => {
437
524
  var _a, _b;
438
- if (drawerRefs.current) {
439
- drawerRefs.current.style.transform = (_a = drawerStyle.transform) !== null && _a !== void 0 ? _a : "";
440
- drawerRefs.current.style.transition = (_b = drawerStyle.transition) !== null && _b !== void 0 ? _b : "";
525
+ if (!drawerRef.current) {
526
+ return;
441
527
  }
442
- }, [drawerStyle, drawerRefs]);
443
- return (jsxs(Drawer, { className: cvaSwipeableDrawer({ docked: isDocked, className }), "data-docked": isDocked, keepMountedWhenClosed: keepMountedWhenClosed, onClose: onClose, open: open, position: position, ref: drawerRefs, ...others, children: [jsx(DrawerPuller, { ...handlers }), children] }));
528
+ if (open) {
529
+ drawerRef.current.style.transform = (_a = drawerStyle.transform) !== null && _a !== void 0 ? _a : "";
530
+ drawerRef.current.style.transition = (_b = drawerStyle.transition) !== null && _b !== void 0 ? _b : "";
531
+ }
532
+ else {
533
+ drawerRef.current.style.transform = "";
534
+ drawerRef.current.style.transition = "";
535
+ }
536
+ }, [drawerStyle, drawerRef, open]);
537
+ return (jsxs(Drawer, { className: cvaSwipeableDrawer({ className }), keepMountedWhenClosed: keepMountedWhenClosed, onClose: onClose, open: open, position: position, ref: drawerRef, ...others, children: [jsx(DrawerPuller, { ...handlers }), children] }));
444
538
  });
445
539
  SwipeableDrawer.displayName = "SwipeableDrawer";
446
- const cvaSwipeableDrawer = cvaMerge([], {
447
- variants: {
448
- docked: {
449
- true: "translate-y-[calc(100%-30px)]",
450
- false: "",
451
- },
452
- },
453
- });
540
+ const cvaSwipeableDrawer = cvaMerge([]);
454
541
 
455
542
  /*
456
543
  * ----------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-drawer",
3
- "version": "0.1.31",
3
+ "version": "0.2.0",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -1,6 +1,7 @@
1
1
  import { Meta, StoryObj } from "@storybook/react";
2
+ import { SwipeableDrawer } from "../SwipeableDrawer/SwipeableDrawer";
2
3
  import { Drawer } from "./Drawer";
3
- type Story = StoryObj<typeof Drawer>;
4
+ type Story = StoryObj<typeof Drawer | typeof SwipeableDrawer>;
4
5
  declare const meta: Meta<typeof Drawer>;
5
6
  export default meta;
6
7
  export declare const packageName: () => import("react/jsx-runtime").JSX.Element;
@@ -9,4 +10,5 @@ export declare const InPortal: Story;
9
10
  export declare const Toggleable: Story;
10
11
  export declare const WithoutOverlay: Story;
11
12
  export declare const Swipeable: Story;
12
- export declare const SwipeableDocked: Story;
13
+ export declare const SwipeableWithSnapPoints: Story;
14
+ export declare const SwipeableWithSnapPointsWithoutContainer: Story;
@@ -1,6 +1,10 @@
1
1
  import { type DrawerProps } from "../Drawer/Drawer";
2
2
  export interface SwipeableDrawerProps extends DrawerProps {
3
3
  onOpenGesture?: () => void;
4
+ onSnapPointChange?: (snapPoint: number | string) => void;
5
+ snapPoints?: (number | string)[];
6
+ activeSnapPoint?: number | string | null;
7
+ container?: HTMLElement | null;
4
8
  }
5
9
  /**
6
10
  *
@@ -10,6 +14,4 @@ export interface SwipeableDrawerProps extends DrawerProps {
10
14
  * It also applies styles dynamically based on whether the drawer is being dragged.
11
15
  */
12
16
  export declare const SwipeableDrawer: import("react").ForwardRefExoticComponent<SwipeableDrawerProps & import("react").RefAttributes<HTMLDivElement>>;
13
- export declare const cvaSwipeableDrawer: (props?: ({
14
- docked?: boolean | null | undefined;
15
- } & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
17
+ export declare const cvaSwipeableDrawer: (props?: import("class-variance-authority/dist/types").ClassProp | undefined) => string;
@@ -4,6 +4,7 @@ interface UseSwipeHandlersProps {
4
4
  closingThreshold: number;
5
5
  onCloseGesture?: () => void;
6
6
  onOpenGesture?: () => void;
7
+ onDragEnd?: (dragDistance: number, velocity: number) => void;
7
8
  }
8
9
  /**
9
10
  * Retrieves the Y-axis translation value of a given HTML element.
@@ -15,7 +16,7 @@ export declare const getElementYTranslation: (element?: HTMLElement | null) => n
15
16
  /**
16
17
  * Hook for handling swipe events on the drawer.
17
18
  */
18
- export declare const useSwipeHandlers: ({ ref, closingThreshold, onCloseGesture, onOpenGesture }: UseSwipeHandlersProps) => {
19
+ export declare const useSwipeHandlers: ({ ref, closingThreshold, onCloseGesture, onOpenGesture, onDragEnd, }: UseSwipeHandlersProps) => {
19
20
  handlers: import("react-swipeable").SwipeableHandlers;
20
21
  isDragging: boolean;
21
22
  dragDistance: number;