@jobber/components 8.24.1 → 8.24.2-visual-reg-5479aeb.18

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.
Files changed (73) hide show
  1. package/ProgressIndicator.d.ts +1 -0
  2. package/{DescriptionList.js → ProgressIndicator.js} +3 -3
  3. package/dist/AtlantisThemeContext/AtlantisThemeContext.d.ts +1 -1
  4. package/dist/AtlantisThemeContext/index.cjs +1 -2
  5. package/dist/AtlantisThemeContext/index.mjs +1 -2
  6. package/dist/AtlantisThemeContext/themeStore.d.ts +11 -0
  7. package/dist/AtlantisThemeContext/types.d.ts +14 -0
  8. package/dist/AtlantisThemeContext/updateTheme.d.ts +1 -1
  9. package/dist/AtlantisThemeContext-cjs.js +58 -17
  10. package/dist/AtlantisThemeContext-es.js +60 -20
  11. package/dist/BottomSheet-cjs.js +8 -7
  12. package/dist/BottomSheet-es.js +9 -8
  13. package/dist/ComboboxChipRemove-es.js +2 -2
  14. package/dist/DatePicker-es.js +1 -1
  15. package/dist/Dialog/Dialog.d.ts +47 -3
  16. package/dist/Dialog/Dialog.types.d.ts +1 -15
  17. package/dist/Dialog/DialogBottomSheet.d.ts +12 -13
  18. package/dist/Dialog/DialogModal.d.ts +11 -11
  19. package/dist/Dialog/dialogComposableShared.d.ts +13 -3
  20. package/dist/DrawerDescription-cjs.js +4 -97
  21. package/dist/DrawerDescription-es.js +6 -98
  22. package/dist/InputNumberExperimental-cjs.js +21 -25
  23. package/dist/InputNumberExperimental-es.js +22 -26
  24. package/dist/MenuSubmenuTrigger-es.js +3 -3
  25. package/dist/Modal/index.cjs +12 -2
  26. package/dist/Modal/index.mjs +13 -3
  27. package/dist/ProgressBar/ProgressBar.d.ts +7 -0
  28. package/dist/ProgressBar-cjs.js +4 -0
  29. package/dist/ProgressBar-es.js +4 -0
  30. package/dist/ProgressIndicator/ProgressIndicator.d.ts +60 -0
  31. package/dist/ProgressIndicator/index.cjs +9 -0
  32. package/dist/ProgressIndicator/index.d.ts +1 -0
  33. package/dist/ProgressIndicator/index.mjs +3 -0
  34. package/dist/ProgressIndicator-cjs.js +43 -0
  35. package/dist/ProgressIndicator-es.js +41 -0
  36. package/dist/ScrollAreaViewport-cjs.js +2122 -12
  37. package/dist/ScrollAreaViewport-es.js +2118 -13
  38. package/dist/dialogReturnFocus-cjs.js +61 -34
  39. package/dist/dialogReturnFocus-es.js +63 -36
  40. package/dist/docs/AtlantisThemeContext/AtlantisThemeContext.md +41 -0
  41. package/dist/docs/DataTable/DataTable.md +0 -4
  42. package/dist/docs/Dialog/Dialog.md +1006 -0
  43. package/dist/docs/Icon/Icon.md +60 -53
  44. package/dist/docs/InputNumberExperimental/InputNumberExperimental.md +578 -0
  45. package/dist/docs/ProgressBar/ProgressBar.md +9 -0
  46. package/dist/docs/ProgressIndicator/ProgressIndicator.md +105 -0
  47. package/dist/docs/Table/Table.md +1 -3
  48. package/dist/docs/index.md +3 -1
  49. package/dist/index.cjs +3 -4
  50. package/dist/index.d.mts +1 -1
  51. package/dist/index.d.ts +1 -1
  52. package/dist/index.mjs +2 -3
  53. package/dist/isTypedArray-es.js +1 -1
  54. package/dist/primitives/InputNumberExperimental/InputNumberExperimental.d.ts +31 -15
  55. package/dist/primitives/InputNumberExperimental/types.d.ts +0 -1
  56. package/dist/styles.css +172 -51
  57. package/dist/unstyledPrimitives/index.cjs +218 -2230
  58. package/dist/unstyledPrimitives/index.mjs +224 -2236
  59. package/dist/useButton-es.js +1 -1
  60. package/dist/useScrollLock-es.js +2 -2
  61. package/dist/useScrollToActive-es.js +1 -1
  62. package/dist/utils/meta/meta.json +1 -1
  63. package/package.json +7 -7
  64. package/DescriptionList.d.ts +0 -1
  65. package/dist/DescriptionList/DescriptionList.d.ts +0 -11
  66. package/dist/DescriptionList/index.cjs +0 -10
  67. package/dist/DescriptionList/index.d.ts +0 -1
  68. package/dist/DescriptionList/index.mjs +0 -4
  69. package/dist/DescriptionList-cjs.js +0 -14
  70. package/dist/DescriptionList-es.js +0 -12
  71. package/dist/docs/DescriptionList/DescriptionList.md +0 -25
  72. package/dist/updateTheme-cjs.js +0 -20
  73. package/dist/updateTheme-es.js +0 -18
@@ -10,12 +10,12 @@ var DrawerDescription = require('../DrawerDescription-cjs.js');
10
10
  var DrawerTitle = require('../DrawerTitle-cjs.js');
11
11
  var ScrollAreaViewport = require('../ScrollAreaViewport-cjs.js');
12
12
  var useRenderElement = require('../useRenderElement-cjs.js');
13
+ var useScrollLock = require('../useScrollLock-cjs.js');
13
14
  var floatingUi_utils_dom = require('../floating-ui.utils.dom-cjs.js');
14
15
  var clamp = require('../clamp-cjs.js');
15
- var useScrollLock = require('../useScrollLock-cjs.js');
16
- var ReactDOM = require('react-dom');
17
16
  var MenuSubmenuTrigger = require('../MenuSubmenuTrigger-cjs.js');
18
17
  var useCompositeListItem = require('../useCompositeListItem-cjs.js');
18
+ var ReactDOM = require('react-dom');
19
19
  var SelectGroupLabel = require('../SelectGroupLabel-cjs.js');
20
20
  var floatingUi_utils = require('../floating-ui.utils-cjs.js');
21
21
  var NumberFieldInput = require('../NumberFieldInput-cjs.js');
@@ -216,7 +216,7 @@ var index_parts$6 = /*#__PURE__*/Object.freeze({
216
216
  Root: ScrollAreaViewport.DialogRoot,
217
217
  Title: DrawerTitle.DialogTitle,
218
218
  Trigger: ScrollAreaViewport.DialogTrigger,
219
- Viewport: DrawerDescription.DialogViewport,
219
+ Viewport: ScrollAreaViewport.DialogViewport,
220
220
  createHandle: DrawerDescription.createDialogHandle
221
221
  });
222
222
 
@@ -419,1198 +419,188 @@ function createVisualStateStore() {
419
419
  };
420
420
  }
421
421
 
422
- function isScrollable(element, axis,
423
- // When true, a container that overflows only once extra space is added (e.g. drawer
424
- // keyboard scroll slack) still counts, as long as it has layout size on the axis.
425
- allowOverflowIntent = false) {
426
- const style = floatingUi_utils_dom.getComputedStyle(element);
427
- if (axis === 'vertical') {
428
- const overflowY = style.overflowY;
429
- if (overflowY !== 'auto' && overflowY !== 'scroll') {
430
- return false;
431
- }
432
- return allowOverflowIntent ? element.clientHeight > 0 : element.scrollHeight > element.clientHeight;
433
- }
434
- const overflowX = style.overflowX;
435
- if (overflowX !== 'auto' && overflowX !== 'scroll') {
436
- return false;
437
- }
438
- return allowOverflowIntent ? element.clientWidth > 0 : element.scrollWidth > element.clientWidth;
439
- }
440
- function hasScrollableAncestor(target, root, axes) {
441
- // `getParentNode` crosses shadow boundaries (and slots), so a target inside a shadow root
442
- // still walks up to scrollable ancestors in the light DOM.
443
- let node = target;
444
- while (floatingUi_utils_dom.isHTMLElement(node) && node !== root && !floatingUi_utils_dom.isLastTraversableNode(node)) {
445
- for (const axis of axes) {
446
- if (isScrollable(node, axis)) {
447
- return true;
448
- }
449
- }
450
- node = floatingUi_utils_dom.getParentNode(node);
451
- }
452
- return false;
453
- }
454
- function findScrollableTouchTarget(target, root, axis = 'vertical', allowOverflowIntent = false) {
455
- // `getParentNode` crosses shadow boundaries (and slots), so a target inside a shadow root
456
- // still reaches a scrollable ancestor in the light DOM.
457
- let node = floatingUi_utils_dom.isHTMLElement(target) ? target : null;
458
- while (floatingUi_utils_dom.isHTMLElement(node) && node !== root && !floatingUi_utils_dom.isLastTraversableNode(node)) {
459
- if (isScrollable(node, axis, allowOverflowIntent)) {
460
- return node;
461
- }
462
- node = floatingUi_utils_dom.getParentNode(node);
463
- }
464
- return isScrollable(root, axis, allowOverflowIntent) ? root : null;
465
- }
466
-
467
- function getElementAtPoint(doc, x, y) {
468
- return typeof doc?.elementFromPoint === 'function' ? doc.elementFromPoint(x, y) : null;
469
- }
422
+ let DrawerSwipeAreaDataAttributes = function (DrawerSwipeAreaDataAttributes) {
423
+ /**
424
+ * Present when the drawer is open.
425
+ */
426
+ DrawerSwipeAreaDataAttributes[DrawerSwipeAreaDataAttributes["open"] = useScrollLock.CommonPopupDataAttributes.open] = "open";
427
+ /**
428
+ * Present when the drawer is closed.
429
+ */
430
+ DrawerSwipeAreaDataAttributes[DrawerSwipeAreaDataAttributes["closed"] = useScrollLock.CommonPopupDataAttributes.closed] = "closed";
431
+ /**
432
+ * Present when the swipe area is disabled.
433
+ */
434
+ DrawerSwipeAreaDataAttributes["disabled"] = "data-disabled";
435
+ /**
436
+ * Indicates the swipe direction.
437
+ * @type {'up' | 'down' | 'left' | 'right'}
438
+ */
439
+ DrawerSwipeAreaDataAttributes["swipeDirection"] = "data-swipe-direction";
440
+ /**
441
+ * Present when the drawer is being swiped.
442
+ */
443
+ DrawerSwipeAreaDataAttributes["swiping"] = "data-swiping";
444
+ return DrawerSwipeAreaDataAttributes;
445
+ }({});
470
446
 
471
- const DEFAULT_SWIPE_THRESHOLD = 40;
472
- const REVERSE_CANCEL_THRESHOLD = 10;
473
- const MIN_DRAG_THRESHOLD = 1;
474
- const MIN_VELOCITY_DURATION_MS = 50;
475
- const MIN_RELEASE_VELOCITY_DURATION_MS = 16;
476
- const MAX_RELEASE_VELOCITY_AGE_MS = 80;
477
- const DEFAULT_IGNORE_SELECTOR = 'button,a,input,select,textarea,label,[role="button"]';
478
- function getDisplacement(direction, deltaX, deltaY) {
479
- switch (direction) {
480
- case 'up':
481
- return -deltaY;
482
- case 'down':
483
- return deltaY;
484
- case 'left':
485
- return -deltaX;
486
- case 'right':
487
- return deltaX;
488
- default:
489
- return 0;
490
- }
491
- }
492
- function getElementTransform(element) {
493
- const computedStyle = floatingUi_utils_dom.getWindow(element).getComputedStyle(element);
494
- const transform = computedStyle.transform;
495
- let translateX = 0;
496
- let translateY = 0;
497
- let scale = 1;
498
- if (transform && transform !== 'none') {
499
- const matrix = transform.match(/matrix(?:3d)?\(([^)]+)\)/);
500
- if (matrix) {
501
- const values = matrix[1].split(', ').map(parseFloat);
502
- if (values.length === 6) {
503
- translateX = values[4];
504
- translateY = values[5];
505
- scale = Math.sqrt(values[0] * values[0] + values[1] * values[1]);
506
- } else if (values.length === 16) {
507
- translateX = values[12];
508
- translateY = values[13];
509
- scale = values[0];
510
- }
511
- }
512
- }
513
- return {
514
- x: translateX,
515
- y: translateY,
516
- scale
517
- };
518
- }
519
- function getValidTimeStamp(timeStamp) {
520
- return Number.isFinite(timeStamp) && timeStamp > 0 ? timeStamp : null;
521
- }
522
- function getDragTransform(dragOffset, scale) {
523
- return `translate3d(${dragOffset.x}px,${dragOffset.y}px,0) scale(${scale})`;
524
- }
525
- function hasPrimaryMouseButton(buttons) {
526
- return buttons % 2 === 1;
527
- }
528
- function safelyChangePointerCapture(element, pointerId, method) {
529
- const pointerCaptureMethod = element[method];
530
- if (typeof pointerCaptureMethod !== 'function') {
531
- return;
532
- }
533
- try {
534
- pointerCaptureMethod.call(element, pointerId);
535
- } catch (error) {
536
- if (error && typeof error === 'object' && 'name' in error && error.name === 'NotFoundError') {
537
- return;
538
- }
539
- throw error;
447
+ const DEFAULT_SWIPE_OPEN_RATIO = 0.5;
448
+ const MIN_SWIPE_START_DISTANCE = 1;
449
+ const VELOCITY_THRESHOLD = 0.1;
450
+ const FALLBACK_SWIPE_OPEN_THRESHOLD = 40;
451
+ const SWIPE_AREA_OPEN_HOOK = {
452
+ [DrawerSwipeAreaDataAttributes.open]: ''
453
+ };
454
+ const SWIPE_AREA_CLOSED_HOOK = {
455
+ [DrawerSwipeAreaDataAttributes.closed]: ''
456
+ };
457
+ const SWIPE_AREA_SWIPING_HOOK = {
458
+ [DrawerSwipeAreaDataAttributes.swiping]: ''
459
+ };
460
+ const SWIPE_AREA_DISABLED_HOOK = {
461
+ [DrawerSwipeAreaDataAttributes.disabled]: ''
462
+ };
463
+ const stateAttributesMapping$3 = {
464
+ open(value) {
465
+ return value ? SWIPE_AREA_OPEN_HOOK : SWIPE_AREA_CLOSED_HOOK;
466
+ },
467
+ swiping(value) {
468
+ return value ? SWIPE_AREA_SWIPING_HOOK : null;
469
+ },
470
+ swipeDirection(value) {
471
+ return value ? {
472
+ [DrawerSwipeAreaDataAttributes.swipeDirection]: value
473
+ } : null;
474
+ },
475
+ disabled(value) {
476
+ return value ? SWIPE_AREA_DISABLED_HOOK : null;
540
477
  }
478
+ };
479
+ const oppositeSwipeDirection = {
480
+ up: 'down',
481
+ down: 'up',
482
+ left: 'right',
483
+ right: 'left'
484
+ };
485
+ function resolveTouchAction(direction) {
486
+ return direction === 'left' || direction === 'right' ? 'pan-y' : 'pan-x';
541
487
  }
542
- function useSwipeDismiss(options) {
488
+
489
+ /**
490
+ * An invisible area that listens for swipe gestures to open the drawer.
491
+ * Renders a `<div>` element.
492
+ *
493
+ * Documentation: [Base UI Drawer](https://base-ui.com/react/components/drawer)
494
+ */
495
+ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function DrawerSwipeArea(componentProps, forwardedRef) {
543
496
  const {
544
- enabled,
545
- directions,
546
- elementRef,
547
- movementCssVars,
548
- canStart,
549
- ignoreSelectorWhenTouch = true,
550
- ignoreScrollableAncestors = false,
551
- swipeThreshold: swipeThresholdProp,
552
- onDismiss,
553
- onProgress,
554
- onCancel,
555
- onSwipeStart,
556
- onRelease,
557
- onSwipingChange,
558
- trackDrag = true
559
- } = options;
560
- const ignoreSelector = DEFAULT_IGNORE_SELECTOR;
561
- const primaryDirection = directions.length === 1 ? directions[0] : undefined;
562
- const swipeThresholdDefault = Math.max(0, typeof swipeThresholdProp === 'number' ? swipeThresholdProp : DEFAULT_SWIPE_THRESHOLD);
563
- const allowLeft = directions.includes('left');
564
- const allowRight = directions.includes('right');
565
- const allowUp = directions.includes('up');
566
- const allowDown = directions.includes('down');
567
- const hasHorizontal = allowLeft || allowRight;
568
- const hasVertical = allowUp || allowDown;
569
- const scrollAxes = React__namespace.useMemo(() => {
570
- const axes = [];
571
- if (hasVertical) {
572
- axes.push('vertical');
573
- }
574
- if (hasHorizontal) {
575
- axes.push('horizontal');
576
- }
577
- return axes;
578
- }, [hasHorizontal, hasVertical]);
579
- const [currentSwipeDirection, setCurrentSwipeDirection] = React__namespace.useState(undefined);
580
- const [isSwiping, setIsSwiping] = React__namespace.useState(false);
581
- const [dragDismissed, setDragDismissed] = React__namespace.useState(false);
582
- const dragStartPosRef = React__namespace.useRef({
583
- x: 0,
584
- y: 0
585
- });
586
- const dragOffsetRef = React__namespace.useRef({
587
- x: 0,
588
- y: 0
589
- });
590
- const lastMovePosRef = React__namespace.useRef(null);
591
- const initialTransformRef = React__namespace.useRef({
592
- x: 0,
593
- y: 0,
594
- scale: 1
595
- });
596
- const intendedSwipeDirectionRef = React__namespace.useRef(undefined);
597
- const maxSwipeDisplacementRef = React__namespace.useRef(0);
598
- const cancelledSwipeRef = React__namespace.useRef(false);
599
- const swipeCancelBaselineRef = React__namespace.useRef({
600
- x: 0,
601
- y: 0
602
- });
603
- const lockedDirectionRef = React__namespace.useRef(null);
604
- const isFirstPointerMoveRef = React__namespace.useRef(false);
605
- const pendingSwipeRef = React__namespace.useRef(false);
606
- const pendingSwipeStartPosRef = React__namespace.useRef(null);
607
- const swipeFromScrollableRef = React__namespace.useRef(false);
608
- const sawPrimaryButtonsOnMoveRef = React__namespace.useRef(false);
609
- const elementSizeRef = React__namespace.useRef({
610
- width: 0,
611
- height: 0
612
- });
613
- const swipeProgressRef = React__namespace.useRef(0);
614
- const swipeThresholdRef = React__namespace.useRef(swipeThresholdDefault);
615
- const swipeStartTimeRef = React__namespace.useRef(null);
616
- const lastDragSampleRef = React__namespace.useRef(null);
617
- const lastDragVelocityRef = React__namespace.useRef({
497
+ render,
498
+ className,
499
+ style,
500
+ disabled = false,
501
+ swipeDirection: swipeDirectionProp,
502
+ ...elementProps
503
+ } = componentProps;
504
+ const {
505
+ store
506
+ } = ScrollAreaViewport.useDialogRootContext();
507
+ const {
508
+ swipeDirection,
509
+ frontmostHeight
510
+ } = ScrollAreaViewport.useDrawerRootContext();
511
+ const providerContext = ScrollAreaViewport.useDrawerProviderContext();
512
+ const [swipeActive, setSwipeActive] = React__namespace.useState(false);
513
+ const releaseDismissTimeout = useButton.useTimeout();
514
+ const swipeAreaRef = React__namespace.useRef(null);
515
+ const swipeStartEventRef = React__namespace.useRef(null);
516
+ const openedBySwipeRef = React__namespace.useRef(false);
517
+ const dragDeltaRef = React__namespace.useRef({
618
518
  x: 0,
619
519
  y: 0
620
520
  });
621
- const lastProgressDetailsRef = React__namespace.useRef(null);
622
- const isSwipingRef = React__namespace.useRef(false);
623
- const dragStyleSnapshotRef = React__namespace.useRef(null);
624
- const setSwiping = useButton.useStableCallback(nextSwiping => {
625
- if (isSwipingRef.current === nextSwiping) {
626
- return;
627
- }
628
- isSwipingRef.current = nextSwiping;
629
- setIsSwiping(nextSwiping);
630
- onSwipingChange?.(nextSwiping);
521
+ const closedOffsetRef = React__namespace.useRef(null);
522
+ const appliedSwipeStylesRef = React__namespace.useRef(false);
523
+ const popupTransitionRef = React__namespace.useRef(null);
524
+ const swipeAreaId = useButton.useBaseUiId(componentProps.id);
525
+ const registerTrigger = useScrollLock.useTriggerRegistration(swipeAreaId, store);
526
+ const open = store.useState('open');
527
+ const resetDragDelta = useButton.useStableCallback(() => {
528
+ dragDeltaRef.current.x = 0;
529
+ dragDeltaRef.current.y = 0;
631
530
  });
632
- function resolveSwipeThreshold(direction) {
633
- if (!direction) {
634
- return;
635
- }
636
- if (typeof swipeThresholdProp !== 'function') {
637
- swipeThresholdRef.current = swipeThresholdDefault;
638
- return;
639
- }
640
- const element = elementRef.current;
641
- if (!element) {
642
- return;
643
- }
644
- const value = swipeThresholdProp({
645
- element,
646
- direction
531
+ const resolvedSwipeDirection = swipeDirectionProp ?? oppositeSwipeDirection[swipeDirection];
532
+ const dismissDirection = oppositeSwipeDirection[resolvedSwipeDirection];
533
+ const enabled = !disabled && (!open || swipeActive);
534
+ function disableDismissForSwipe() {
535
+ releaseDismissTimeout.clear();
536
+ store.context.outsidePressEnabledRef.current = false;
537
+ }
538
+ function enableDismissAfterRelease() {
539
+ // Safari can dispatch outside-press for the same swipe-open gesture
540
+ // after release, so defer re-enabling dismissal to the next macrotask.
541
+ releaseDismissTimeout.start(0, () => {
542
+ store.context.outsidePressEnabledRef.current = true;
647
543
  });
648
- swipeThresholdRef.current = Math.max(0, value);
649
544
  }
650
- const updateSwipeProgress = useButton.useStableCallback((progress, details) => {
651
- const nextProgress = Number.isFinite(progress) ? clamp.clamp(progress, 0, 1) : 0;
652
- const progressChanged = nextProgress !== swipeProgressRef.current;
653
- let detailsChanged = false;
654
- if (details) {
655
- const lastDetails = lastProgressDetailsRef.current;
656
- detailsChanged = !lastDetails || lastDetails.deltaX !== details.deltaX || lastDetails.deltaY !== details.deltaY || lastDetails.direction !== details.direction;
657
- }
658
- if (!progressChanged && !detailsChanged) {
659
- return;
660
- }
661
- swipeProgressRef.current = nextProgress;
662
- if (details) {
663
- lastProgressDetailsRef.current = details;
664
- } else if (progressChanged) {
665
- lastProgressDetailsRef.current = null;
666
- }
667
- onProgress?.(nextProgress, details);
668
- });
669
- const syncDragStyles = useButton.useStableCallback(swiping => {
670
- const element = elementRef.current;
671
- if (!trackDrag || !element) {
672
- if (!swiping) {
673
- dragStyleSnapshotRef.current = null;
674
- }
675
- return;
676
- }
677
- const style = element.style;
678
- const dragStyleSnapshot = dragStyleSnapshotRef.current;
679
- if (swiping) {
680
- if (!dragStyleSnapshot) {
681
- dragStyleSnapshotRef.current = [style.transition, style.transform];
682
- }
683
- style.transition = 'none';
684
- } else if (dragStyleSnapshot) {
685
- [style.transition, style.transform] = dragStyleSnapshot;
686
- dragStyleSnapshotRef.current = null;
687
- }
688
- const dragOffset = dragOffsetRef.current;
689
- const initialTransform = initialTransformRef.current;
690
- const deltaX = dragOffset.x - initialTransform.x;
691
- const deltaY = dragOffset.y - initialTransform.y;
692
- if (swiping) {
693
- style.transform = getDragTransform(dragOffset, initialTransform.scale);
694
- }
695
- style.setProperty(movementCssVars.x, `${deltaX}px`);
696
- style.setProperty(movementCssVars.y, `${deltaY}px`);
697
- });
698
- function recordDragSample(offset, timeStamp) {
699
- if (timeStamp === null) {
700
- return;
545
+ function resolvePopupSize() {
546
+ const popupElement = store.context.popupRef.current;
547
+ if (!popupElement) {
548
+ return null;
701
549
  }
702
- const lastSample = lastDragSampleRef.current;
703
- if (lastSample && timeStamp > lastSample.time) {
704
- const durationMs = Math.max(timeStamp - lastSample.time, MIN_RELEASE_VELOCITY_DURATION_MS);
705
- lastDragVelocityRef.current = {
706
- x: (offset.x - lastSample.x) / durationMs,
707
- y: (offset.y - lastSample.y) / durationMs
708
- };
550
+ const isHorizontal = dismissDirection === 'left' || dismissDirection === 'right';
551
+ const size = isHorizontal ? popupElement.offsetWidth : popupElement.offsetHeight;
552
+ if (size <= 0) {
553
+ return null;
709
554
  }
710
- lastDragSampleRef.current = {
711
- x: offset.x,
712
- y: offset.y,
713
- time: timeStamp
714
- };
555
+ return size;
715
556
  }
716
- const reset = React__namespace.useCallback(() => {
717
- setCurrentSwipeDirection(undefined);
718
- setSwiping(false);
719
- setDragDismissed(false);
720
- updateSwipeProgress(0);
721
- swipeThresholdRef.current = swipeThresholdDefault;
722
- dragStartPosRef.current = {
723
- x: 0,
724
- y: 0
725
- };
726
- dragOffsetRef.current = {
727
- x: 0,
728
- y: 0
729
- };
730
- initialTransformRef.current = {
731
- x: 0,
732
- y: 0,
733
- scale: 1
734
- };
735
- intendedSwipeDirectionRef.current = undefined;
736
- maxSwipeDisplacementRef.current = 0;
737
- cancelledSwipeRef.current = false;
738
- swipeCancelBaselineRef.current = {
739
- x: 0,
740
- y: 0
741
- };
742
- lockedDirectionRef.current = null;
743
- isFirstPointerMoveRef.current = false;
744
- lastMovePosRef.current = null;
745
- pendingSwipeRef.current = false;
746
- pendingSwipeStartPosRef.current = null;
747
- swipeFromScrollableRef.current = false;
748
- sawPrimaryButtonsOnMoveRef.current = false;
749
- elementSizeRef.current = {
750
- width: 0,
751
- height: 0
752
- };
753
- swipeStartTimeRef.current = null;
754
- lastDragSampleRef.current = null;
755
- lastDragVelocityRef.current = {
756
- x: 0,
757
- y: 0
758
- };
759
- lastProgressDetailsRef.current = null;
760
- syncDragStyles(false);
761
- }, [setSwiping, swipeThresholdDefault, syncDragStyles, updateSwipeProgress]);
762
- React__namespace.useEffect(() => {
763
- if (typeof swipeThresholdProp !== 'function') {
764
- swipeThresholdRef.current = swipeThresholdDefault;
557
+ function resolveClosedOffset() {
558
+ const offset = resolvePopupSize();
559
+ if (offset == null) {
560
+ return null;
765
561
  }
766
- }, [swipeThresholdDefault, swipeThresholdProp]);
767
- function getPrimaryPointerPosition(event) {
768
- if ('touches' in event) {
769
- const touch = event.touches[0];
770
- return touch ? {
771
- x: touch.clientX,
772
- y: touch.clientY
773
- } : null;
562
+ const popupElement = store.context.popupRef.current;
563
+ if (!popupElement) {
564
+ return offset;
774
565
  }
775
- return {
776
- x: event.clientX,
777
- y: event.clientY
778
- };
779
- }
780
- function isTouchLikeEvent(event) {
781
- if ('touches' in event) {
782
- return true;
566
+ const isHorizontal = dismissDirection === 'left' || dismissDirection === 'right';
567
+ const transform = ScrollAreaViewport.getElementTransform(popupElement);
568
+ const transformOffset = isHorizontal ? transform.x : transform.y;
569
+ if (Number.isFinite(transformOffset) && Math.abs(transformOffset) > 0.5) {
570
+ return Math.min(offset, Math.abs(transformOffset));
783
571
  }
784
- return event.pointerType === 'touch';
785
- }
786
- function getTargetAtPoint(position, nativeEvent) {
787
- const doc = useButton.ownerDocument(elementRef.current);
788
- const elementAtPoint = getElementAtPoint(doc, position.x, position.y);
789
- const target = elementAtPoint ?? useButton.getTarget(nativeEvent);
790
- return target;
572
+ return offset;
791
573
  }
792
- function findGestureScrollableTouchTarget(target, root) {
793
- if (hasHorizontal && !hasVertical) {
794
- return findScrollableTouchTarget(target, root, 'horizontal');
795
- }
796
- if (hasVertical && !hasHorizontal) {
797
- return findScrollableTouchTarget(target, root, 'vertical');
574
+ function resolveSwipeOpenThreshold() {
575
+ const popupSize = resolvePopupSize();
576
+ if (popupSize == null) {
577
+ return FALLBACK_SWIPE_OPEN_THRESHOLD;
798
578
  }
799
- return findScrollableTouchTarget(target, root, 'vertical') ?? findScrollableTouchTarget(target, root, 'horizontal');
579
+ return popupSize * DEFAULT_SWIPE_OPEN_RATIO;
800
580
  }
801
- function startSwipeAtPosition(event, position, startOptions) {
802
- swipeFromScrollableRef.current = false;
803
- const touchLike = isTouchLikeEvent(event);
804
- const target = getTargetAtPoint(position, event.nativeEvent);
805
- const doc = useButton.ownerDocument(elementRef.current);
806
- const body = doc.body;
807
- const scrollableTarget = touchLike && body ? findGestureScrollableTouchTarget(target, body) : null;
808
- const ignoreScrollableTarget = startOptions?.ignoreScrollableTarget ?? false;
809
- if (scrollableTarget && !ignoreScrollableTarget) {
810
- return false;
581
+ function applySwipeMovement() {
582
+ if (!swipeActive) {
583
+ return;
811
584
  }
812
- swipeFromScrollableRef.current = Boolean(scrollableTarget && ignoreScrollableTarget);
813
- const isInteractiveElement = target ? target.closest(ignoreSelector) : false;
814
- if (isInteractiveElement && (!touchLike || ignoreSelectorWhenTouch)) {
815
- return false;
585
+ const popupElement = store.context.popupRef.current;
586
+ if (!popupElement) {
587
+ return;
816
588
  }
817
- const element = elementRef.current;
818
- if (ignoreScrollableAncestors && element && target && scrollAxes.length > 0) {
819
- const ignoreAncestors = startOptions?.ignoreScrollableAncestors ?? false;
820
- if (!ignoreAncestors && hasScrollableAncestor(target, element, scrollAxes)) {
821
- return false;
822
- }
589
+ if (!store.select('open') || !store.select('mounted')) {
590
+ return;
823
591
  }
824
- cancelledSwipeRef.current = false;
825
- intendedSwipeDirectionRef.current = undefined;
826
- maxSwipeDisplacementRef.current = 0;
827
- dragStartPosRef.current = position;
828
- swipeStartTimeRef.current = getValidTimeStamp(event.timeStamp);
829
- swipeCancelBaselineRef.current = position;
830
- lastMovePosRef.current = position;
831
- if (element) {
832
- elementSizeRef.current = {
833
- width: element.offsetWidth,
834
- height: element.offsetHeight
835
- };
836
- resolveSwipeThreshold(primaryDirection);
837
- const transform = getElementTransform(element);
838
- initialTransformRef.current = transform;
839
- dragOffsetRef.current = {
840
- x: transform.x,
841
- y: transform.y
842
- };
843
- recordDragSample({
844
- x: transform.x,
845
- y: transform.y
846
- }, swipeStartTimeRef.current);
847
- if (!('touches' in event)) {
848
- safelyChangePointerCapture(element, event.pointerId, 'setPointerCapture');
849
- }
592
+ if (closedOffsetRef.current == null) {
593
+ closedOffsetRef.current = resolveClosedOffset();
850
594
  }
851
- onSwipeStart?.(event.nativeEvent);
852
- setSwiping(true);
853
- lockedDirectionRef.current = null;
854
- isFirstPointerMoveRef.current = true;
855
- updateSwipeProgress(0);
856
- syncDragStyles(true);
857
- return true;
858
- }
859
- function resetPendingSwipeState() {
860
- clearPendingSwipeStartState();
861
- swipeFromScrollableRef.current = false;
862
- lastMovePosRef.current = null;
863
- }
864
- function clearPendingSwipeStartState() {
865
- pendingSwipeRef.current = false;
866
- pendingSwipeStartPosRef.current = null;
867
- }
868
- function cancelSwipeInteraction(event) {
869
- resetPendingSwipeState();
870
- if (!isSwipingRef.current) {
871
- return;
872
- }
873
- setSwiping(false);
874
- lockedDirectionRef.current = null;
875
- const resolvedInitialTransform = initialTransformRef.current;
876
- dragOffsetRef.current = {
877
- x: resolvedInitialTransform.x,
878
- y: resolvedInitialTransform.y
879
- };
880
- setCurrentSwipeDirection(undefined);
881
- sawPrimaryButtonsOnMoveRef.current = false;
882
- syncDragStyles(false);
883
- const element = elementRef.current;
884
- if (element) {
885
- safelyChangePointerCapture(element, event.pointerId, 'releasePointerCapture');
886
- }
887
- updateSwipeProgress(0, {
888
- deltaX: 0,
889
- deltaY: 0,
890
- direction: undefined
891
- });
892
- onCancel?.(event.nativeEvent);
893
- }
894
- function applyDirectionalDamping(deltaX, deltaY) {
895
- const exponent = value => value >= 0 ? value ** 0.5 : -(Math.abs(value) ** 0.5);
896
- const dampAxis = (delta, allowNegative, allowPositive) => {
897
- if (!allowNegative && delta < 0) {
898
- return exponent(delta);
899
- }
900
- if (!allowPositive && delta > 0) {
901
- return exponent(delta);
902
- }
903
- return delta;
904
- };
905
- const newDeltaX = hasHorizontal ? dampAxis(deltaX, allowLeft, allowRight) : exponent(deltaX);
906
- const newDeltaY = hasVertical ? dampAxis(deltaY, allowUp, allowDown) : exponent(deltaY);
907
- return {
908
- x: newDeltaX,
909
- y: newDeltaY
910
- };
911
- }
912
- function canSwipeFromScrollEdgeOnPendingMove(scrollTarget, deltaX, deltaY) {
913
- const absDeltaX = Math.abs(deltaX);
914
- const absDeltaY = Math.abs(deltaY);
915
- const useVerticalAxis = hasVertical && deltaY !== 0 && (!hasHorizontal || absDeltaY >= absDeltaX);
916
- if (useVerticalAxis) {
917
- const maxScrollTop = Math.max(0, scrollTarget.scrollHeight - scrollTarget.clientHeight);
918
- const atTop = scrollTarget.scrollTop <= 0;
919
- const atBottom = scrollTarget.scrollTop >= maxScrollTop;
920
- const movingDown = deltaY > 0;
921
- const movingUp = deltaY < 0;
922
- const canSwipeDown = movingDown && atTop && allowDown;
923
- const canSwipeUp = movingUp && atBottom && allowUp;
924
- return canSwipeDown || canSwipeUp;
925
- }
926
- const useHorizontalAxis = hasHorizontal && deltaX !== 0 && (!hasVertical || absDeltaX > absDeltaY);
927
- if (useHorizontalAxis) {
928
- const maxScrollLeft = Math.max(0, scrollTarget.scrollWidth - scrollTarget.clientWidth);
929
- const atLeft = scrollTarget.scrollLeft <= 0;
930
- const atRight = scrollTarget.scrollLeft >= maxScrollLeft;
931
- const movingRight = deltaX > 0;
932
- const movingLeft = deltaX < 0;
933
- const canSwipeRight = movingRight && atLeft && allowRight;
934
- const canSwipeLeft = movingLeft && atRight && allowLeft;
935
- return canSwipeRight || canSwipeLeft;
936
- }
937
- return null;
938
- }
939
- const handleStart = useButton.useStableCallback(event => {
940
- if (!enabled) {
941
- return;
942
- }
943
- if (event.defaultPrevented || event.nativeEvent.defaultPrevented) {
944
- return;
945
- }
946
- if (!('touches' in event) && event.button !== 0) {
947
- return;
948
- }
949
- const startPos = getPrimaryPointerPosition(event);
950
- if (!startPos) {
951
- return;
952
- }
953
- pendingSwipeRef.current = true;
954
- pendingSwipeStartPosRef.current = startPos;
955
- swipeFromScrollableRef.current = false;
956
- sawPrimaryButtonsOnMoveRef.current = false;
957
- const allowedToStart = canStart ? canStart(startPos, {
958
- nativeEvent: event.nativeEvent,
959
- direction: primaryDirection
960
- }) : true;
961
- if (!allowedToStart) {
962
- return;
963
- }
964
- if (startSwipeAtPosition(event, startPos)) {
965
- clearPendingSwipeStartState();
966
- }
967
- });
968
- function handleMoveCore(event, position, movement) {
969
- if (!enabled || !isSwipingRef.current) {
970
- return;
971
- }
972
- const target = useButton.getTarget(event.nativeEvent);
973
- if (isTouchLikeEvent(event) && !swipeFromScrollableRef.current) {
974
- const boundaryElement = event.currentTarget;
975
- if (findGestureScrollableTouchTarget(target, boundaryElement)) {
976
- return;
977
- }
978
- }
979
- if (!('touches' in event)) {
980
- // Prevent text selection on Safari
981
- event.preventDefault();
982
- }
983
- if (isFirstPointerMoveRef.current) {
984
- // Adjust the starting position to the current position on the first move
985
- // to account for the delay between pointerdown and the first pointermove on iOS.
986
- dragStartPosRef.current = position;
987
- const moveTime = getValidTimeStamp(event.timeStamp);
988
- if (moveTime !== null) {
989
- swipeStartTimeRef.current = moveTime;
990
- }
991
- isFirstPointerMoveRef.current = false;
992
- }
993
- const clientX = position.x;
994
- const clientY = position.y;
995
- const movementX = movement.x;
996
- const movementY = movement.y;
997
- if (movementY < 0 && clientY > swipeCancelBaselineRef.current.y || movementY > 0 && clientY < swipeCancelBaselineRef.current.y) {
998
- swipeCancelBaselineRef.current = {
999
- x: swipeCancelBaselineRef.current.x,
1000
- y: clientY
1001
- };
1002
- }
1003
- if (movementX < 0 && clientX > swipeCancelBaselineRef.current.x || movementX > 0 && clientX < swipeCancelBaselineRef.current.x) {
1004
- swipeCancelBaselineRef.current = {
1005
- x: clientX,
1006
- y: swipeCancelBaselineRef.current.y
1007
- };
1008
- }
1009
- const deltaX = clientX - dragStartPosRef.current.x;
1010
- const deltaY = clientY - dragStartPosRef.current.y;
1011
- const cancelDeltaY = clientY - swipeCancelBaselineRef.current.y;
1012
- const cancelDeltaX = clientX - swipeCancelBaselineRef.current.x;
1013
- let lockedDirection = lockedDirectionRef.current;
1014
- if (lockedDirection === null && hasHorizontal && hasVertical) {
1015
- const movementDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
1016
- if (movementDistance >= MIN_DRAG_THRESHOLD) {
1017
- lockedDirection = Math.abs(deltaX) > Math.abs(deltaY) ? 'horizontal' : 'vertical';
1018
- lockedDirectionRef.current = lockedDirection;
1019
- }
1020
- }
1021
- let candidate;
1022
- if (!intendedSwipeDirectionRef.current) {
1023
- if (lockedDirection === 'vertical') {
1024
- if (deltaY > 0) {
1025
- candidate = 'down';
1026
- } else if (deltaY < 0) {
1027
- candidate = 'up';
1028
- }
1029
- } else if (lockedDirection === 'horizontal') {
1030
- if (deltaX > 0) {
1031
- candidate = 'right';
1032
- } else if (deltaX < 0) {
1033
- candidate = 'left';
1034
- }
1035
- } else if (Math.abs(deltaX) >= Math.abs(deltaY)) {
1036
- candidate = deltaX > 0 ? 'right' : 'left';
1037
- } else {
1038
- candidate = deltaY > 0 ? 'down' : 'up';
1039
- }
1040
- if (candidate) {
1041
- const isAllowed = candidate === 'left' && allowLeft || candidate === 'right' && allowRight || candidate === 'up' && allowUp || candidate === 'down' && allowDown;
1042
- if (isAllowed) {
1043
- intendedSwipeDirectionRef.current = candidate;
1044
- maxSwipeDisplacementRef.current = getDisplacement(candidate, deltaX, deltaY);
1045
- setCurrentSwipeDirection(candidate);
1046
- resolveSwipeThreshold(candidate);
1047
- }
1048
- }
1049
- } else {
1050
- const direction = intendedSwipeDirectionRef.current;
1051
- const currentDisplacement = getDisplacement(direction, cancelDeltaX, cancelDeltaY);
1052
- if (currentDisplacement > swipeThresholdRef.current) {
1053
- cancelledSwipeRef.current = false;
1054
- setCurrentSwipeDirection(direction);
1055
- } else if (!(allowLeft && allowRight) && !(allowUp && allowDown) && maxSwipeDisplacementRef.current - currentDisplacement >= REVERSE_CANCEL_THRESHOLD) {
1056
- // Mark that a change-of-mind has occurred
1057
- cancelledSwipeRef.current = true;
1058
- }
1059
- }
1060
- const dampedDelta = applyDirectionalDamping(deltaX, deltaY);
1061
- let newOffsetX = initialTransformRef.current.x;
1062
- let newOffsetY = initialTransformRef.current.y;
1063
- if (lockedDirection === 'horizontal') {
1064
- if (hasHorizontal) {
1065
- newOffsetX += dampedDelta.x;
1066
- }
1067
- } else if (lockedDirection === 'vertical') {
1068
- if (hasVertical) {
1069
- newOffsetY += dampedDelta.y;
1070
- }
1071
- } else {
1072
- if (hasHorizontal) {
1073
- newOffsetX += dampedDelta.x;
1074
- }
1075
- if (hasVertical) {
1076
- newOffsetY += dampedDelta.y;
1077
- }
1078
- }
1079
- dragOffsetRef.current = {
1080
- x: newOffsetX,
1081
- y: newOffsetY
1082
- };
1083
- syncDragStyles(true);
1084
- recordDragSample({
1085
- x: newOffsetX,
1086
- y: newOffsetY
1087
- }, getValidTimeStamp(event.timeStamp));
1088
- const dragDeltaX = newOffsetX - initialTransformRef.current.x;
1089
- const dragDeltaY = newOffsetY - initialTransformRef.current.y;
1090
- const swipeDirectionDetails = intendedSwipeDirectionRef.current;
1091
- const progressDirection = primaryDirection ?? intendedSwipeDirectionRef.current;
1092
- if (!progressDirection) {
1093
- updateSwipeProgress(0, {
1094
- deltaX: dragDeltaX,
1095
- deltaY: dragDeltaY,
1096
- direction: swipeDirectionDetails
1097
- });
1098
- return;
1099
- }
1100
- const size = progressDirection === 'left' || progressDirection === 'right' ? elementSizeRef.current.width : elementSizeRef.current.height;
1101
- const scale = initialTransformRef.current.scale || 1;
1102
- if (size <= 0 || scale <= 0) {
1103
- updateSwipeProgress(0, {
1104
- deltaX: dragDeltaX,
1105
- deltaY: dragDeltaY,
1106
- direction: swipeDirectionDetails
1107
- });
1108
- return;
1109
- }
1110
- const progressDisplacement = getDisplacement(progressDirection, newOffsetX - initialTransformRef.current.x, newOffsetY - initialTransformRef.current.y);
1111
- if (progressDisplacement <= 0) {
1112
- updateSwipeProgress(0, {
1113
- deltaX: dragDeltaX,
1114
- deltaY: dragDeltaY,
1115
- direction: swipeDirectionDetails
1116
- });
1117
- return;
1118
- }
1119
- updateSwipeProgress(progressDisplacement / (size * scale), {
1120
- deltaX: dragDeltaX,
1121
- deltaY: dragDeltaY,
1122
- direction: swipeDirectionDetails
1123
- });
1124
- }
1125
- const handleEnd = useButton.useStableCallback(event => {
1126
- if (!enabled) {
1127
- return;
1128
- }
1129
- const resolvedDragOffset = dragOffsetRef.current;
1130
- const resolvedInitialTransform = initialTransformRef.current;
1131
- const releaseDeltaX = resolvedDragOffset.x - resolvedInitialTransform.x;
1132
- const releaseDeltaY = resolvedDragOffset.y - resolvedInitialTransform.y;
1133
- const progressDetails = {
1134
- deltaX: releaseDeltaX,
1135
- deltaY: releaseDeltaY,
1136
- direction: intendedSwipeDirectionRef.current
1137
- };
1138
- if (!isSwipingRef.current) {
1139
- resetPendingSwipeState();
1140
- updateSwipeProgress(0, progressDetails);
1141
- return;
1142
- }
1143
- setSwiping(false);
1144
- lockedDirectionRef.current = null;
1145
- resetPendingSwipeState();
1146
- sawPrimaryButtonsOnMoveRef.current = false;
1147
- const element = elementRef.current;
1148
- if (element) {
1149
- if (!('touches' in event)) {
1150
- safelyChangePointerCapture(element, event.pointerId, 'releasePointerCapture');
1151
- }
1152
- }
1153
- const deltaX = releaseDeltaX;
1154
- const deltaY = releaseDeltaY;
1155
- const startTime = swipeStartTimeRef.current;
1156
- const endTime = getValidTimeStamp(event.timeStamp);
1157
- const durationMs = startTime !== null && endTime !== null && endTime > startTime ? endTime - startTime : 0;
1158
- const velocityDurationMs = durationMs > 0 ? Math.max(durationMs, MIN_VELOCITY_DURATION_MS) : 0;
1159
- const velocityX = velocityDurationMs > 0 ? deltaX / velocityDurationMs : 0;
1160
- const velocityY = velocityDurationMs > 0 ? deltaY / velocityDurationMs : 0;
1161
- let releaseVelocityX = lastDragVelocityRef.current.x;
1162
- let releaseVelocityY = lastDragVelocityRef.current.y;
1163
- const lastSample = lastDragSampleRef.current;
1164
- if (lastSample && endTime !== null && endTime >= lastSample.time) {
1165
- const ageMs = endTime - lastSample.time;
1166
- if (ageMs <= MAX_RELEASE_VELOCITY_AGE_MS) {
1167
- const sampleDurationMs = Math.max(ageMs, MIN_RELEASE_VELOCITY_DURATION_MS);
1168
- const deltaFromLastSampleX = resolvedDragOffset.x - lastSample.x;
1169
- const deltaFromLastSampleY = resolvedDragOffset.y - lastSample.y;
1170
- const sampleVelocityX = deltaFromLastSampleX / sampleDurationMs;
1171
- const sampleVelocityY = deltaFromLastSampleY / sampleDurationMs;
1172
- if (sampleVelocityX !== 0) {
1173
- releaseVelocityX = sampleVelocityX;
1174
- }
1175
- if (sampleVelocityY !== 0) {
1176
- releaseVelocityY = sampleVelocityY;
1177
- }
1178
- } else {
1179
- releaseVelocityX = 0;
1180
- releaseVelocityY = 0;
1181
- }
1182
- }
1183
- const releaseDecision = onRelease?.({
1184
- event: event.nativeEvent,
1185
- direction: intendedSwipeDirectionRef.current,
1186
- deltaX,
1187
- deltaY,
1188
- velocityX,
1189
- velocityY,
1190
- releaseVelocityX,
1191
- releaseVelocityY
1192
- });
1193
- const hasReleaseDecision = typeof releaseDecision === 'boolean';
1194
- if (cancelledSwipeRef.current && !hasReleaseDecision) {
1195
- dragOffsetRef.current = {
1196
- x: resolvedInitialTransform.x,
1197
- y: resolvedInitialTransform.y
1198
- };
1199
- setCurrentSwipeDirection(undefined);
1200
- syncDragStyles(false);
1201
- updateSwipeProgress(0, progressDetails);
1202
- return;
1203
- }
1204
- let shouldClose = false;
1205
- let dismissDirection;
1206
- if (hasReleaseDecision) {
1207
- shouldClose = releaseDecision;
1208
- dismissDirection = intendedSwipeDirectionRef.current ?? primaryDirection;
1209
- } else {
1210
- for (const direction of directions) {
1211
- switch (direction) {
1212
- case 'right':
1213
- if (deltaX > swipeThresholdRef.current) {
1214
- shouldClose = true;
1215
- dismissDirection = 'right';
1216
- }
1217
- break;
1218
- case 'left':
1219
- if (deltaX < -swipeThresholdRef.current) {
1220
- shouldClose = true;
1221
- dismissDirection = 'left';
1222
- }
1223
- break;
1224
- case 'down':
1225
- if (deltaY > swipeThresholdRef.current) {
1226
- shouldClose = true;
1227
- dismissDirection = 'down';
1228
- }
1229
- break;
1230
- case 'up':
1231
- if (deltaY < -swipeThresholdRef.current) {
1232
- shouldClose = true;
1233
- dismissDirection = 'up';
1234
- }
1235
- break;
1236
- }
1237
- if (shouldClose) {
1238
- break;
1239
- }
1240
- }
1241
- }
1242
- if (shouldClose && dismissDirection) {
1243
- setCurrentSwipeDirection(dismissDirection);
1244
- setDragDismissed(true);
1245
- syncDragStyles(false);
1246
- onDismiss?.(event.nativeEvent, {
1247
- direction: dismissDirection
1248
- });
1249
- } else {
1250
- dragOffsetRef.current = {
1251
- x: resolvedInitialTransform.x,
1252
- y: resolvedInitialTransform.y
1253
- };
1254
- setCurrentSwipeDirection(undefined);
1255
- syncDragStyles(false);
1256
- updateSwipeProgress(0, progressDetails);
1257
- }
1258
- });
1259
- const handleMove = useButton.useStableCallback(event => {
1260
- const currentPos = getPrimaryPointerPosition(event);
1261
- if (!currentPos) {
1262
- return;
1263
- }
1264
- let endAfterMove = false;
1265
- if (!('touches' in event)) {
1266
- const hasPrimaryButton = hasPrimaryMouseButton(event.buttons);
1267
- if (hasPrimaryButton) {
1268
- sawPrimaryButtonsOnMoveRef.current = true;
1269
- }
1270
-
1271
- // Cancel the swipe if a non-primary button takes over the interaction.
1272
- // This handles cases where a right-click interrupts dragging.
1273
- if (event.buttons !== 0 && !hasPrimaryButton) {
1274
- cancelSwipeInteraction(event);
1275
- return;
1276
- }
1277
-
1278
- // A `buttons: 0` pointermove means the primary button was already released, so the gesture is
1279
- // over even if no pointerup reached us. On fast trackpad flicks this trailing move is
1280
- // dispatched just before pointerup; treat it as the release (mirroring touchend) instead of
1281
- // cancelling and snapping the element back.
1282
- if (event.buttons === 0 && sawPrimaryButtonsOnMoveRef.current) {
1283
- if (!isSwipingRef.current) {
1284
- // The gesture never activated — discard it.
1285
- handleEnd(event);
1286
- return;
1287
- }
1288
- // This release move can itself carry the threshold-crossing displacement (and the peak
1289
- // release velocity), so let it flow through `handleMoveCore` below to update the drag
1290
- // offset / velocity sample, then commit the release afterwards.
1291
- endAfterMove = true;
1292
- }
1293
- }
1294
- if (!isSwiping && pendingSwipeRef.current) {
1295
- if (!isTouchLikeEvent(event) && (event.defaultPrevented || event.nativeEvent.defaultPrevented)) {
1296
- resetPendingSwipeState();
1297
- return;
1298
- }
1299
- const allowedToStart = canStart ? canStart(currentPos, {
1300
- nativeEvent: event.nativeEvent,
1301
- direction: primaryDirection
1302
- }) : true;
1303
- if (allowedToStart) {
1304
- const pendingStartPos = pendingSwipeStartPosRef.current;
1305
- let ignoreScrollableOnStart = false;
1306
- if (isTouchLikeEvent(event)) {
1307
- const element = elementRef.current;
1308
- if (pendingStartPos && element) {
1309
- const target = getTargetAtPoint(currentPos, event.nativeEvent);
1310
- const doc = useButton.ownerDocument(element);
1311
- const body = doc.body;
1312
- const scrollTarget = body ? findGestureScrollableTouchTarget(target, body) : null;
1313
- if (scrollTarget && (useButton.contains(element, scrollTarget) || useButton.contains(scrollTarget, element))) {
1314
- const deltaX = currentPos.x - pendingStartPos.x;
1315
- const deltaY = currentPos.y - pendingStartPos.y;
1316
- const canSwipeFromEdge = canSwipeFromScrollEdgeOnPendingMove(scrollTarget, deltaX, deltaY);
1317
- if (canSwipeFromEdge === false) {
1318
- return;
1319
- }
1320
- if (canSwipeFromEdge === true) {
1321
- ignoreScrollableOnStart = true;
1322
- }
1323
- }
1324
- }
1325
- }
1326
- const started = startSwipeAtPosition(event, currentPos, {
1327
- ignoreScrollableTarget: ignoreScrollableOnStart,
1328
- ignoreScrollableAncestors: ignoreScrollableOnStart
1329
- });
1330
- if (started) {
1331
- if (pendingStartPos && ignoreScrollableOnStart) {
1332
- // Preserve displacement between touchstart and the move that activates swipe from
1333
- // a scroll-edge so quick flicks can dismiss.
1334
- clearPendingSwipeStartState();
1335
- dragStartPosRef.current = pendingStartPos;
1336
- swipeCancelBaselineRef.current = pendingStartPos;
1337
- lastMovePosRef.current = pendingStartPos;
1338
- isFirstPointerMoveRef.current = false;
1339
- } else {
1340
- // Start from the current in-bounds position without dropping follow-up move
1341
- // displacement; this avoids jumps when entering from outside the element while
1342
- // keeping swipe tracking responsive on the next move.
1343
- clearPendingSwipeStartState();
1344
- swipeFromScrollableRef.current = false;
1345
- }
1346
- }
1347
- }
1348
- }
1349
- const previousPos = lastMovePosRef.current;
1350
- const movement = previousPos === null ? {
1351
- x: 0,
1352
- y: 0
1353
- } : {
1354
- x: currentPos.x - previousPos.x,
1355
- y: currentPos.y - previousPos.y
1356
- };
1357
- lastMovePosRef.current = currentPos;
1358
- handleMoveCore(event, currentPos, movement);
1359
-
1360
- // `endAfterMove` is only set in the non-touch branch above; the `'touches'` guard re-narrows the
1361
- // event type for `handleEnd` after the shared move handling has run.
1362
- if (endAfterMove && !('touches' in event)) {
1363
- handleEnd(event);
1364
- }
1365
- });
1366
-
1367
- // Feeds a native touchmove into the swipe pipeline. Used by consumers that claim the gesture
1368
- // in a capture-phase listener and stop it from reaching React's delegated touch handlers.
1369
- const moveNative = useButton.useStableCallback((nativeEvent, currentTarget) => {
1370
- handleMove({
1371
- touches: nativeEvent.touches,
1372
- currentTarget,
1373
- nativeEvent,
1374
- defaultPrevented: nativeEvent.defaultPrevented,
1375
- timeStamp: nativeEvent.timeStamp
1376
- });
1377
- });
1378
- const getDragStyles = React__namespace.useCallback(() => {
1379
- const dragOffset = dragOffsetRef.current;
1380
- const initialTransform = initialTransformRef.current;
1381
- const deltaX = dragOffset.x - initialTransform.x;
1382
- const deltaY = dragOffset.y - initialTransform.y;
1383
- if (!isSwiping && deltaX === 0 && deltaY === 0 && !dragDismissed) {
1384
- return {
1385
- [movementCssVars.x]: '0px',
1386
- [movementCssVars.y]: '0px'
1387
- };
1388
- }
1389
- return {
1390
- transition: isSwiping ? 'none' : undefined,
1391
- // While swiping, freeze the element at its current visual transform so it doesn't snap to the
1392
- // end position.
1393
- transform: isSwiping ? getDragTransform(dragOffset, initialTransform.scale) : undefined,
1394
- [movementCssVars.x]: `${deltaX}px`,
1395
- [movementCssVars.y]: `${deltaY}px`
1396
- };
1397
- }, [dragDismissed, isSwiping, movementCssVars]);
1398
- const getPointerProps = React__namespace.useCallback(() => {
1399
- if (!enabled) {
1400
- return {};
1401
- }
1402
- return {
1403
- onPointerDown: handleStart,
1404
- onPointerMove: handleMove,
1405
- onPointerUp: handleEnd,
1406
- onPointerCancel: handleEnd
1407
- };
1408
- }, [enabled, handleEnd, handleMove, handleStart]);
1409
- const getTouchProps = React__namespace.useCallback(() => {
1410
- if (!enabled) {
1411
- return {};
1412
- }
1413
- return {
1414
- onTouchStart: handleStart,
1415
- onTouchMove: handleMove,
1416
- onTouchEnd: handleEnd,
1417
- onTouchCancel: handleEnd
1418
- };
1419
- }, [enabled, handleEnd, handleMove, handleStart]);
1420
- return {
1421
- swiping: isSwiping,
1422
- swipeDirection: currentSwipeDirection,
1423
- dragDismissed,
1424
- getPointerProps,
1425
- getTouchProps,
1426
- moveNative,
1427
- getDragStyles,
1428
- reset
1429
- };
1430
- }
1431
-
1432
- let DrawerSwipeAreaDataAttributes = function (DrawerSwipeAreaDataAttributes) {
1433
- /**
1434
- * Present when the drawer is open.
1435
- */
1436
- DrawerSwipeAreaDataAttributes[DrawerSwipeAreaDataAttributes["open"] = useScrollLock.CommonPopupDataAttributes.open] = "open";
1437
- /**
1438
- * Present when the drawer is closed.
1439
- */
1440
- DrawerSwipeAreaDataAttributes[DrawerSwipeAreaDataAttributes["closed"] = useScrollLock.CommonPopupDataAttributes.closed] = "closed";
1441
- /**
1442
- * Present when the swipe area is disabled.
1443
- */
1444
- DrawerSwipeAreaDataAttributes["disabled"] = "data-disabled";
1445
- /**
1446
- * Indicates the swipe direction.
1447
- * @type {'up' | 'down' | 'left' | 'right'}
1448
- */
1449
- DrawerSwipeAreaDataAttributes["swipeDirection"] = "data-swipe-direction";
1450
- /**
1451
- * Present when the drawer is being swiped.
1452
- */
1453
- DrawerSwipeAreaDataAttributes["swiping"] = "data-swiping";
1454
- return DrawerSwipeAreaDataAttributes;
1455
- }({});
1456
-
1457
- const DEFAULT_SWIPE_OPEN_RATIO = 0.5;
1458
- const MIN_SWIPE_START_DISTANCE = 1;
1459
- const VELOCITY_THRESHOLD = 0.1;
1460
- const FALLBACK_SWIPE_OPEN_THRESHOLD = 40;
1461
- const SWIPE_AREA_OPEN_HOOK = {
1462
- [DrawerSwipeAreaDataAttributes.open]: ''
1463
- };
1464
- const SWIPE_AREA_CLOSED_HOOK = {
1465
- [DrawerSwipeAreaDataAttributes.closed]: ''
1466
- };
1467
- const SWIPE_AREA_SWIPING_HOOK = {
1468
- [DrawerSwipeAreaDataAttributes.swiping]: ''
1469
- };
1470
- const SWIPE_AREA_DISABLED_HOOK = {
1471
- [DrawerSwipeAreaDataAttributes.disabled]: ''
1472
- };
1473
- const stateAttributesMapping$3 = {
1474
- open(value) {
1475
- return value ? SWIPE_AREA_OPEN_HOOK : SWIPE_AREA_CLOSED_HOOK;
1476
- },
1477
- swiping(value) {
1478
- return value ? SWIPE_AREA_SWIPING_HOOK : null;
1479
- },
1480
- swipeDirection(value) {
1481
- return value ? {
1482
- [DrawerSwipeAreaDataAttributes.swipeDirection]: value
1483
- } : null;
1484
- },
1485
- disabled(value) {
1486
- return value ? SWIPE_AREA_DISABLED_HOOK : null;
1487
- }
1488
- };
1489
- const oppositeSwipeDirection = {
1490
- up: 'down',
1491
- down: 'up',
1492
- left: 'right',
1493
- right: 'left'
1494
- };
1495
- function resolveTouchAction(direction) {
1496
- return direction === 'left' || direction === 'right' ? 'pan-y' : 'pan-x';
1497
- }
1498
-
1499
- /**
1500
- * An invisible area that listens for swipe gestures to open the drawer.
1501
- * Renders a `<div>` element.
1502
- *
1503
- * Documentation: [Base UI Drawer](https://base-ui.com/react/components/drawer)
1504
- */
1505
- const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function DrawerSwipeArea(componentProps, forwardedRef) {
1506
- const {
1507
- render,
1508
- className,
1509
- style,
1510
- disabled = false,
1511
- swipeDirection: swipeDirectionProp,
1512
- ...elementProps
1513
- } = componentProps;
1514
- const {
1515
- store
1516
- } = ScrollAreaViewport.useDialogRootContext();
1517
- const {
1518
- swipeDirection,
1519
- frontmostHeight
1520
- } = ScrollAreaViewport.useDrawerRootContext();
1521
- const providerContext = ScrollAreaViewport.useDrawerProviderContext();
1522
- const [swipeActive, setSwipeActive] = React__namespace.useState(false);
1523
- const releaseDismissTimeout = useButton.useTimeout();
1524
- const swipeAreaRef = React__namespace.useRef(null);
1525
- const swipeStartEventRef = React__namespace.useRef(null);
1526
- const openedBySwipeRef = React__namespace.useRef(false);
1527
- const dragDeltaRef = React__namespace.useRef({
1528
- x: 0,
1529
- y: 0
1530
- });
1531
- const closedOffsetRef = React__namespace.useRef(null);
1532
- const appliedSwipeStylesRef = React__namespace.useRef(false);
1533
- const popupTransitionRef = React__namespace.useRef(null);
1534
- const swipeAreaId = useButton.useBaseUiId(componentProps.id);
1535
- const registerTrigger = useScrollLock.useTriggerRegistration(swipeAreaId, store);
1536
- const open = store.useState('open');
1537
- const resetDragDelta = useButton.useStableCallback(() => {
1538
- dragDeltaRef.current.x = 0;
1539
- dragDeltaRef.current.y = 0;
1540
- });
1541
- const resolvedSwipeDirection = swipeDirectionProp ?? oppositeSwipeDirection[swipeDirection];
1542
- const dismissDirection = oppositeSwipeDirection[resolvedSwipeDirection];
1543
- const enabled = !disabled && (!open || swipeActive);
1544
- function disableDismissForSwipe() {
1545
- releaseDismissTimeout.clear();
1546
- store.context.outsidePressEnabledRef.current = false;
1547
- }
1548
- function enableDismissAfterRelease() {
1549
- // Safari can dispatch outside-press for the same swipe-open gesture
1550
- // after release, so defer re-enabling dismissal to the next macrotask.
1551
- releaseDismissTimeout.start(0, () => {
1552
- store.context.outsidePressEnabledRef.current = true;
1553
- });
1554
- }
1555
- function resolvePopupSize() {
1556
- const popupElement = store.context.popupRef.current;
1557
- if (!popupElement) {
1558
- return null;
1559
- }
1560
- const isHorizontal = dismissDirection === 'left' || dismissDirection === 'right';
1561
- const size = isHorizontal ? popupElement.offsetWidth : popupElement.offsetHeight;
1562
- if (size <= 0) {
1563
- return null;
1564
- }
1565
- return size;
1566
- }
1567
- function resolveClosedOffset() {
1568
- const offset = resolvePopupSize();
1569
- if (offset == null) {
1570
- return null;
1571
- }
1572
- const popupElement = store.context.popupRef.current;
1573
- if (!popupElement) {
1574
- return offset;
1575
- }
1576
- const isHorizontal = dismissDirection === 'left' || dismissDirection === 'right';
1577
- const transform = getElementTransform(popupElement);
1578
- const transformOffset = isHorizontal ? transform.x : transform.y;
1579
- if (Number.isFinite(transformOffset) && Math.abs(transformOffset) > 0.5) {
1580
- return Math.min(offset, Math.abs(transformOffset));
1581
- }
1582
- return offset;
1583
- }
1584
- function resolveSwipeOpenThreshold() {
1585
- const popupSize = resolvePopupSize();
1586
- if (popupSize == null) {
1587
- return FALLBACK_SWIPE_OPEN_THRESHOLD;
1588
- }
1589
- return popupSize * DEFAULT_SWIPE_OPEN_RATIO;
1590
- }
1591
- function applySwipeMovement() {
1592
- if (!swipeActive) {
1593
- return;
1594
- }
1595
- const popupElement = store.context.popupRef.current;
1596
- if (!popupElement) {
1597
- return;
1598
- }
1599
- if (!store.select('open') || !store.select('mounted')) {
1600
- return;
1601
- }
1602
- if (closedOffsetRef.current == null) {
1603
- closedOffsetRef.current = resolveClosedOffset();
1604
- }
1605
- const closedOffset = closedOffsetRef.current;
1606
- if (!closedOffset || !Number.isFinite(closedOffset) || closedOffset <= 0) {
595
+ const closedOffset = closedOffsetRef.current;
596
+ if (!closedOffset || !Number.isFinite(closedOffset) || closedOffset <= 0) {
1607
597
  return;
1608
598
  }
1609
599
  const {
1610
600
  x,
1611
601
  y
1612
602
  } = dragDeltaRef.current;
1613
- const displacement = getDisplacement(resolvedSwipeDirection, x, y);
603
+ const displacement = ScrollAreaViewport.getDisplacement(resolvedSwipeDirection, x, y);
1614
604
  const clampedDisplacement = Math.max(0, displacement);
1615
605
  const dampedDisplacement = clampedDisplacement > closedOffset ? closedOffset + Math.sqrt(clampedDisplacement - closedOffset) : clampedDisplacement;
1616
606
  const remaining = closedOffset - dampedDisplacement;
@@ -1692,7 +682,7 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
1692
682
  resetDragDelta();
1693
683
  clearSwipeStyles();
1694
684
  }
1695
- const swipe = useSwipeDismiss({
685
+ const swipe = ScrollAreaViewport.useSwipeDismiss({
1696
686
  enabled,
1697
687
  directions: [resolvedSwipeDirection],
1698
688
  elementRef: swipeAreaRef,
@@ -1715,856 +705,89 @@ const DrawerSwipeArea = /*#__PURE__*/React__namespace.forwardRef(function Drawer
1715
705
  if (!swipeStartEventRef.current) {
1716
706
  return;
1717
707
  }
1718
- dragDeltaRef.current.x = details.deltaX;
1719
- dragDeltaRef.current.y = details.deltaY;
1720
- if (details.direction !== resolvedSwipeDirection) {
1721
- return;
1722
- }
1723
- const displacement = getDisplacement(resolvedSwipeDirection, details.deltaX, details.deltaY);
1724
- if (displacement < MIN_SWIPE_START_DISTANCE && !openedBySwipeRef.current) {
1725
- return;
1726
- }
1727
- if (!openedBySwipeRef.current) {
1728
- openDrawer(swipeStartEventRef.current);
1729
- }
1730
- applySwipeMovement();
1731
- },
1732
- onRelease({
1733
- event,
1734
- direction,
1735
- deltaX,
1736
- deltaY,
1737
- releaseVelocityX,
1738
- releaseVelocityY
1739
- }) {
1740
- const displacement = getDisplacement(resolvedSwipeDirection, deltaX, deltaY);
1741
- const releaseVelocity = getDisplacement(resolvedSwipeDirection, releaseVelocityX, releaseVelocityY);
1742
- const threshold = resolveSwipeOpenThreshold();
1743
- const hasEnoughDistance = threshold != null && displacement >= threshold;
1744
- const hasEnoughVelocity = releaseVelocity >= VELOCITY_THRESHOLD;
1745
- const shouldOpen = threshold != null && direction === resolvedSwipeDirection && (hasEnoughDistance || hasEnoughVelocity) && !disabled;
1746
- if (shouldOpen) {
1747
- if (!store.select('open')) {
1748
- openDrawer(event);
1749
- }
1750
- } else if (openedBySwipeRef.current) {
1751
- closeDrawer(event);
1752
- }
1753
- finishSwipeInteraction();
1754
- return false;
1755
- },
1756
- onCancel: finishSwipeInteraction
1757
- });
1758
- const swipePointerProps = swipe.getPointerProps();
1759
- const swipeTouchProps = swipe.getTouchProps();
1760
- const resetSwipe = swipe.reset;
1761
- React__namespace.useEffect(() => {
1762
- if (!enabled) {
1763
- resetSwipe();
1764
- resetDragDelta();
1765
- clearSwipeStyles();
1766
- resetSwipeInteractionState();
1767
- }
1768
- }, [clearSwipeStyles, enabled, resetDragDelta, resetSwipe]);
1769
- React__namespace.useEffect(() => {
1770
- return () => {
1771
- store.context.outsidePressEnabledRef.current = true;
1772
- };
1773
- }, [store]);
1774
- const state = {
1775
- open,
1776
- swiping: swipe.swiping,
1777
- swipeDirection: resolvedSwipeDirection,
1778
- disabled
1779
- };
1780
- return useRenderElement.useRenderElement('div', componentProps, {
1781
- state,
1782
- ref: [forwardedRef, swipeAreaRef, registerTrigger],
1783
- stateAttributesMapping: stateAttributesMapping$3,
1784
- props: [{
1785
- role: 'presentation',
1786
- 'aria-hidden': true,
1787
- style: {
1788
- pointerEvents: !enabled ? 'none' : undefined,
1789
- touchAction: resolveTouchAction(resolvedSwipeDirection)
1790
- },
1791
- onPointerDown(event) {
1792
- if (event.pointerType === 'touch') {
1793
- return;
1794
- }
1795
- swipePointerProps.onPointerDown?.(event);
1796
-
1797
- // Prevent native text selection/drag gestures from competing with swipe-open dragging.
1798
- if (event.cancelable) {
1799
- event.preventDefault();
1800
- }
1801
- },
1802
- onPointerMove(event) {
1803
- if (event.pointerType === 'touch') {
1804
- return;
1805
- }
1806
- swipePointerProps.onPointerMove?.(event);
1807
- },
1808
- onPointerUp(event) {
1809
- if (event.pointerType === 'touch') {
1810
- return;
1811
- }
1812
- swipePointerProps.onPointerUp?.(event);
1813
- },
1814
- onPointerCancel(event) {
1815
- if (event.pointerType === 'touch') {
1816
- return;
1817
- }
1818
- swipePointerProps.onPointerCancel?.(event);
1819
- }
1820
- }, swipeTouchProps, swipeAreaId ? {
1821
- id: swipeAreaId
1822
- } : undefined, elementProps]
1823
- });
1824
- });
1825
- if (process.env.NODE_ENV !== "production") DrawerSwipeArea.displayName = "DrawerSwipeArea";
1826
-
1827
- const DrawerVirtualKeyboardContext = /*#__PURE__*/React__namespace.createContext(undefined);
1828
- if (process.env.NODE_ENV !== "production") DrawerVirtualKeyboardContext.displayName = "DrawerVirtualKeyboardContext";
1829
- function useDrawerVirtualKeyboardContext() {
1830
- return React__namespace.useContext(DrawerVirtualKeyboardContext);
1831
- }
1832
-
1833
- const MIN_SWIPE_THRESHOLD = 10;
1834
- const FAST_SWIPE_VELOCITY = 0.5;
1835
- const SNAP_VELOCITY_THRESHOLD = 0.5;
1836
- const SNAP_VELOCITY_MULTIPLIER = 300;
1837
- const MAX_SNAP_VELOCITY = 4;
1838
- const MIN_SWIPE_RELEASE_VELOCITY = 0.2;
1839
- const MAX_SWIPE_RELEASE_VELOCITY = 4;
1840
- const MIN_SWIPE_RELEASE_DURATION_MS = 80;
1841
- const MAX_SWIPE_RELEASE_DURATION_MS = 360;
1842
- const MIN_SWIPE_RELEASE_SCALAR = 0.1;
1843
- const MAX_SWIPE_RELEASE_SCALAR = 1;
1844
- const DRAWER_CONTENT_SELECTOR = `[${ScrollAreaViewport.DRAWER_CONTENT_ATTRIBUTE}]`;
1845
- /**
1846
- * A positioning container for the drawer popup that can be made scrollable.
1847
- * Renders a `<div>` element.
1848
- *
1849
- * Documentation: [Base UI Drawer](https://base-ui.com/react/components/drawer)
1850
- */
1851
- const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerViewport(props, forwardedRef) {
1852
- const {
1853
- render,
1854
- className,
1855
- style,
1856
- children,
1857
- ...elementProps
1858
- } = props;
1859
- const {
1860
- store
1861
- } = ScrollAreaViewport.useDialogRootContext();
1862
- const popupRef = store.context.popupRef;
1863
- const backdropRef = store.context.backdropRef;
1864
- const {
1865
- swipeDirection,
1866
- notifyParentSwipingChange,
1867
- notifyParentSwipeProgressChange,
1868
- frontmostHeight,
1869
- snapToSequentialPoints
1870
- } = ScrollAreaViewport.useDrawerRootContext();
1871
- const providerContext = ScrollAreaViewport.useDrawerProviderContext();
1872
- const {
1873
- snapPoints,
1874
- resolvedSnapPoints,
1875
- activeSnapPoint,
1876
- activeSnapPointOffset,
1877
- setActiveSnapPoint,
1878
- popupHeight
1879
- } = ScrollAreaViewport.useDrawerSnapPoints();
1880
- const open = store.useState('open');
1881
- const mounted = store.useState('mounted');
1882
- const nested = store.useState('nested');
1883
- const nestedOpenDrawerCount = store.useState('nestedOpenDrawerCount');
1884
- const viewportElement = store.useState('viewportElement');
1885
- const popupElementState = store.useState('popupElement');
1886
- const visualStateStore = providerContext?.visualStateStore;
1887
- const nestedDrawerOpen = nestedOpenDrawerCount > 0;
1888
- const scrollAxis = swipeDirection === 'left' || swipeDirection === 'right' ? 'horizontal' : 'vertical';
1889
- const isVerticalScrollAxis = scrollAxis === 'vertical';
1890
- const crossScrollAxis = isVerticalScrollAxis ? 'horizontal' : 'vertical';
1891
- const [swipeRelease, setSwipeRelease] = React__namespace.useState(null);
1892
- const pendingSwipeCloseSnapPointRef = React__namespace.useRef(undefined);
1893
- const resetSwipeRef = React__namespace.useRef(null);
1894
- const controlledDismissFrame = useButton.useAnimationFrame();
1895
- const swipingRef = React__namespace.useRef(false);
1896
- const nestedSwipeActiveRef = React__namespace.useRef(false);
1897
- const lastPointerTypeRef = React__namespace.useRef('');
1898
- const ignoreNextTouchStartFromPenRef = React__namespace.useRef(false);
1899
- const ignoreTouchSwipeRef = React__namespace.useRef(false);
1900
- const touchScrollStateRef = React__namespace.useRef(null);
1901
- const virtualKeyboard = useDrawerVirtualKeyboardContext();
1902
- const snapPointRange = React__namespace.useMemo(() => {
1903
- if (!snapPoints || snapPoints.length < 2) {
1904
- return null;
1905
- }
1906
- if (swipeDirection !== 'down' && swipeDirection !== 'up') {
1907
- return null;
1908
- }
1909
- if (resolvedSnapPoints.length < 2) {
1910
- return null;
1911
- }
1912
- const offsets = resolvedSnapPoints.map(point => point.offset).filter(offset => Number.isFinite(offset)).sort((a, b) => a - b);
1913
- if (offsets.length < 2) {
1914
- return null;
1915
- }
1916
- const minOffset = offsets[0];
1917
- const nextOffset = offsets[1];
1918
- const maxOffset = offsets[offsets.length - 1];
1919
- let range = nextOffset - minOffset;
1920
- if (!Number.isFinite(range) || range <= 0) {
1921
- const fallbackRange = maxOffset - minOffset;
1922
- if (!Number.isFinite(fallbackRange) || fallbackRange <= 0) {
1923
- return null;
1924
- }
1925
- range = fallbackRange;
1926
- }
1927
- return {
1928
- minOffset,
1929
- range
1930
- };
1931
- }, [resolvedSnapPoints, snapPoints, swipeDirection]);
1932
- const snapPointProgress = React__namespace.useMemo(() => {
1933
- if (!snapPointRange || activeSnapPointOffset === null) {
1934
- return null;
1935
- }
1936
- return clamp.clamp((activeSnapPointOffset - snapPointRange.minOffset) / snapPointRange.range, 0, 1);
1937
- }, [activeSnapPointOffset, snapPointRange]);
1938
- const swipeDirections = React__namespace.useMemo(() => {
1939
- if (snapPoints && snapPoints.length > 0 && (swipeDirection === 'down' || swipeDirection === 'up')) {
1940
- return swipeDirection === 'down' ? ['down', 'up'] : ['up', 'down'];
1941
- }
1942
- return [swipeDirection];
1943
- }, [snapPoints, swipeDirection]);
1944
- const setSwipeDismissed = useButton.useStableCallback(dismissed => {
1945
- setSwipeDismissedElements(popupRef.current, backdropRef.current, dismissed);
1946
- });
1947
- const clearSwipeRelease = useButton.useStableCallback(() => {
1948
- setSwipeDismissed(false);
1949
- popupRef.current?.removeAttribute(useButton.TransitionStatusDataAttributes.endingStyle);
1950
- setSwipeRelease(null);
1951
- });
1952
- const finishNestedSwipe = useButton.useStableCallback(() => {
1953
- if (!nestedSwipeActiveRef.current) {
1954
- return;
1955
- }
1956
- nestedSwipeActiveRef.current = false;
1957
- notifyParentSwipingChange?.(false);
1958
- });
1959
- const applySwipeProgress = useButton.useStableCallback(({
1960
- resolvedProgress,
1961
- shouldTrackProgress,
1962
- notifyParent
1963
- }) => {
1964
- const isActive = open && !nested && shouldTrackProgress;
1965
- const swipeProgress = isActive ? resolvedProgress : 0;
1966
- const nestedSwipeProgress = open && shouldTrackProgress ? resolvedProgress : 0;
1967
- if (notifyParent && notifyParentSwipeProgressChange) {
1968
- notifyParentSwipeProgressChange(nestedSwipeProgress);
1969
- if (nestedSwipeProgress <= 0) {
1970
- finishNestedSwipe();
1971
- }
1972
- }
1973
- visualStateStore?.set({
1974
- swipeProgress,
1975
- frontmostHeight: swipeProgress > 0 ? frontmostHeight : 0
1976
- });
1977
- const backdropElement = backdropRef.current;
1978
- if (!backdropElement) {
1979
- return;
1980
- }
1981
- if (!isActive || swipeProgress <= 0) {
1982
- backdropElement.style.setProperty(ScrollAreaViewport.DrawerBackdropCssVars.swipeProgress, '0');
1983
- backdropElement.style.removeProperty(ScrollAreaViewport.DrawerPopupCssVars.height);
1984
- return;
1985
- }
1986
- backdropElement.style.setProperty(ScrollAreaViewport.DrawerBackdropCssVars.swipeProgress, `${swipeProgress}`);
1987
- if (frontmostHeight > 0) {
1988
- backdropElement.style.setProperty(ScrollAreaViewport.DrawerPopupCssVars.height, `${frontmostHeight}px`);
1989
- } else {
1990
- backdropElement.style.removeProperty(ScrollAreaViewport.DrawerPopupCssVars.height);
1991
- }
1992
- });
1993
- function resolveSwipeRelease({
1994
- direction,
1995
- deltaX,
1996
- deltaY,
1997
- velocityX,
1998
- velocityY,
1999
- releaseVelocityX,
2000
- releaseVelocityY
2001
- }) {
2002
- if (!direction) {
2003
- return null;
2004
- }
2005
- const popupElement = store.context.popupRef.current;
2006
- if (!popupElement) {
2007
- return null;
2008
- }
2009
- const size = direction === 'left' || direction === 'right' ? popupElement.offsetWidth : popupElement.offsetHeight;
2010
- if (!Number.isFinite(size) || size <= 0) {
2011
- return null;
2012
- }
2013
- const axisDelta = direction === 'left' || direction === 'right' ? deltaX : deltaY;
2014
- const snapPointBaseOffset = snapPoints && snapPoints.length > 0 ? activeSnapPointOffset ?? 0 : 0;
2015
- let baseOffset = 0;
2016
- if (direction === 'down') {
2017
- baseOffset = snapPointBaseOffset;
2018
- } else if (direction === 'up') {
2019
- baseOffset = -snapPointBaseOffset;
2020
- }
2021
- const translation = baseOffset + axisDelta;
2022
- const translationAlongDirection = direction === 'left' || direction === 'up' ? -translation : translation;
2023
- const remainingDistance = Math.max(0, size - translationAlongDirection);
2024
- if (!Number.isFinite(remainingDistance) || remainingDistance <= 0) {
2025
- return null;
2026
- }
2027
- const axisVelocity = direction === 'left' || direction === 'right' ? releaseVelocityX : releaseVelocityY;
2028
- const fallbackVelocity = direction === 'left' || direction === 'right' ? velocityX : velocityY;
2029
- const resolvedVelocity = Math.abs(axisVelocity) > 0 && Number.isFinite(axisVelocity) ? axisVelocity : fallbackVelocity;
2030
- const directionalVelocity = direction === 'left' || direction === 'up' ? -resolvedVelocity : resolvedVelocity;
2031
- if (!Number.isFinite(directionalVelocity) || directionalVelocity <= MIN_SWIPE_RELEASE_VELOCITY) {
2032
- return null;
2033
- }
2034
- const clampedVelocity = clamp.clamp(directionalVelocity, MIN_SWIPE_RELEASE_VELOCITY, MAX_SWIPE_RELEASE_VELOCITY);
2035
- const durationMs = clamp.clamp(remainingDistance / clampedVelocity, MIN_SWIPE_RELEASE_DURATION_MS, MAX_SWIPE_RELEASE_DURATION_MS);
2036
- if (!Number.isFinite(durationMs)) {
2037
- return null;
2038
- }
2039
- const normalizedDuration = (durationMs - MIN_SWIPE_RELEASE_DURATION_MS) / (MAX_SWIPE_RELEASE_DURATION_MS - MIN_SWIPE_RELEASE_DURATION_MS);
2040
- const durationScalar = clamp.clamp(MIN_SWIPE_RELEASE_SCALAR + normalizedDuration * (MAX_SWIPE_RELEASE_SCALAR - MIN_SWIPE_RELEASE_SCALAR), MIN_SWIPE_RELEASE_SCALAR, MAX_SWIPE_RELEASE_SCALAR);
2041
- if (!Number.isFinite(durationScalar) || durationScalar <= 0) {
2042
- return null;
2043
- }
2044
- return durationScalar;
2045
- }
2046
- function updateNestedSwipeActive(details) {
2047
- if (nestedSwipeActiveRef.current || !details) {
2048
- return;
2049
- }
2050
- const direction = details.direction ?? swipeDirection;
2051
- const delta = direction === 'left' || direction === 'right' ? details.deltaX : details.deltaY;
2052
- if (!Number.isFinite(delta) || Math.abs(delta) < MIN_SWIPE_THRESHOLD) {
2053
- return;
2054
- }
2055
- nestedSwipeActiveRef.current = true;
2056
- notifyParentSwipingChange?.(true);
2057
- }
2058
- const swipe = useSwipeDismiss({
2059
- enabled: mounted && !nestedDrawerOpen,
2060
- directions: swipeDirections,
2061
- elementRef: store.context.popupRef,
2062
- ignoreSelectorWhenTouch: false,
2063
- ignoreScrollableAncestors: true,
2064
- movementCssVars: {
2065
- x: ScrollAreaViewport.DrawerPopupCssVars.swipeMovementX,
2066
- y: ScrollAreaViewport.DrawerPopupCssVars.swipeMovementY
2067
- },
2068
- onSwipeStart(event) {
2069
- if ('touches' in event || 'pointerType' in event && event.pointerType === 'touch') {
2070
- return;
2071
- }
2072
- const popupElement = popupRef.current;
2073
- if (!popupElement) {
2074
- return;
2075
- }
2076
- const doc = useButton.ownerDocument(popupElement);
2077
- const selection = doc.getSelection?.();
2078
- if (!selection || selection.isCollapsed) {
708
+ dragDeltaRef.current.x = details.deltaX;
709
+ dragDeltaRef.current.y = details.deltaY;
710
+ if (details.direction !== resolvedSwipeDirection) {
2079
711
  return;
2080
712
  }
2081
- const anchorElement = floatingUi_utils_dom.isElement(selection.anchorNode) ? selection.anchorNode : selection.anchorNode?.parentElement;
2082
- const focusElement = floatingUi_utils_dom.isElement(selection.focusNode) ? selection.focusNode : selection.focusNode?.parentElement;
2083
- if (!useButton.contains(popupElement, anchorElement) && !useButton.contains(popupElement, focusElement)) {
713
+ const displacement = ScrollAreaViewport.getDisplacement(resolvedSwipeDirection, details.deltaX, details.deltaY);
714
+ if (displacement < MIN_SWIPE_START_DISTANCE && !openedBySwipeRef.current) {
2084
715
  return;
2085
716
  }
2086
- selection.removeAllRanges();
2087
- },
2088
- onSwipingChange(swiping) {
2089
- swipingRef.current = swiping;
2090
- setBackdropSwipingAttribute(store.context.backdropRef.current, swiping);
2091
- if (!swiping && !notifyParentSwipeProgressChange) {
2092
- finishNestedSwipe();
2093
- }
2094
- },
2095
- swipeThreshold({
2096
- element,
2097
- direction
2098
- }) {
2099
- return getBaseSwipeThreshold(element, direction);
2100
- },
2101
- canStart(position, details) {
2102
- const popupElement = store.context.popupRef.current;
2103
- if (!popupElement) {
2104
- return false;
2105
- }
2106
- const doc = popupElement.ownerDocument;
2107
- const elementAtPoint = getElementAtPoint(doc, position.x, position.y);
2108
- if (!elementAtPoint || !useButton.contains(popupElement, elementAtPoint)) {
2109
- return false;
2110
- }
2111
- const nativeEvent = details.nativeEvent;
2112
- const touchLike = 'touches' in nativeEvent || 'pointerType' in nativeEvent && nativeEvent.pointerType === 'touch';
2113
- if (touchLike && shouldIgnoreSwipeForTextSelection(doc, popupElement)) {
2114
- return false;
2115
- }
2116
- if (nativeEvent.type === 'touchstart' && isSwipeIgnoredTarget(elementAtPoint)) {
2117
- return false;
2118
- }
2119
- return true;
2120
- },
2121
- onProgress(progress, details) {
2122
- updateNestedSwipeActive(details);
2123
- const hasSnapPoints = Boolean(snapPoints && snapPoints.length > 0);
2124
- if (swipingRef.current && swipeDirection === 'down' && hasSnapPoints && details && Number.isFinite(details.deltaY)) {
2125
- const popupElement = store.context.popupRef.current;
2126
- if (popupElement) {
2127
- popupElement.style.removeProperty('transform');
2128
- popupElement.style.setProperty(ScrollAreaViewport.DrawerPopupCssVars.swipeMovementY, `${ScrollAreaViewport.getSnapPointSwipeMovement(activeSnapPointOffset ?? 0, details.deltaY)}px`);
2129
- }
2130
- }
2131
- const currentDirection = details?.direction ?? swipe.swipeDirection;
2132
- const isDismissSwipe = currentDirection === undefined || currentDirection === swipeDirection;
2133
- const isVerticalSwipe = swipeDirection === 'down' || swipeDirection === 'up';
2134
- const shouldTrackProgress = hasSnapPoints && isVerticalSwipe || !hasSnapPoints || swipeDirection === 'left' || swipeDirection === 'right' || isDismissSwipe;
2135
- let resolvedProgress = progress;
2136
- if (snapPointRange && popupHeight > 0) {
2137
- if (details && Number.isFinite(details.deltaY)) {
2138
- const baseOffset = activeSnapPointOffset ?? snapPointRange.minOffset;
2139
- const nextOffset = clamp.clamp(baseOffset + details.deltaY, 0, popupHeight);
2140
- resolvedProgress = clamp.clamp((nextOffset - snapPointRange.minOffset) / snapPointRange.range, 0, 1);
2141
- } else if (snapPointProgress !== null) {
2142
- resolvedProgress = snapPointProgress;
2143
- } else if (currentDirection === 'down' || currentDirection === 'up') {
2144
- const displacement = progress * popupHeight;
2145
- const baseOffset = activeSnapPointOffset ?? snapPointRange.minOffset;
2146
- const nextOffset = currentDirection === 'down' ? baseOffset + displacement : baseOffset - displacement;
2147
- resolvedProgress = clamp.clamp((nextOffset - snapPointRange.minOffset) / snapPointRange.range, 0, 1);
2148
- }
717
+ if (!openedBySwipeRef.current) {
718
+ openDrawer(swipeStartEventRef.current);
2149
719
  }
2150
- applySwipeProgress({
2151
- resolvedProgress,
2152
- shouldTrackProgress,
2153
- notifyParent: true
2154
- });
720
+ applySwipeMovement();
2155
721
  },
2156
722
  onRelease({
2157
723
  event,
724
+ direction,
2158
725
  deltaX,
2159
726
  deltaY,
2160
- direction,
2161
- velocityX,
2162
- velocityY,
2163
727
  releaseVelocityX,
2164
728
  releaseVelocityY
2165
729
  }) {
2166
- const swipeReleasePayload = {
2167
- deltaX,
2168
- deltaY,
2169
- velocityX,
2170
- velocityY,
2171
- releaseVelocityX,
2172
- releaseVelocityY
2173
- };
2174
- function startSwipeRelease(resolvedDirection) {
2175
- // Start ending transition styles earlier and synchronously to prevent a period where
2176
- // the popup appears stuck on release before the actual closing animation starts.
2177
- const popupElement = store.context.popupRef.current;
2178
- if (!popupElement) {
2179
- return;
2180
- }
2181
- finishNestedSwipe();
2182
- setSwipeDismissed(true);
2183
- popupElement.style.removeProperty('transition');
2184
- popupElement.setAttribute(useButton.TransitionStatusDataAttributes.endingStyle, '');
2185
- ReactDOM__namespace.flushSync(() => {
2186
- setSwipeRelease(resolveSwipeRelease({
2187
- direction: resolvedDirection,
2188
- ...swipeReleasePayload
2189
- }));
2190
- });
2191
- }
2192
- if (!snapPoints || snapPoints.length === 0) {
2193
- if (!direction) {
2194
- clearSwipeRelease();
2195
- return undefined;
2196
- }
2197
- const element = store.context.popupRef.current;
2198
- if (!element) {
2199
- clearSwipeRelease();
2200
- return undefined;
2201
- }
2202
- const baseThreshold = getBaseSwipeThreshold(element, direction);
2203
- const delta = direction === 'left' || direction === 'right' ? deltaX : deltaY;
2204
- if (!Number.isFinite(delta)) {
2205
- clearSwipeRelease();
2206
- return undefined;
2207
- }
2208
- const directionalDelta = direction === 'left' || direction === 'up' ? -delta : delta;
2209
- if (directionalDelta <= 0) {
2210
- clearSwipeRelease();
2211
- return false;
2212
- }
2213
- const velocity = direction === 'left' || direction === 'right' ? velocityX : velocityY;
2214
- const directionalVelocity = direction === 'left' || direction === 'up' ? -velocity : velocity;
2215
- if (directionalVelocity >= FAST_SWIPE_VELOCITY && directionalDelta > 0) {
2216
- startSwipeRelease(direction);
2217
- return true;
2218
- }
2219
- const shouldClose = directionalDelta > baseThreshold;
2220
- if (shouldClose) {
2221
- startSwipeRelease(direction);
2222
- } else {
2223
- clearSwipeRelease();
2224
- }
2225
- return shouldClose;
2226
- }
2227
- if (swipeDirection !== 'down' && swipeDirection !== 'up') {
2228
- clearSwipeRelease();
2229
- return undefined;
2230
- }
2231
- if (!popupHeight || resolvedSnapPoints.length === 0) {
2232
- clearSwipeRelease();
2233
- return undefined;
2234
- }
2235
- const dragDelta = swipeDirection === 'down' ? deltaY : -deltaY;
2236
- if (!Number.isFinite(dragDelta)) {
2237
- clearSwipeRelease();
2238
- return undefined;
2239
- }
2240
- const dragDirection = Math.sign(dragDelta);
2241
- const releaseDirectionalVelocity = swipeDirection === 'down' ? releaseVelocityY : -releaseVelocityY;
2242
- const fallbackDirectionalVelocity = swipeDirection === 'down' ? velocityY : -velocityY;
2243
- let resolvedDirectionalVelocity = Number.isFinite(releaseDirectionalVelocity) ? releaseDirectionalVelocity : fallbackDirectionalVelocity;
2244
- if (dragDirection !== 0 && Math.abs(dragDelta) >= MIN_SWIPE_THRESHOLD && Number.isFinite(resolvedDirectionalVelocity)) {
2245
- const velocityDirection = Math.sign(resolvedDirectionalVelocity);
2246
- if (velocityDirection !== 0 && velocityDirection !== dragDirection) {
2247
- // Ignore touch reversals that would otherwise flip the snap decision.
2248
- resolvedDirectionalVelocity = fallbackDirectionalVelocity;
2249
- }
2250
- }
2251
- const currentOffset = activeSnapPointOffset ?? 0;
2252
- const dragTargetOffset = clamp.clamp(currentOffset + dragDelta, 0, popupHeight);
2253
- const velocityOffset = Number.isFinite(resolvedDirectionalVelocity) && Math.abs(resolvedDirectionalVelocity) >= SNAP_VELOCITY_THRESHOLD ? clamp.clamp(resolvedDirectionalVelocity, -MAX_SNAP_VELOCITY, MAX_SNAP_VELOCITY) * SNAP_VELOCITY_MULTIPLIER : 0;
2254
- const targetOffset = snapToSequentialPoints ? dragTargetOffset : clamp.clamp(dragTargetOffset + velocityOffset, 0, popupHeight);
2255
- const snapPointEventDetails = useButton.createChangeEventDetails(useButton.swipe, event);
2256
- const closeFromSnapPoints = () => {
2257
- pendingSwipeCloseSnapPointRef.current = activeSnapPoint;
2258
- setActiveSnapPoint?.(null, snapPointEventDetails);
2259
- startSwipeRelease(swipeDirection);
2260
- return true;
2261
- };
2262
- if (snapToSequentialPoints) {
2263
- const orderedSnapPoints = [...resolvedSnapPoints].sort((first, second) => first.offset - second.offset);
2264
- if (orderedSnapPoints.length === 0) {
2265
- clearSwipeRelease();
2266
- return false;
2267
- }
2268
- let currentIndex = 0;
2269
- let closestDistance = Math.abs(currentOffset - orderedSnapPoints[0].offset);
2270
- for (let index = 1; index < orderedSnapPoints.length; index += 1) {
2271
- const distance = Math.abs(currentOffset - orderedSnapPoints[index].offset);
2272
- if (distance < closestDistance) {
2273
- closestDistance = distance;
2274
- currentIndex = index;
2275
- }
2276
- }
2277
- let targetSnapPoint = orderedSnapPoints[0];
2278
- closestDistance = Math.abs(targetOffset - targetSnapPoint.offset);
2279
- for (const snapPoint of orderedSnapPoints) {
2280
- const distance = Math.abs(targetOffset - snapPoint.offset);
2281
- if (distance < closestDistance) {
2282
- closestDistance = distance;
2283
- targetSnapPoint = snapPoint;
2284
- }
2285
- }
2286
- const velocityDirection = Math.sign(resolvedDirectionalVelocity);
2287
- const shouldAdvance = dragDirection !== 0 && velocityDirection !== 0 && velocityDirection === dragDirection && Math.abs(resolvedDirectionalVelocity) >= SNAP_VELOCITY_THRESHOLD;
2288
- let effectiveTargetOffset = targetOffset;
2289
- if (shouldAdvance) {
2290
- const adjacentIndex = clamp.clamp(currentIndex + dragDirection, 0, orderedSnapPoints.length - 1);
2291
- if (adjacentIndex !== currentIndex) {
2292
- const adjacentPoint = orderedSnapPoints[adjacentIndex];
2293
- const shouldForceAdjacent = dragDirection > 0 ? targetOffset < adjacentPoint.offset : targetOffset > adjacentPoint.offset;
2294
- if (shouldForceAdjacent) {
2295
- targetSnapPoint = adjacentPoint;
2296
- effectiveTargetOffset = adjacentPoint.offset;
2297
- }
2298
- } else if (dragDirection > 0) {
2299
- return closeFromSnapPoints();
2300
- }
2301
- }
2302
- const closeOffset = popupHeight;
2303
- const closeDistance = Math.abs(effectiveTargetOffset - closeOffset);
2304
- const snapDistance = Math.abs(effectiveTargetOffset - targetSnapPoint.offset);
2305
- if (closeDistance < snapDistance) {
2306
- return closeFromSnapPoints();
2307
- }
2308
- setActiveSnapPoint?.(targetSnapPoint.value, snapPointEventDetails);
2309
- clearSwipeRelease();
2310
- return false;
2311
- }
2312
- if (resolvedDirectionalVelocity >= FAST_SWIPE_VELOCITY && dragDelta > 0) {
2313
- return closeFromSnapPoints();
2314
- }
2315
- let closestSnapPoint = resolvedSnapPoints[0];
2316
- let closestDistance = Math.abs(targetOffset - closestSnapPoint.offset);
2317
- for (const snapPoint of resolvedSnapPoints) {
2318
- const distance = Math.abs(targetOffset - snapPoint.offset);
2319
- if (distance < closestDistance) {
2320
- closestDistance = distance;
2321
- closestSnapPoint = snapPoint;
730
+ const displacement = ScrollAreaViewport.getDisplacement(resolvedSwipeDirection, deltaX, deltaY);
731
+ const releaseVelocity = ScrollAreaViewport.getDisplacement(resolvedSwipeDirection, releaseVelocityX, releaseVelocityY);
732
+ const threshold = resolveSwipeOpenThreshold();
733
+ const hasEnoughDistance = threshold != null && displacement >= threshold;
734
+ const hasEnoughVelocity = releaseVelocity >= VELOCITY_THRESHOLD;
735
+ const shouldOpen = threshold != null && direction === resolvedSwipeDirection && (hasEnoughDistance || hasEnoughVelocity) && !disabled;
736
+ if (shouldOpen) {
737
+ if (!store.select('open')) {
738
+ openDrawer(event);
2322
739
  }
740
+ } else if (openedBySwipeRef.current) {
741
+ closeDrawer(event);
2323
742
  }
2324
- const closeOffset = popupHeight;
2325
- const closeDistance = Math.abs(targetOffset - closeOffset);
2326
- if (closeDistance < closestDistance) {
2327
- return closeFromSnapPoints();
2328
- }
2329
- setActiveSnapPoint?.(closestSnapPoint.value, snapPointEventDetails);
2330
- clearSwipeRelease();
743
+ finishSwipeInteraction();
2331
744
  return false;
2332
745
  },
2333
- onDismiss(event) {
2334
- visualStateStore?.set({
2335
- swipeProgress: 0,
2336
- frontmostHeight: 0
2337
- });
2338
- const backdropElement = store.context.backdropRef.current;
2339
- if (backdropElement) {
2340
- backdropElement.style.setProperty(ScrollAreaViewport.DrawerBackdropCssVars.swipeProgress, '0');
2341
- backdropElement.style.removeProperty(ScrollAreaViewport.DrawerPopupCssVars.height);
2342
- }
2343
- const dismissEventDetails = useButton.createChangeEventDetails(useButton.swipe, event);
2344
- store.setOpen(false, dismissEventDetails);
2345
- if (dismissEventDetails.isCanceled) {
2346
- const pendingSnapPoint = pendingSwipeCloseSnapPointRef.current;
2347
- if (pendingSnapPoint !== undefined) {
2348
- setActiveSnapPoint?.(pendingSnapPoint, useButton.createChangeEventDetails(useButton.swipe, event));
2349
- }
2350
- pendingSwipeCloseSnapPointRef.current = undefined;
2351
- resetSwipeRef.current?.();
2352
- clearSwipeRelease();
2353
- return;
2354
- }
2355
-
2356
- // In controlled mode, the effective open state may not have changed yet
2357
- // (openProp takes precedence over state.open). Proceed optimistically with the
2358
- // dismiss animation — React's Scheduler flushes before the next rAF, so we can
2359
- // reliably check whether the parent accepted or rejected the close.
2360
- // Note: if onOpenChange is asynchronous (e.g., closes the drawer after a network
2361
- // call), the rAF check will see open === true, revert the animation, and the
2362
- // drawer will close without animation when the parent eventually sets open={false}.
2363
- if (store.select('open')) {
2364
- const savedEvent = event;
2365
- controlledDismissFrame.request(() => {
2366
- if (store.select('open')) {
2367
- // Parent rejected: revert animation and restore snap point.
2368
- const pendingSnapPoint = pendingSwipeCloseSnapPointRef.current;
2369
- if (pendingSnapPoint !== undefined) {
2370
- setActiveSnapPoint?.(pendingSnapPoint, useButton.createChangeEventDetails(useButton.swipe, savedEvent));
2371
- }
2372
- pendingSwipeCloseSnapPointRef.current = undefined;
2373
- clearSwipeRelease();
2374
- resetSwipeRef.current?.();
2375
- } else {
2376
- // Parent accepted: clean up the ref.
2377
- pendingSwipeCloseSnapPointRef.current = undefined;
2378
- }
2379
- });
2380
- return;
2381
- }
2382
- pendingSwipeCloseSnapPointRef.current = undefined;
2383
- setSwipeDismissed(true);
2384
- }
746
+ onCancel: finishSwipeInteraction
2385
747
  });
2386
748
  const swipePointerProps = swipe.getPointerProps();
2387
749
  const swipeTouchProps = swipe.getTouchProps();
2388
- const {
2389
- moveNative: moveSwipeNative,
2390
- reset: resetSwipe
2391
- } = swipe;
2392
- resetSwipeRef.current = resetSwipe;
2393
- React__namespace.useEffect(() => {
2394
- const rootElement = viewportElement ?? popupElementState;
2395
- if (!rootElement) {
2396
- return undefined;
2397
- }
2398
- const resolvedRootElement = rootElement;
2399
- const doc = useButton.ownerDocument(resolvedRootElement);
2400
- const win = floatingUi_utils_dom.getWindow(doc);
2401
- function handleNativeTouchMove(event) {
2402
- // The virtual keyboard provider observes the move to tell a tap apart from a drag.
2403
- // It must run even when the swipe gesture below claims the event with
2404
- // `stopPropagation()`, which would otherwise prevent React's delegated handlers
2405
- // (and the provider) from ever seeing the move.
2406
- virtualKeyboard?.onTouchMove(event);
2407
- if (ignoreTouchSwipeRef.current) {
2408
- return;
2409
- }
2410
- const touchState = touchScrollStateRef.current;
2411
- const touch = event.touches[0];
2412
- if (!touch || !touchState) {
2413
- return;
2414
- }
2415
- const drawerAxisDelta = isVerticalScrollAxis ? touch.clientY - touchState.lastY : touch.clientX - touchState.lastX;
2416
-
2417
- // Preserve native range interaction by never locking touchmove for range inputs.
2418
- if (isEventOnRangeInput(event, win)) {
2419
- touchState.allowSwipe = false;
2420
- updateTouchScrollPosition(touchState, touch);
2421
- return;
2422
- }
2423
-
2424
- // Avoid blocking pinch zoom or text selection adjustments on iOS Safari.
2425
- if (event.touches.length === 2) {
2426
- updateTouchScrollPosition(touchState, touch);
2427
- return;
2428
- }
2429
- const allowTouchMove = shouldIgnoreSwipeForTextSelection(doc, resolvedRootElement);
2430
- if (allowTouchMove || !open || !mounted || nestedDrawerOpen) {
2431
- updateTouchScrollPosition(touchState, touch);
2432
- return;
2433
- }
2434
- if (preserveNativeCrossAxisScrollOnMove(touchState, touch, isVerticalScrollAxis)) {
2435
- updateTouchScrollPosition(touchState, touch);
2436
- return;
2437
- }
2438
- const scrollTarget = touchState.scrollTarget;
2439
- if (!scrollTarget || scrollTarget === doc.documentElement || scrollTarget === doc.body) {
2440
- if (event.cancelable) {
2441
- event.preventDefault();
2442
- }
2443
- // Claim the gesture before React's delegated touch handlers see it; dispatching the
2444
- // move through React re-rasterizes the popup content on every frame.
2445
- event.stopPropagation();
2446
- moveSwipeNative(event, resolvedRootElement);
2447
- updateTouchScrollPosition(touchState, touch);
2448
- return;
2449
- }
2450
- const hasScrollableContent = hasScrollableContentOnAxis(scrollTarget, scrollAxis);
2451
- if (!hasScrollableContent) {
2452
- // If the scroll container doesn't overflow on the drawer axis, prevent the window from
2453
- // scrolling instead.
2454
- if (event.cancelable) {
2455
- event.preventDefault();
2456
- }
2457
- event.stopPropagation();
2458
- updateTouchScrollPosition(touchState, touch);
2459
- return;
2460
- }
2461
- const delta = drawerAxisDelta;
2462
- if (delta !== 0) {
2463
- const canSwipeFromScrollEdge = canSwipeFromScrollEdgeOnMove(scrollTarget, scrollAxis, swipeDirection, delta);
2464
- if (!touchState.allowSwipe) {
2465
- if (!event.cancelable) {
2466
- touchState.allowSwipe = false;
2467
- } else if (canSwipeFromScrollEdge) {
2468
- touchState.allowSwipe = true;
2469
- event.preventDefault();
2470
- } else {
2471
- touchState.allowSwipe = false;
2472
- }
2473
- } else if (event.cancelable) {
2474
- event.preventDefault();
2475
- }
2476
- }
2477
- if (touchState.allowSwipe === true) {
2478
- event.stopPropagation();
2479
- moveSwipeNative(event, resolvedRootElement);
2480
- }
2481
- updateTouchScrollPosition(touchState, touch);
2482
- }
2483
- return useButton.addEventListener(doc, 'touchmove', handleNativeTouchMove, {
2484
- passive: false,
2485
- capture: true
2486
- });
2487
- }, [mounted, nestedDrawerOpen, open, popupElementState, isVerticalScrollAxis, scrollAxis, swipeDirection, moveSwipeNative, viewportElement, virtualKeyboard]);
2488
- React__namespace.useEffect(() => {
2489
- if (!snapPointRange || swipe.swiping) {
2490
- return;
2491
- }
2492
- const resolvedProgress = !open || nested ? 0 : snapPointProgress ?? 0;
2493
- applySwipeProgress({
2494
- resolvedProgress,
2495
- shouldTrackProgress: true,
2496
- notifyParent: false
2497
- });
2498
- }, [applySwipeProgress, frontmostHeight, nested, notifyParentSwipeProgressChange, open, snapPointProgress, snapPointRange, swipe.swiping, store, visualStateStore]);
2499
- React__namespace.useEffect(() => {
2500
- if (!notifyParentSwipeProgressChange) {
2501
- return undefined;
2502
- }
2503
- if (!open) {
2504
- notifyParentSwipeProgressChange(0);
2505
- }
2506
- return () => {
2507
- notifyParentSwipeProgressChange(0);
2508
- };
2509
- }, [notifyParentSwipeProgressChange, open]);
750
+ const resetSwipe = swipe.reset;
2510
751
  React__namespace.useEffect(() => {
2511
- if (open) {
752
+ if (!enabled) {
2512
753
  resetSwipe();
2513
- clearSwipeRelease();
754
+ resetDragDelta();
755
+ clearSwipeStyles();
756
+ resetSwipeInteractionState();
2514
757
  }
2515
- }, [clearSwipeRelease, open, resetSwipe]);
758
+ }, [clearSwipeStyles, enabled, resetDragDelta, resetSwipe]);
2516
759
  React__namespace.useEffect(() => {
2517
- const backdropElement = backdropRef.current;
2518
760
  return () => {
2519
- visualStateStore?.set({
2520
- swipeProgress: 0,
2521
- frontmostHeight: 0
2522
- });
2523
- setBackdropSwipingAttribute(backdropElement, false);
2524
- // `data-swiping` is set on whichever backdrop is current when a swipe starts, which can
2525
- // differ from the captured element if the backdrop mounted late or changed identity.
2526
- // Reading the live ref here is intentional so the current backdrop is cleared too.
2527
- // eslint-disable-next-line react-hooks/exhaustive-deps
2528
- const currentBackdrop = backdropRef.current;
2529
- if (currentBackdrop !== backdropElement) {
2530
- setBackdropSwipingAttribute(currentBackdrop, false);
2531
- }
2532
- finishNestedSwipe();
761
+ store.context.outsidePressEnabledRef.current = true;
2533
762
  };
2534
- }, [backdropRef, finishNestedSwipe, visualStateStore]);
2535
- const swipeProviderValue = React__namespace.useMemo(() => ({
763
+ }, [store]);
764
+ const state = {
765
+ open,
2536
766
  swiping: swipe.swiping,
2537
- getDragStyles: swipe.getDragStyles,
2538
- swipeStrength: swipeRelease ?? null,
2539
- setSwipeDismissed
2540
- }), [setSwipeDismissed, swipe.getDragStyles, swipe.swiping, swipeRelease]);
2541
- function resetTouchTrackingState() {
2542
- ignoreTouchSwipeRef.current = false;
2543
- touchScrollStateRef.current = null;
2544
- lastPointerTypeRef.current = '';
2545
- ignoreNextTouchStartFromPenRef.current = false;
2546
- }
2547
- return /*#__PURE__*/jsxRuntime.jsx(DrawerDescription.DialogViewport, {
2548
- ref: forwardedRef,
2549
- className: className,
2550
- style: style,
2551
- render: render,
2552
- ...useRenderElement.mergeProps(elementProps, {
767
+ swipeDirection: resolvedSwipeDirection,
768
+ disabled
769
+ };
770
+ return useRenderElement.useRenderElement('div', componentProps, {
771
+ state,
772
+ ref: [forwardedRef, swipeAreaRef, registerTrigger],
773
+ stateAttributesMapping: stateAttributesMapping$3,
774
+ props: [{
775
+ role: 'presentation',
776
+ 'aria-hidden': true,
777
+ style: {
778
+ pointerEvents: !enabled ? 'none' : undefined,
779
+ touchAction: resolveTouchAction(resolvedSwipeDirection)
780
+ },
2553
781
  onPointerDown(event) {
2554
- lastPointerTypeRef.current = event.pointerType;
2555
- ignoreNextTouchStartFromPenRef.current = event.pointerType === 'pen';
2556
- if (!open || !mounted || nestedDrawerOpen) {
2557
- return;
2558
- }
2559
- const doc = useButton.ownerDocument(event.currentTarget);
2560
- const elementAtPoint = getElementAtPoint(doc, event.clientX, event.clientY);
2561
- if (isSwipeIgnoredTarget(elementAtPoint) || isDrawerContentTarget(elementAtPoint)) {
2562
- return;
2563
- }
2564
782
  if (event.pointerType === 'touch') {
2565
783
  return;
2566
784
  }
2567
785
  swipePointerProps.onPointerDown?.(event);
786
+
787
+ // Prevent native text selection/drag gestures from competing with swipe-open dragging.
788
+ if (event.cancelable) {
789
+ event.preventDefault();
790
+ }
2568
791
  },
2569
792
  onPointerMove(event) {
2570
793
  if (event.pointerType === 'touch') {
@@ -2573,258 +796,23 @@ const DrawerViewport = /*#__PURE__*/React__namespace.forwardRef(function DrawerV
2573
796
  swipePointerProps.onPointerMove?.(event);
2574
797
  },
2575
798
  onPointerUp(event) {
2576
- if (lastPointerTypeRef.current === event.pointerType) {
2577
- lastPointerTypeRef.current = '';
2578
- }
2579
799
  if (event.pointerType === 'touch') {
2580
800
  return;
2581
801
  }
2582
802
  swipePointerProps.onPointerUp?.(event);
2583
803
  },
2584
804
  onPointerCancel(event) {
2585
- if (lastPointerTypeRef.current === event.pointerType) {
2586
- lastPointerTypeRef.current = '';
2587
- }
2588
805
  if (event.pointerType === 'touch') {
2589
806
  return;
2590
807
  }
2591
808
  swipePointerProps.onPointerCancel?.(event);
2592
- },
2593
- onTouchStart(event) {
2594
- const startedFromPenPointerDown = lastPointerTypeRef.current === 'pen' && ignoreNextTouchStartFromPenRef.current;
2595
- if (startedFromPenPointerDown) {
2596
- ignoreNextTouchStartFromPenRef.current = false;
2597
- ignoreTouchSwipeRef.current = false;
2598
- touchScrollStateRef.current = null;
2599
- return;
2600
- }
2601
- if (!open || !mounted || nestedDrawerOpen) {
2602
- ignoreTouchSwipeRef.current = false;
2603
- touchScrollStateRef.current = null;
2604
- return;
2605
- }
2606
- const touch = event.touches[0];
2607
- if (!touch) {
2608
- return;
2609
- }
2610
- if (isReactTouchEventOnRangeInput(event)) {
2611
- ignoreTouchSwipeRef.current = false;
2612
- touchScrollStateRef.current = null;
2613
- return;
2614
- }
2615
- const doc = useButton.ownerDocument(event.currentTarget);
2616
- const elementAtPoint = getElementAtPoint(doc, touch.clientX, touch.clientY);
2617
- const rootElement = viewportElement ?? popupElementState;
2618
- const eventTarget = useButton.getTarget(event.nativeEvent);
2619
- const target = floatingUi_utils_dom.isElement(eventTarget) ? eventTarget : null;
2620
- if (rootElement && target && !useButton.contains(rootElement, target)) {
2621
- ignoreTouchSwipeRef.current = true;
2622
- touchScrollStateRef.current = null;
2623
- return;
2624
- }
2625
- virtualKeyboard?.onTouchStart(event);
2626
- ignoreTouchSwipeRef.current = isSwipeIgnoredTarget(elementAtPoint);
2627
- if (ignoreTouchSwipeRef.current) {
2628
- touchScrollStateRef.current = null;
2629
- return;
2630
- }
2631
- let scrollTarget = null;
2632
- let hasCrossAxisScrollableContent = false;
2633
- if (rootElement && target) {
2634
- scrollTarget = findScrollableTouchTarget(target, rootElement, scrollAxis);
2635
- hasCrossAxisScrollableContent = findScrollableTouchTarget(target, rootElement, crossScrollAxis) != null;
2636
- }
2637
- let allowSwipe = null;
2638
- if (scrollTarget) {
2639
- const canSwipeFromEdge = isAtSwipeStartEdge(scrollTarget, scrollAxis, swipeDirection);
2640
- allowSwipe = canSwipeFromEdge ? null : false;
2641
- }
2642
- touchScrollStateRef.current = {
2643
- startX: touch.clientX,
2644
- startY: touch.clientY,
2645
- lastX: touch.clientX,
2646
- lastY: touch.clientY,
2647
- scrollTarget,
2648
- hasCrossAxisScrollableContent,
2649
- allowSwipe,
2650
- preserveNativeCrossAxisScroll: false
2651
- };
2652
- swipeTouchProps.onTouchStart?.(event);
2653
- },
2654
- onTouchEnd(event) {
2655
- virtualKeyboard?.onTouchEnd(event);
2656
- resetTouchTrackingState();
2657
- swipeTouchProps.onTouchEnd?.(event);
2658
- },
2659
- onTouchCancel(event) {
2660
- virtualKeyboard?.onTouchCancel();
2661
- resetTouchTrackingState();
2662
- swipeTouchProps.onTouchCancel?.(event);
2663
- },
2664
- // Drawer popups use drawer-specific nested state attributes.
2665
- // Suppress DialogViewport's generic nested dialog attribute.
2666
- ['data-nested-dialog-open']: undefined
2667
- }),
2668
- children: /*#__PURE__*/jsxRuntime.jsx(ScrollAreaViewport.DrawerViewportContext.Provider, {
2669
- value: swipeProviderValue,
2670
- children: children
2671
- })
809
+ }
810
+ }, swipeTouchProps, swipeAreaId ? {
811
+ id: swipeAreaId
812
+ } : undefined, elementProps]
2672
813
  });
2673
814
  });
2674
- if (process.env.NODE_ENV !== "production") DrawerViewport.displayName = "DrawerViewport";
2675
- function setSwipeDismissedElements(popupElement, backdropElement, dismissed) {
2676
- if (dismissed) {
2677
- popupElement?.setAttribute(ScrollAreaViewport.DrawerPopupDataAttributes.swipeDismiss, '');
2678
- backdropElement?.setAttribute(ScrollAreaViewport.DrawerPopupDataAttributes.swipeDismiss, '');
2679
- return;
2680
- }
2681
- popupElement?.removeAttribute(ScrollAreaViewport.DrawerPopupDataAttributes.swipeDismiss);
2682
- backdropElement?.removeAttribute(ScrollAreaViewport.DrawerPopupDataAttributes.swipeDismiss);
2683
- }
2684
- function setBackdropSwipingAttribute(backdropElement, swiping) {
2685
- if (!backdropElement) {
2686
- return;
2687
- }
2688
- if (swiping) {
2689
- backdropElement.setAttribute(ScrollAreaViewport.DrawerPopupDataAttributes.swiping, '');
2690
- return;
2691
- }
2692
- backdropElement.removeAttribute(ScrollAreaViewport.DrawerPopupDataAttributes.swiping);
2693
- }
2694
- function isSwipeIgnoredTarget(target) {
2695
- return Boolean(target?.closest(useScrollLock.BASE_UI_SWIPE_IGNORE_SELECTOR));
2696
- }
2697
- function isDrawerContentTarget(target) {
2698
- return Boolean(target?.closest(DRAWER_CONTENT_SELECTOR));
2699
- }
2700
- function getBaseSwipeThreshold(element, direction) {
2701
- const size = direction === 'left' || direction === 'right' ? element.offsetWidth : element.offsetHeight;
2702
- return Math.max(size * 0.5, MIN_SWIPE_THRESHOLD);
2703
- }
2704
- function isRangeInput(target, win) {
2705
- return target instanceof win.HTMLInputElement && target.type === 'range';
2706
- }
2707
- function isTextSelectionControl(target) {
2708
- if (!floatingUi_utils_dom.isElement(target)) {
2709
- return false;
2710
- }
2711
- return target.tagName === 'INPUT' || target.tagName === 'TEXTAREA';
2712
- }
2713
- function hasExpandedSelectionWithinTarget(selection, target) {
2714
- const anchorElement = floatingUi_utils_dom.isElement(selection.anchorNode) ? selection.anchorNode : selection.anchorNode?.parentElement;
2715
- const focusElement = floatingUi_utils_dom.isElement(selection.focusNode) ? selection.focusNode : selection.focusNode?.parentElement;
2716
- return selection.containsNode(target, true) || useButton.contains(target, anchorElement) || useButton.contains(target, focusElement);
2717
- }
2718
- function shouldIgnoreSwipeForTextSelection(doc, rootElement) {
2719
- const activeEl = useButton.activeElement(doc);
2720
- const activeElementWithinRoot = Boolean(activeEl && useButton.contains(rootElement, activeEl));
2721
- if (activeElementWithinRoot && isTextSelectionControl(activeEl)) {
2722
- const {
2723
- selectionStart,
2724
- selectionEnd
2725
- } = activeEl;
2726
- if (selectionStart != null && selectionEnd != null && selectionStart < selectionEnd) {
2727
- return true;
2728
- }
2729
- }
2730
- const selection = doc.getSelection?.();
2731
- if (!selection || selection.isCollapsed) {
2732
- return false;
2733
- }
2734
- return hasExpandedSelectionWithinTarget(selection, rootElement);
2735
- }
2736
- function isEventOnRangeInput(event, win) {
2737
- const composedPath = event.composedPath();
2738
- if (composedPath) {
2739
- return composedPath.some(pathTarget => isRangeInput(pathTarget, win));
2740
- }
2741
- return isRangeInput(useButton.getTarget(event), win);
2742
- }
2743
- function isReactTouchEventOnRangeInput(event) {
2744
- return isEventOnRangeInput(event.nativeEvent, floatingUi_utils_dom.getWindow(event.currentTarget));
2745
- }
2746
- function updateTouchScrollPosition(touchState, touch) {
2747
- touchState.lastX = touch.clientX;
2748
- touchState.lastY = touch.clientY;
2749
- }
2750
- function preserveNativeCrossAxisScrollOnMove(touchState, touch, isVerticalScrollAxis) {
2751
- if (touchState.preserveNativeCrossAxisScroll) {
2752
- return true;
2753
- }
2754
- if (touchState.allowSwipe === true || !touchState.hasCrossAxisScrollableContent) {
2755
- return false;
2756
- }
2757
- const drawerAxisGestureDelta = isVerticalScrollAxis ? touch.clientY - touchState.startY : touch.clientX - touchState.startX;
2758
- const crossAxisGestureDelta = isVerticalScrollAxis ? touch.clientX - touchState.startX : touch.clientY - touchState.startY;
2759
- const absDrawerAxisGestureDelta = Math.abs(drawerAxisGestureDelta);
2760
- const absCrossAxisGestureDelta = Math.abs(crossAxisGestureDelta);
2761
- if (absCrossAxisGestureDelta < 6 || absCrossAxisGestureDelta <= absDrawerAxisGestureDelta + 2) {
2762
- return false;
2763
- }
2764
- touchState.preserveNativeCrossAxisScroll = true;
2765
- return true;
2766
- }
2767
- function hasScrollableContentOnAxis(scrollTarget, axis) {
2768
- return axis === 'vertical' ? scrollTarget.scrollHeight > scrollTarget.clientHeight : scrollTarget.scrollWidth > scrollTarget.clientWidth;
2769
- }
2770
- function getScrollMetrics(scrollTarget, axis) {
2771
- if (axis === 'vertical') {
2772
- const max = Math.max(0, scrollTarget.scrollHeight - scrollTarget.clientHeight);
2773
- return {
2774
- offset: scrollTarget.scrollTop,
2775
- max
2776
- };
2777
- }
2778
- const max = Math.max(0, scrollTarget.scrollWidth - scrollTarget.clientWidth);
2779
- return {
2780
- offset: scrollTarget.scrollLeft,
2781
- max
2782
- };
2783
- }
2784
- function isAtSwipeStartEdge(scrollTarget, axis, direction) {
2785
- const {
2786
- offset,
2787
- max
2788
- } = getScrollMetrics(scrollTarget, axis);
2789
- const dismissFromStartEdge = shouldDismissFromStartEdge(direction, axis);
2790
- if (dismissFromStartEdge === null) {
2791
- return false;
2792
- }
2793
- return dismissFromStartEdge ? offset <= 0 : offset >= max;
2794
- }
2795
- function canSwipeFromScrollEdgeOnMove(scrollTarget, axis, direction, delta) {
2796
- const {
2797
- offset,
2798
- max
2799
- } = getScrollMetrics(scrollTarget, axis);
2800
- const dismissFromStartEdge = shouldDismissFromStartEdge(direction, axis);
2801
- if (dismissFromStartEdge === null) {
2802
- return false;
2803
- }
2804
- const movingTowardDismiss = dismissFromStartEdge ? delta > 0 : delta < 0;
2805
- if (!movingTowardDismiss) {
2806
- return false;
2807
- }
2808
- return dismissFromStartEdge ? offset <= 0 : offset >= max;
2809
- }
2810
- function shouldDismissFromStartEdge(direction, axis) {
2811
- if (axis === 'vertical') {
2812
- if (direction === 'down') {
2813
- return true;
2814
- }
2815
- if (direction === 'up') {
2816
- return false;
2817
- }
2818
- return null;
2819
- }
2820
- if (direction === 'right') {
2821
- return true;
2822
- }
2823
- if (direction === 'left') {
2824
- return false;
2825
- }
2826
- return null;
2827
- }
815
+ if (process.env.NODE_ENV !== "production") DrawerSwipeArea.displayName = "DrawerSwipeArea";
2828
816
 
2829
817
  let DrawerViewportCssVars = /*#__PURE__*/function (DrawerViewportCssVars) {
2830
818
  /**
@@ -3147,7 +1135,7 @@ function DrawerVirtualKeyboardProvider(props) {
3147
1135
  onTouchEnd,
3148
1136
  onTouchCancel: resetTouchTrackingState
3149
1137
  }), [onTouchEnd, onTouchMove, onTouchStart, resetTouchTrackingState]);
3150
- return /*#__PURE__*/jsxRuntime.jsx(DrawerVirtualKeyboardContext.Provider, {
1138
+ return /*#__PURE__*/jsxRuntime.jsx(ScrollAreaViewport.DrawerVirtualKeyboardContext.Provider, {
3151
1139
  value: contextValue,
3152
1140
  children: children
3153
1141
  });
@@ -3196,7 +1184,7 @@ function getContentEditableHost(element) {
3196
1184
  return host;
3197
1185
  }
3198
1186
  function resolveKeyboardTouchTargetFromPoint(doc, clientX, clientY) {
3199
- const exactTarget = getElementAtPoint(doc, clientX, clientY);
1187
+ const exactTarget = ScrollAreaViewport.getElementAtPoint(doc, clientX, clientY);
3200
1188
  const exactKeyboardTarget = resolveKeyboardInputTarget(exactTarget);
3201
1189
  if (exactKeyboardTarget) {
3202
1190
  return {
@@ -3215,7 +1203,7 @@ function resolveKeyboardTouchTargetFromPoint(doc, clientX, clientY) {
3215
1203
  return KEYBOARD_TAP_BLOCKED;
3216
1204
  }
3217
1205
  for (const [offsetX, offsetY] of [[0, INPUT_TAP_HIT_SLOP], [0, -INPUT_TAP_HIT_SLOP], [INPUT_TAP_HIT_SLOP, 0], [-INPUT_TAP_HIT_SLOP, 0]]) {
3218
- const keyboardTarget = resolveKeyboardInputTarget(getElementAtPoint(doc, clientX + offsetX, clientY + offsetY));
1206
+ const keyboardTarget = resolveKeyboardInputTarget(ScrollAreaViewport.getElementAtPoint(doc, clientX + offsetX, clientY + offsetY));
3219
1207
  if (keyboardTarget) {
3220
1208
  return {
3221
1209
  focusTarget: keyboardTarget,
@@ -3269,7 +1257,7 @@ function findKeyboardScrollTarget(target, root) {
3269
1257
  // that only becomes scrollable once keyboard slack is added (overflow intent without
3270
1258
  // current overflow).
3271
1259
  const scrollStart = floatingUi_utils_dom.getParentNode(target);
3272
- return findScrollableTouchTarget(scrollStart, root, 'vertical') ?? findScrollableTouchTarget(scrollStart, root, 'vertical', true);
1260
+ return ScrollAreaViewport.findScrollableTouchTarget(scrollStart, root, 'vertical') ?? ScrollAreaViewport.findScrollableTouchTarget(scrollStart, root, 'vertical', true);
3273
1261
  }
3274
1262
  function getKeyboardVisualViewport(win) {
3275
1263
  const visualViewport = win.visualViewport;
@@ -3310,7 +1298,7 @@ var index_parts$5 = /*#__PURE__*/Object.freeze({
3310
1298
  SwipeArea: DrawerSwipeArea,
3311
1299
  Title: DrawerTitle.DrawerTitle,
3312
1300
  Trigger: ScrollAreaViewport.DrawerTrigger,
3313
- Viewport: DrawerViewport,
1301
+ Viewport: ScrollAreaViewport.DrawerViewport,
3314
1302
  VirtualKeyboardProvider: DrawerVirtualKeyboardProvider,
3315
1303
  createHandle: DrawerDescription.createDialogHandle
3316
1304
  });