@jobber/components 8.21.1 → 8.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/Dialog.d.ts +1 -0
  2. package/Dialog.js +17 -0
  3. package/dist/BottomSheet-cjs.js +34 -7
  4. package/dist/BottomSheet-es.js +34 -8
  5. package/dist/CSPContext-cjs.js +69 -0
  6. package/dist/CSPContext-es.js +46 -0
  7. package/dist/Card/index.cjs +3 -0
  8. package/dist/Card/index.mjs +3 -0
  9. package/dist/ComboboxChipRemove-cjs.js +54 -211
  10. package/dist/ComboboxChipRemove-es.js +8 -164
  11. package/dist/DataDump/index.cjs +3 -0
  12. package/dist/DataDump/index.mjs +3 -0
  13. package/dist/Dialog/Dialog.d.ts +3 -0
  14. package/dist/Dialog/Dialog.types.d.ts +205 -0
  15. package/dist/Dialog/DialogBottomSheet.d.ts +28 -0
  16. package/dist/Dialog/DialogModal.d.ts +20 -0
  17. package/dist/Dialog/constants.d.ts +1 -0
  18. package/dist/Dialog/dialogComposableShared.d.ts +53 -0
  19. package/dist/Dialog/dialogReturnFocus.d.ts +43 -0
  20. package/dist/Dialog/index.cjs +33 -0
  21. package/dist/Dialog/index.d.ts +4 -0
  22. package/dist/Dialog/index.mjs +25 -0
  23. package/dist/DrawerDescription-cjs.js +410 -0
  24. package/dist/DrawerDescription-es.js +383 -0
  25. package/dist/DrawerTitle-cjs.js +116 -0
  26. package/dist/DrawerTitle-es.js +92 -0
  27. package/dist/InputNumberExperimental-cjs.js +24 -23
  28. package/dist/InputNumberExperimental-es.js +3 -2
  29. package/dist/InternalBackdrop-es.js +3 -3
  30. package/dist/Menu/index.cjs +3 -0
  31. package/dist/Menu/index.mjs +3 -0
  32. package/dist/Menu-cjs.js +6 -4
  33. package/dist/Menu-es.js +6 -4
  34. package/dist/MenuSubmenuTrigger-cjs.js +53 -103
  35. package/dist/MenuSubmenuTrigger-es.js +55 -103
  36. package/dist/NumberFieldInput-cjs.js +9 -9
  37. package/dist/NumberFieldInput-es.js +3 -3
  38. package/dist/Page/index.cjs +3 -0
  39. package/dist/Page/index.mjs +3 -0
  40. package/dist/ScrollAreaViewport-cjs.js +921 -960
  41. package/dist/ScrollAreaViewport-es.js +917 -958
  42. package/dist/SelectGroupLabel-cjs.js +1924 -0
  43. package/dist/SelectGroupLabel-es.js +1888 -0
  44. package/dist/SelectPrimitive-cjs.js +112 -0
  45. package/dist/SelectPrimitive-es.js +110 -0
  46. package/dist/ToolbarRootContext-cjs.js +35 -0
  47. package/dist/ToolbarRootContext-es.js +14 -0
  48. package/dist/dialogReturnFocus-cjs.js +427 -0
  49. package/dist/dialogReturnFocus-es.js +423 -0
  50. package/dist/docs/SelectPrimitive/SelectPrimitive.md +326 -0
  51. package/dist/docs/index.md +1 -0
  52. package/dist/floating-ui.react-dom-cjs.js +1 -0
  53. package/dist/floating-ui.react-dom-es.js +2 -2
  54. package/dist/floating-ui.utils.dom-es.js +1 -1
  55. package/dist/index.cjs +15 -7
  56. package/dist/index.d.mts +1 -0
  57. package/dist/index.d.ts +1 -0
  58. package/dist/index.mjs +12 -7
  59. package/dist/primitives/BottomSheet/BottomSheet.d.ts +19 -1
  60. package/dist/primitives/BottomSheet/BottomSheet.types.d.ts +8 -0
  61. package/dist/primitives/BottomSheet/index.cjs +2 -0
  62. package/dist/primitives/BottomSheet/index.d.ts +1 -1
  63. package/dist/primitives/BottomSheet/index.mjs +2 -1
  64. package/dist/primitives/ComboboxPrimitive/index.cjs +3 -1
  65. package/dist/primitives/ComboboxPrimitive/index.mjs +3 -1
  66. package/dist/primitives/InputNumberExperimental/index.cjs +2 -1
  67. package/dist/primitives/InputNumberExperimental/index.mjs +2 -1
  68. package/dist/primitives/SelectPrimitive/SelectPrimitive.d.ts +63 -0
  69. package/dist/primitives/SelectPrimitive/index.cjs +29 -0
  70. package/dist/primitives/SelectPrimitive/index.d.ts +2 -0
  71. package/dist/primitives/SelectPrimitive/index.mjs +23 -0
  72. package/dist/primitives/SelectPrimitive/types.d.ts +16 -0
  73. package/dist/primitives/index.cjs +9 -1
  74. package/dist/primitives/index.d.ts +1 -0
  75. package/dist/primitives/index.mjs +8 -2
  76. package/dist/primitives/mergeClassName.d.ts +12 -0
  77. package/dist/resolveAriaLabelledBy-cjs.js +195 -0
  78. package/dist/resolveAriaLabelledBy-es.js +162 -0
  79. package/dist/styles.css +685 -0
  80. package/dist/unstyledPrimitives/index.cjs +434 -259
  81. package/dist/unstyledPrimitives/index.d.ts +2 -0
  82. package/dist/unstyledPrimitives/index.mjs +425 -252
  83. package/dist/useButton-cjs.js +2 -0
  84. package/dist/useButton-es.js +3 -2
  85. package/dist/useCompositeListItem-es.js +4 -4
  86. package/dist/useLabel-cjs.js +3 -329
  87. package/dist/useLabel-es.js +4 -299
  88. package/dist/useLabelableId-cjs.js +332 -0
  89. package/dist/useLabelableId-es.js +300 -0
  90. package/dist/useRenderElement-cjs.js +3 -0
  91. package/dist/useRenderElement-es.js +4 -1
  92. package/dist/utils/meta/meta.json +11 -1
  93. package/package.json +2 -2
@@ -4,6 +4,7 @@ var React = require('react');
4
4
  var useRenderElement = require('./useRenderElement-cjs.js');
5
5
  var InternalBackdrop = require('./InternalBackdrop-cjs.js');
6
6
  var useButton = require('./useButton-cjs.js');
7
+ var CSPContext = require('./CSPContext-cjs.js');
7
8
  var clamp = require('./clamp-cjs.js');
8
9
  var jsxRuntime = require('react/jsx-runtime');
9
10
  var floatingUi_utils_dom = require('./floating-ui.utils.dom-cjs.js');
@@ -414,179 +415,6 @@ function useDialogRootContext(optional) {
414
415
  return dialogRootContext;
415
416
  }
416
417
 
417
- let DrawerPopupCssVars = /*#__PURE__*/function (DrawerPopupCssVars) {
418
- /**
419
- * The number of nested drawers that are currently open.
420
- * @type {number}
421
- */
422
- DrawerPopupCssVars["nestedDrawers"] = "--nested-drawers";
423
- /**
424
- * The height of the drawer popup.
425
- * @type {CSS length}
426
- */
427
- DrawerPopupCssVars["height"] = "--drawer-height";
428
- /**
429
- * The height of the frontmost open drawer in the current nested drawer stack.
430
- * @type {CSS length}
431
- */
432
- DrawerPopupCssVars["frontmostHeight"] = "--drawer-frontmost-height";
433
- /**
434
- * The swipe movement on the X axis.
435
- * @type {CSS length}
436
- */
437
- DrawerPopupCssVars["swipeMovementX"] = "--drawer-swipe-movement-x";
438
- /**
439
- * The swipe movement on the Y axis.
440
- * @type {CSS length}
441
- */
442
- DrawerPopupCssVars["swipeMovementY"] = "--drawer-swipe-movement-y";
443
- /**
444
- * The snap point offset used for translating the drawer.
445
- * @type {CSS length}
446
- */
447
- DrawerPopupCssVars["snapPointOffset"] = "--drawer-snap-point-offset";
448
- /**
449
- * A scalar (0.1-1) used to scale the swipe release transition duration in CSS.
450
- * @type {number}
451
- */
452
- DrawerPopupCssVars["swipeStrength"] = "--drawer-swipe-strength";
453
- return DrawerPopupCssVars;
454
- }({});
455
-
456
- let DrawerBackdropCssVars = /*#__PURE__*/function (DrawerBackdropCssVars) {
457
- /**
458
- * The swipe progress of the drawer gesture.
459
- * @type {number}
460
- */
461
- DrawerBackdropCssVars["swipeProgress"] = "--drawer-swipe-progress";
462
- return DrawerBackdropCssVars;
463
- }({});
464
-
465
- const stateAttributesMapping$1 = {
466
- ...InternalBackdrop.popupStateMapping,
467
- ...useButton.transitionStatusMapping
468
- };
469
-
470
- /**
471
- * An overlay displayed beneath the popup.
472
- * Renders a `<div>` element.
473
- *
474
- * Documentation: [Base UI Drawer](https://base-ui.com/react/components/drawer)
475
- */
476
- const DrawerBackdrop = /*#__PURE__*/React__namespace.forwardRef(function DrawerBackdrop(componentProps, forwardedRef) {
477
- const {
478
- render,
479
- className,
480
- forceRender = false,
481
- ...elementProps
482
- } = componentProps;
483
- const {
484
- store
485
- } = useDialogRootContext();
486
- const open = store.useState('open');
487
- const nested = store.useState('nested');
488
- const mounted = store.useState('mounted');
489
- const transitionStatus = store.useState('transitionStatus');
490
- const state = {
491
- open,
492
- transitionStatus
493
- };
494
- return useRenderElement.useRenderElement('div', componentProps, {
495
- state,
496
- ref: [store.context.backdropRef, forwardedRef],
497
- stateAttributesMapping: stateAttributesMapping$1,
498
- props: [{
499
- role: 'presentation',
500
- hidden: !mounted,
501
- style: {
502
- pointerEvents: !open ? 'none' : undefined,
503
- userSelect: 'none',
504
- WebkitUserSelect: 'none',
505
- [DrawerBackdropCssVars.swipeProgress]: '0',
506
- [DrawerPopupCssVars.swipeStrength]: '1'
507
- }
508
- }, elementProps],
509
- enabled: forceRender || !nested
510
- });
511
- });
512
- if (process.env.NODE_ENV !== "production") DrawerBackdrop.displayName = "DrawerBackdrop";
513
-
514
- const DRAWER_CONTENT_ATTRIBUTE = 'data-drawer-content';
515
-
516
- /**
517
- * A container for the drawer contents.
518
- * Renders a `<div>` element.
519
- *
520
- * Documentation: [Base UI Drawer](https://base-ui.com/react/components/drawer)
521
- */
522
- const DrawerContent = /*#__PURE__*/React__namespace.forwardRef(function DrawerContent(componentProps, forwardedRef) {
523
- const {
524
- render,
525
- className,
526
- ...elementProps
527
- } = componentProps;
528
- useDialogRootContext();
529
- return useRenderElement.useRenderElement('div', componentProps, {
530
- ref: forwardedRef,
531
- props: [{
532
- [DRAWER_CONTENT_ATTRIBUTE]: ''
533
- }, elementProps]
534
- });
535
- });
536
- if (process.env.NODE_ENV !== "production") DrawerContent.displayName = "DrawerContent";
537
-
538
- const DrawerProviderContext = /*#__PURE__*/React__namespace.createContext(undefined);
539
- if (process.env.NODE_ENV !== "production") DrawerProviderContext.displayName = "DrawerProviderContext";
540
- function useDrawerProviderContext(optional) {
541
- const context = React__namespace.useContext(DrawerProviderContext);
542
- return context;
543
- }
544
-
545
- let DrawerPopupDataAttributes = function (DrawerPopupDataAttributes) {
546
- /**
547
- * Present when the drawer is open.
548
- */
549
- DrawerPopupDataAttributes[DrawerPopupDataAttributes["open"] = InternalBackdrop.CommonPopupDataAttributes.open] = "open";
550
- /**
551
- * Present when the drawer is closed.
552
- */
553
- DrawerPopupDataAttributes[DrawerPopupDataAttributes["closed"] = InternalBackdrop.CommonPopupDataAttributes.closed] = "closed";
554
- /**
555
- * Present when the drawer is animating in.
556
- */
557
- DrawerPopupDataAttributes[DrawerPopupDataAttributes["startingStyle"] = InternalBackdrop.CommonPopupDataAttributes.startingStyle] = "startingStyle";
558
- /**
559
- * Present when the drawer is animating out.
560
- */
561
- DrawerPopupDataAttributes[DrawerPopupDataAttributes["endingStyle"] = InternalBackdrop.CommonPopupDataAttributes.endingStyle] = "endingStyle";
562
- /**
563
- * Present when the drawer is at the expanded (full-height) snap point.
564
- */
565
- DrawerPopupDataAttributes["expanded"] = "data-expanded";
566
- /**
567
- * Present when a nested drawer is open.
568
- */
569
- DrawerPopupDataAttributes["nestedDrawerOpen"] = "data-nested-drawer-open";
570
- /**
571
- * Present when a nested drawer is being swiped.
572
- */
573
- DrawerPopupDataAttributes["nestedDrawerSwiping"] = "data-nested-drawer-swiping";
574
- /**
575
- * Present when the drawer is dismissed by swiping.
576
- */
577
- DrawerPopupDataAttributes["swipeDismiss"] = "data-swipe-dismiss";
578
- /**
579
- * Indicates the swipe direction.
580
- * @type {'up' | 'down' | 'left' | 'right'}
581
- */
582
- DrawerPopupDataAttributes["swipeDirection"] = "data-swipe-direction";
583
- /**
584
- * Present when the drawer is being swiped.
585
- */
586
- DrawerPopupDataAttributes["swiping"] = "data-swiping";
587
- return DrawerPopupDataAttributes;
588
- }({});
589
-
590
418
  const DialogPortalContext = /*#__PURE__*/React__namespace.createContext(undefined);
591
419
  if (process.env.NODE_ENV !== "production") DialogPortalContext.displayName = "DialogPortalContext";
592
420
  function useDialogPortalContext() {
@@ -597,77 +425,598 @@ function useDialogPortalContext() {
597
425
  return value;
598
426
  }
599
427
 
600
- const ARROW_UP = 'ArrowUp';
601
- const ARROW_DOWN = 'ArrowDown';
602
- const ARROW_LEFT = 'ArrowLeft';
603
- const ARROW_RIGHT = 'ArrowRight';
604
- const HOME = 'Home';
605
- const END = 'End';
606
- const HORIZONTAL_KEYS = new Set([ARROW_LEFT, ARROW_RIGHT]);
607
- const VERTICAL_KEYS = new Set([ARROW_UP, ARROW_DOWN]);
608
- const ARROW_KEYS = new Set([...HORIZONTAL_KEYS, ...VERTICAL_KEYS]);
609
- new Set([...ARROW_KEYS, HOME, END]);
610
- const COMPOSITE_KEYS = new Set([ARROW_UP, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT, HOME, END]);
611
-
612
- const DrawerRootContext = /*#__PURE__*/React__namespace.createContext(undefined);
613
- if (process.env.NODE_ENV !== "production") DrawerRootContext.displayName = "DrawerRootContext";
614
- function useDrawerRootContext(optional) {
615
- const drawerRootContext = React__namespace.useContext(DrawerRootContext);
616
- if (optional === false && drawerRootContext === undefined) {
617
- throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: DrawerRootContext is missing. Drawer parts must be placed within <Drawer.Root>.' : useRenderElement.formatErrorMessage(90));
618
- }
619
- return drawerRootContext;
620
- }
621
-
622
- function resolveSnapPointValue(snapPoint, viewportHeight, rootFontSize) {
623
- if (!Number.isFinite(viewportHeight) || viewportHeight <= 0) {
624
- return null;
625
- }
626
- if (typeof snapPoint === 'number') {
627
- if (!Number.isFinite(snapPoint)) {
628
- return null;
629
- }
630
- if (snapPoint <= 1) {
631
- return clamp.clamp(snapPoint, 0, 1) * viewportHeight;
632
- }
633
- return snapPoint;
634
- }
635
- const trimmed = snapPoint.trim();
636
- if (trimmed.endsWith('px')) {
637
- const value = Number.parseFloat(trimmed);
638
- return Number.isFinite(value) ? value : null;
639
- }
640
- if (trimmed.endsWith('rem')) {
641
- const value = Number.parseFloat(trimmed);
642
- return Number.isFinite(value) ? value * rootFontSize : null;
643
- }
644
- return null;
645
- }
646
- function findClosestSnapPoint(height, points) {
647
- let closest = null;
648
- let closestDistance = Infinity;
649
- for (const point of points) {
650
- const distance = Math.abs(point.height - height);
651
- if (distance < closestDistance) {
652
- closestDistance = distance;
653
- closest = point;
654
- }
655
- }
656
- return closest;
657
- }
658
- function useDrawerSnapPoints() {
428
+ const DialogPortal = /*#__PURE__*/React__namespace.forwardRef(function DialogPortal(props, forwardedRef) {
429
+ const {
430
+ keepMounted = false,
431
+ ...portalProps
432
+ } = props;
659
433
  const {
660
434
  store
661
435
  } = useDialogRootContext();
436
+ const mounted = store.useState('mounted');
437
+ const modal = store.useState('modal');
438
+ const open = store.useState('open');
439
+ const shouldRender = mounted || keepMounted;
440
+ if (!shouldRender) {
441
+ return null;
442
+ }
443
+ return /*#__PURE__*/jsxRuntime.jsx(DialogPortalContext.Provider, {
444
+ value: keepMounted,
445
+ children: /*#__PURE__*/jsxRuntime.jsxs(InternalBackdrop.FloatingPortal, {
446
+ ref: forwardedRef,
447
+ ...portalProps,
448
+ children: [mounted && modal === true && /*#__PURE__*/jsxRuntime.jsx(InternalBackdrop.InternalBackdrop, {
449
+ ref: store.context.internalBackdropRef,
450
+ inert: InternalBackdrop.inertValue(!open)
451
+ }), props.children]
452
+ })
453
+ });
454
+ });
455
+ if (process.env.NODE_ENV !== "production") DialogPortal.displayName = "DialogPortal";
456
+
457
+ function useDialogRoot(params) {
662
458
  const {
663
- snapPoints,
664
- activeSnapPoint,
665
- setActiveSnapPoint,
666
- popupHeight
667
- } = useDrawerRootContext();
668
- const viewportElement = store.useState('viewportElement');
669
- const [viewportHeight, setViewportHeight] = React__namespace.useState(0);
670
- const [rootFontSize, setRootFontSize] = React__namespace.useState(16);
459
+ store,
460
+ parentContext,
461
+ actionsRef
462
+ } = params;
463
+ const open = store.useState('open');
464
+ const disablePointerDismissal = store.useState('disablePointerDismissal');
465
+ const modal = store.useState('modal');
466
+ const popupElement = store.useState('popupElement');
467
+ const {
468
+ openMethod,
469
+ triggerProps
470
+ } = InternalBackdrop.useOpenInteractionType(open);
471
+ useImplicitActiveTrigger(store);
472
+ const {
473
+ forceUnmount
474
+ } = useOpenStateTransitions(open, store);
475
+ const createDialogEventDetails = useButton.useStableCallback(reason => {
476
+ const details = useButton.createChangeEventDetails(reason);
477
+ details.preventUnmountOnClose = () => {
478
+ store.set('preventUnmountingOnClose', true);
479
+ };
480
+ return details;
481
+ });
482
+ const handleImperativeClose = React__namespace.useCallback(() => {
483
+ store.setOpen(false, createDialogEventDetails(useButton.imperativeAction));
484
+ }, [store, createDialogEventDetails]);
485
+ React__namespace.useImperativeHandle(actionsRef, () => ({
486
+ unmount: forceUnmount,
487
+ close: handleImperativeClose
488
+ }), [forceUnmount, handleImperativeClose]);
489
+ const floatingRootContext = useSyncedFloatingRootContext({
490
+ popupStore: store,
491
+ onOpenChange: store.setOpen,
492
+ treatPopupAsFloatingElement: true,
493
+ noEmit: true
494
+ });
495
+ const [ownNestedOpenDialogs, setOwnNestedOpenDialogs] = React__namespace.useState(0);
496
+ const isTopmost = ownNestedOpenDialogs === 0;
497
+ const role = useRole(floatingRootContext);
498
+ const dismiss = InternalBackdrop.useDismiss(floatingRootContext, {
499
+ outsidePressEvent() {
500
+ if (store.context.internalBackdropRef.current || store.context.backdropRef.current) {
501
+ return 'intentional';
502
+ }
503
+ // Ensure `aria-hidden` on outside elements is removed immediately
504
+ // on outside press when trapping focus.
505
+ return {
506
+ mouse: modal === 'trap-focus' ? 'sloppy' : 'intentional',
507
+ touch: 'sloppy'
508
+ };
509
+ },
510
+ outsidePress(event) {
511
+ if (!store.context.outsidePressEnabledRef.current) {
512
+ return false;
513
+ }
514
+
515
+ // For mouse events, only accept left button (button 0)
516
+ // For touch events, a single touch is equivalent to left button
517
+ if ('button' in event && event.button !== 0) {
518
+ return false;
519
+ }
520
+ if ('touches' in event && event.touches.length !== 1) {
521
+ return false;
522
+ }
523
+ const target = useButton.getTarget(event);
524
+ if (isTopmost && !disablePointerDismissal) {
525
+ const eventTarget = target;
526
+ // Only close if the click occurred on the dialog's owning backdrop.
527
+ // This supports multiple modal dialogs that aren't nested in the React tree:
528
+ // https://github.com/mui/base-ui/issues/1320
529
+ if (modal) {
530
+ return store.context.internalBackdropRef.current || store.context.backdropRef.current ? store.context.internalBackdropRef.current === eventTarget || store.context.backdropRef.current === eventTarget || useButton.contains(eventTarget, popupElement) && !eventTarget?.hasAttribute('data-base-ui-portal') : true;
531
+ }
532
+ return true;
533
+ }
534
+ return false;
535
+ },
536
+ escapeKey: isTopmost
537
+ });
538
+ InternalBackdrop.useScrollLock(open && modal === true, popupElement);
539
+ const {
540
+ getReferenceProps,
541
+ getFloatingProps,
542
+ getTriggerProps
543
+ } = InternalBackdrop.useInteractions([role, dismiss]);
544
+
545
+ // Listen for nested open/close events on this store to maintain the count
546
+ store.useContextCallback('onNestedDialogOpen', ownChildrenCount => {
547
+ setOwnNestedOpenDialogs(ownChildrenCount + 1);
548
+ });
549
+ store.useContextCallback('onNestedDialogClose', () => {
550
+ setOwnNestedOpenDialogs(0);
551
+ });
552
+
553
+ // Notify parent of our open/close state using parent callbacks, if any
554
+ React__namespace.useEffect(() => {
555
+ if (parentContext?.onNestedDialogOpen && open) {
556
+ parentContext.onNestedDialogOpen(ownNestedOpenDialogs);
557
+ }
558
+ if (parentContext?.onNestedDialogClose && !open) {
559
+ parentContext.onNestedDialogClose();
560
+ }
561
+ return () => {
562
+ if (parentContext?.onNestedDialogClose && open) {
563
+ parentContext.onNestedDialogClose();
564
+ }
565
+ };
566
+ }, [open, parentContext, ownNestedOpenDialogs]);
567
+ const activeTriggerProps = React__namespace.useMemo(() => getReferenceProps(triggerProps), [getReferenceProps, triggerProps]);
568
+ const inactiveTriggerProps = React__namespace.useMemo(() => getTriggerProps(triggerProps), [getTriggerProps, triggerProps]);
569
+ const popupProps = React__namespace.useMemo(() => getFloatingProps(), [getFloatingProps]);
570
+ store.useSyncedValues({
571
+ openMethod,
572
+ activeTriggerProps,
573
+ inactiveTriggerProps,
574
+ popupProps,
575
+ floatingRootContext,
576
+ nestedOpenDialogCount: ownNestedOpenDialogs
577
+ });
578
+ }
579
+
580
+ const selectors = {
581
+ ...popupStoreSelectors,
582
+ modal: InternalBackdrop.createSelector(state => state.modal),
583
+ nested: InternalBackdrop.createSelector(state => state.nested),
584
+ nestedOpenDialogCount: InternalBackdrop.createSelector(state => state.nestedOpenDialogCount),
585
+ disablePointerDismissal: InternalBackdrop.createSelector(state => state.disablePointerDismissal),
586
+ openMethod: InternalBackdrop.createSelector(state => state.openMethod),
587
+ descriptionElementId: InternalBackdrop.createSelector(state => state.descriptionElementId),
588
+ titleElementId: InternalBackdrop.createSelector(state => state.titleElementId),
589
+ viewportElement: InternalBackdrop.createSelector(state => state.viewportElement),
590
+ role: InternalBackdrop.createSelector(state => state.role)
591
+ };
592
+ class DialogStore extends InternalBackdrop.ReactStore {
593
+ constructor(initialState) {
594
+ super(createInitialState(initialState), {
595
+ popupRef: /*#__PURE__*/React__namespace.createRef(),
596
+ backdropRef: /*#__PURE__*/React__namespace.createRef(),
597
+ internalBackdropRef: /*#__PURE__*/React__namespace.createRef(),
598
+ outsidePressEnabledRef: {
599
+ current: true
600
+ },
601
+ triggerElements: new InternalBackdrop.PopupTriggerMap(),
602
+ onOpenChange: undefined,
603
+ onOpenChangeComplete: undefined
604
+ }, selectors);
605
+ }
606
+ setOpen = (nextOpen, eventDetails) => {
607
+ eventDetails.preventUnmountOnClose = () => {
608
+ this.set('preventUnmountingOnClose', true);
609
+ };
610
+ if (!nextOpen && eventDetails.trigger == null && this.state.activeTriggerId != null) {
611
+ // When closing the dialog, pass the old trigger to the onOpenChange event
612
+ // so it's not reset too early (potentially causing focus issues in controlled scenarios).
613
+ eventDetails.trigger = this.state.activeTriggerElement ?? undefined;
614
+ }
615
+ this.context.onOpenChange?.(nextOpen, eventDetails);
616
+ if (eventDetails.isCanceled) {
617
+ return;
618
+ }
619
+ const details = {
620
+ open: nextOpen,
621
+ nativeEvent: eventDetails.event,
622
+ reason: eventDetails.reason,
623
+ nested: this.state.nested
624
+ };
625
+ this.state.floatingRootContext.context.events?.emit('openchange', details);
626
+ const updatedState = {
627
+ open: nextOpen
628
+ };
629
+
630
+ // If a popup is closing, the `trigger` may be null.
631
+ // We want to keep the previous value so that exit animations are played and focus is returned correctly.
632
+ const newTriggerId = eventDetails.trigger?.id ?? null;
633
+ if (newTriggerId || nextOpen) {
634
+ updatedState.activeTriggerId = newTriggerId;
635
+ updatedState.activeTriggerElement = eventDetails.trigger ?? null;
636
+ }
637
+ this.update(updatedState);
638
+ };
639
+ }
640
+ function createInitialState(initialState = {}) {
641
+ return {
642
+ ...createInitialPopupStoreState(),
643
+ modal: true,
644
+ disablePointerDismissal: false,
645
+ popupElement: null,
646
+ viewportElement: null,
647
+ descriptionElementId: undefined,
648
+ titleElementId: undefined,
649
+ openMethod: null,
650
+ nested: false,
651
+ nestedOpenDialogCount: 0,
652
+ role: 'dialog',
653
+ ...initialState
654
+ };
655
+ }
656
+
657
+ /**
658
+ * Groups all parts of the dialog.
659
+ * Doesn’t render its own HTML element.
660
+ *
661
+ * Documentation: [Base UI Dialog](https://base-ui.com/react/components/dialog)
662
+ */
663
+ function DialogRoot(props) {
664
+ const {
665
+ children,
666
+ open: openProp,
667
+ defaultOpen = false,
668
+ onOpenChange,
669
+ onOpenChangeComplete,
670
+ disablePointerDismissal = false,
671
+ modal = true,
672
+ actionsRef,
673
+ handle,
674
+ triggerId: triggerIdProp,
675
+ defaultTriggerId: defaultTriggerIdProp = null
676
+ } = props;
677
+ const parentDialogRootContext = useDialogRootContext(true);
678
+ const nested = Boolean(parentDialogRootContext);
679
+ const store = useRenderElement.useRefWithInit(() => {
680
+ return handle?.store ?? new DialogStore({
681
+ open: defaultOpen,
682
+ openProp,
683
+ activeTriggerId: defaultTriggerIdProp,
684
+ triggerIdProp,
685
+ modal,
686
+ disablePointerDismissal,
687
+ nested
688
+ });
689
+ }).current;
690
+
691
+ // Support initially open state when uncontrolled
692
+ InternalBackdrop.useOnFirstRender(() => {
693
+ if (openProp === undefined && store.state.open === false && defaultOpen === true) {
694
+ store.update({
695
+ open: true,
696
+ activeTriggerId: defaultTriggerIdProp
697
+ });
698
+ }
699
+ });
700
+ store.useControlledProp('openProp', openProp);
701
+ store.useControlledProp('triggerIdProp', triggerIdProp);
702
+ store.useSyncedValues({
703
+ disablePointerDismissal,
704
+ nested,
705
+ modal
706
+ });
707
+ store.useContextCallback('onOpenChange', onOpenChange);
708
+ store.useContextCallback('onOpenChangeComplete', onOpenChangeComplete);
709
+ const payload = store.useState('payload');
710
+ useDialogRoot({
711
+ store,
712
+ actionsRef,
713
+ parentContext: parentDialogRootContext?.store.context,
714
+ onOpenChange,
715
+ triggerIdProp
716
+ });
717
+ const contextValue = React__namespace.useMemo(() => ({
718
+ store
719
+ }), [store]);
720
+ return /*#__PURE__*/jsxRuntime.jsx(DialogRootContext.Provider, {
721
+ value: contextValue,
722
+ children: typeof children === 'function' ? children({
723
+ payload
724
+ }) : children
725
+ });
726
+ }
727
+
728
+ /**
729
+ * A button that opens the dialog.
730
+ * Renders a `<button>` element.
731
+ *
732
+ * Documentation: [Base UI Dialog](https://base-ui.com/react/components/dialog)
733
+ */
734
+ const DialogTrigger = /*#__PURE__*/React__namespace.forwardRef(function DialogTrigger(componentProps, forwardedRef) {
735
+ const {
736
+ render,
737
+ className,
738
+ disabled = false,
739
+ nativeButton = true,
740
+ id: idProp,
741
+ payload,
742
+ handle,
743
+ ...elementProps
744
+ } = componentProps;
745
+ const dialogRootContext = useDialogRootContext(true);
746
+ const store = handle?.store ?? dialogRootContext?.store;
747
+ if (!store) {
748
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: <Dialog.Trigger> must be used within <Dialog.Root> or provided with a handle.' : useRenderElement.formatErrorMessage(79));
749
+ }
750
+ const thisTriggerId = useButton.useBaseUiId(idProp);
751
+ const floatingContext = store.useState('floatingRootContext');
752
+ const isOpenedByThisTrigger = store.useState('isOpenedByTrigger', thisTriggerId);
753
+ const triggerElementRef = React__namespace.useRef(null);
754
+ const {
755
+ registerTrigger,
756
+ isMountedByThisTrigger
757
+ } = useTriggerDataForwarding(thisTriggerId, triggerElementRef, store, {
758
+ payload
759
+ });
760
+ const {
761
+ getButtonProps,
762
+ buttonRef
763
+ } = useButton.useButton({
764
+ disabled,
765
+ native: nativeButton
766
+ });
767
+ const click = InternalBackdrop.useClick(floatingContext, {
768
+ enabled: floatingContext != null
769
+ });
770
+ const localInteractionProps = InternalBackdrop.useInteractions([click]);
771
+ const state = {
772
+ disabled,
773
+ open: isOpenedByThisTrigger
774
+ };
775
+ const rootTriggerProps = store.useState('triggerProps', isMountedByThisTrigger);
776
+ return useRenderElement.useRenderElement('button', componentProps, {
777
+ state,
778
+ ref: [buttonRef, forwardedRef, registerTrigger, triggerElementRef],
779
+ props: [localInteractionProps.getReferenceProps(), rootTriggerProps, {
780
+ [InternalBackdrop.CLICK_TRIGGER_IDENTIFIER]: '',
781
+ id: thisTriggerId
782
+ }, elementProps, getButtonProps],
783
+ stateAttributesMapping: InternalBackdrop.triggerOpenStateMapping
784
+ });
785
+ });
786
+ if (process.env.NODE_ENV !== "production") DialogTrigger.displayName = "DialogTrigger";
787
+
788
+ let DrawerPopupCssVars = /*#__PURE__*/function (DrawerPopupCssVars) {
789
+ /**
790
+ * The number of nested drawers that are currently open.
791
+ * @type {number}
792
+ */
793
+ DrawerPopupCssVars["nestedDrawers"] = "--nested-drawers";
794
+ /**
795
+ * The height of the drawer popup.
796
+ * @type {CSS length}
797
+ */
798
+ DrawerPopupCssVars["height"] = "--drawer-height";
799
+ /**
800
+ * The height of the frontmost open drawer in the current nested drawer stack.
801
+ * @type {CSS length}
802
+ */
803
+ DrawerPopupCssVars["frontmostHeight"] = "--drawer-frontmost-height";
804
+ /**
805
+ * The swipe movement on the X axis.
806
+ * @type {CSS length}
807
+ */
808
+ DrawerPopupCssVars["swipeMovementX"] = "--drawer-swipe-movement-x";
809
+ /**
810
+ * The swipe movement on the Y axis.
811
+ * @type {CSS length}
812
+ */
813
+ DrawerPopupCssVars["swipeMovementY"] = "--drawer-swipe-movement-y";
814
+ /**
815
+ * The snap point offset used for translating the drawer.
816
+ * @type {CSS length}
817
+ */
818
+ DrawerPopupCssVars["snapPointOffset"] = "--drawer-snap-point-offset";
819
+ /**
820
+ * A scalar (0.1-1) used to scale the swipe release transition duration in CSS.
821
+ * @type {number}
822
+ */
823
+ DrawerPopupCssVars["swipeStrength"] = "--drawer-swipe-strength";
824
+ return DrawerPopupCssVars;
825
+ }({});
826
+
827
+ let DrawerBackdropCssVars = /*#__PURE__*/function (DrawerBackdropCssVars) {
828
+ /**
829
+ * The swipe progress of the drawer gesture.
830
+ * @type {number}
831
+ */
832
+ DrawerBackdropCssVars["swipeProgress"] = "--drawer-swipe-progress";
833
+ return DrawerBackdropCssVars;
834
+ }({});
835
+
836
+ const stateAttributesMapping$1 = {
837
+ ...InternalBackdrop.popupStateMapping,
838
+ ...useButton.transitionStatusMapping
839
+ };
840
+
841
+ /**
842
+ * An overlay displayed beneath the popup.
843
+ * Renders a `<div>` element.
844
+ *
845
+ * Documentation: [Base UI Drawer](https://base-ui.com/react/components/drawer)
846
+ */
847
+ const DrawerBackdrop = /*#__PURE__*/React__namespace.forwardRef(function DrawerBackdrop(componentProps, forwardedRef) {
848
+ const {
849
+ render,
850
+ className,
851
+ forceRender = false,
852
+ ...elementProps
853
+ } = componentProps;
854
+ const {
855
+ store
856
+ } = useDialogRootContext();
857
+ const open = store.useState('open');
858
+ const nested = store.useState('nested');
859
+ const mounted = store.useState('mounted');
860
+ const transitionStatus = store.useState('transitionStatus');
861
+ const state = {
862
+ open,
863
+ transitionStatus
864
+ };
865
+ return useRenderElement.useRenderElement('div', componentProps, {
866
+ state,
867
+ ref: [store.context.backdropRef, forwardedRef],
868
+ stateAttributesMapping: stateAttributesMapping$1,
869
+ props: [{
870
+ role: 'presentation',
871
+ hidden: !mounted,
872
+ style: {
873
+ pointerEvents: !open ? 'none' : undefined,
874
+ userSelect: 'none',
875
+ WebkitUserSelect: 'none',
876
+ [DrawerBackdropCssVars.swipeProgress]: '0',
877
+ [DrawerPopupCssVars.swipeStrength]: '1'
878
+ }
879
+ }, elementProps],
880
+ enabled: forceRender || !nested
881
+ });
882
+ });
883
+ if (process.env.NODE_ENV !== "production") DrawerBackdrop.displayName = "DrawerBackdrop";
884
+
885
+ const DRAWER_CONTENT_ATTRIBUTE = 'data-drawer-content';
886
+
887
+ /**
888
+ * A container for the drawer contents.
889
+ * Renders a `<div>` element.
890
+ *
891
+ * Documentation: [Base UI Drawer](https://base-ui.com/react/components/drawer)
892
+ */
893
+ const DrawerContent = /*#__PURE__*/React__namespace.forwardRef(function DrawerContent(componentProps, forwardedRef) {
894
+ const {
895
+ render,
896
+ className,
897
+ ...elementProps
898
+ } = componentProps;
899
+ useDialogRootContext();
900
+ return useRenderElement.useRenderElement('div', componentProps, {
901
+ ref: forwardedRef,
902
+ props: [{
903
+ [DRAWER_CONTENT_ATTRIBUTE]: ''
904
+ }, elementProps]
905
+ });
906
+ });
907
+ if (process.env.NODE_ENV !== "production") DrawerContent.displayName = "DrawerContent";
908
+
909
+ const DrawerProviderContext = /*#__PURE__*/React__namespace.createContext(undefined);
910
+ if (process.env.NODE_ENV !== "production") DrawerProviderContext.displayName = "DrawerProviderContext";
911
+ function useDrawerProviderContext(optional) {
912
+ const context = React__namespace.useContext(DrawerProviderContext);
913
+ return context;
914
+ }
915
+
916
+ let DrawerPopupDataAttributes = function (DrawerPopupDataAttributes) {
917
+ /**
918
+ * Present when the drawer is open.
919
+ */
920
+ DrawerPopupDataAttributes[DrawerPopupDataAttributes["open"] = InternalBackdrop.CommonPopupDataAttributes.open] = "open";
921
+ /**
922
+ * Present when the drawer is closed.
923
+ */
924
+ DrawerPopupDataAttributes[DrawerPopupDataAttributes["closed"] = InternalBackdrop.CommonPopupDataAttributes.closed] = "closed";
925
+ /**
926
+ * Present when the drawer is animating in.
927
+ */
928
+ DrawerPopupDataAttributes[DrawerPopupDataAttributes["startingStyle"] = InternalBackdrop.CommonPopupDataAttributes.startingStyle] = "startingStyle";
929
+ /**
930
+ * Present when the drawer is animating out.
931
+ */
932
+ DrawerPopupDataAttributes[DrawerPopupDataAttributes["endingStyle"] = InternalBackdrop.CommonPopupDataAttributes.endingStyle] = "endingStyle";
933
+ /**
934
+ * Present when the drawer is at the expanded (full-height) snap point.
935
+ */
936
+ DrawerPopupDataAttributes["expanded"] = "data-expanded";
937
+ /**
938
+ * Present when a nested drawer is open.
939
+ */
940
+ DrawerPopupDataAttributes["nestedDrawerOpen"] = "data-nested-drawer-open";
941
+ /**
942
+ * Present when a nested drawer is being swiped.
943
+ */
944
+ DrawerPopupDataAttributes["nestedDrawerSwiping"] = "data-nested-drawer-swiping";
945
+ /**
946
+ * Present when the drawer is dismissed by swiping.
947
+ */
948
+ DrawerPopupDataAttributes["swipeDismiss"] = "data-swipe-dismiss";
949
+ /**
950
+ * Indicates the swipe direction.
951
+ * @type {'up' | 'down' | 'left' | 'right'}
952
+ */
953
+ DrawerPopupDataAttributes["swipeDirection"] = "data-swipe-direction";
954
+ /**
955
+ * Present when the drawer is being swiped.
956
+ */
957
+ DrawerPopupDataAttributes["swiping"] = "data-swiping";
958
+ return DrawerPopupDataAttributes;
959
+ }({});
960
+
961
+ const DrawerRootContext = /*#__PURE__*/React__namespace.createContext(undefined);
962
+ if (process.env.NODE_ENV !== "production") DrawerRootContext.displayName = "DrawerRootContext";
963
+ function useDrawerRootContext(optional) {
964
+ const drawerRootContext = React__namespace.useContext(DrawerRootContext);
965
+ if (optional === false && drawerRootContext === undefined) {
966
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: DrawerRootContext is missing. Drawer parts must be placed within <Drawer.Root>.' : useRenderElement.formatErrorMessage(90));
967
+ }
968
+ return drawerRootContext;
969
+ }
970
+
971
+ function resolveSnapPointValue(snapPoint, viewportHeight, rootFontSize) {
972
+ if (!Number.isFinite(viewportHeight) || viewportHeight <= 0) {
973
+ return null;
974
+ }
975
+ if (typeof snapPoint === 'number') {
976
+ if (!Number.isFinite(snapPoint)) {
977
+ return null;
978
+ }
979
+ if (snapPoint <= 1) {
980
+ return clamp.clamp(snapPoint, 0, 1) * viewportHeight;
981
+ }
982
+ return snapPoint;
983
+ }
984
+ const trimmed = snapPoint.trim();
985
+ if (trimmed.endsWith('px')) {
986
+ const value = Number.parseFloat(trimmed);
987
+ return Number.isFinite(value) ? value : null;
988
+ }
989
+ if (trimmed.endsWith('rem')) {
990
+ const value = Number.parseFloat(trimmed);
991
+ return Number.isFinite(value) ? value * rootFontSize : null;
992
+ }
993
+ return null;
994
+ }
995
+ function findClosestSnapPoint(height, points) {
996
+ let closest = null;
997
+ let closestDistance = Infinity;
998
+ for (const point of points) {
999
+ const distance = Math.abs(point.height - height);
1000
+ if (distance < closestDistance) {
1001
+ closestDistance = distance;
1002
+ closest = point;
1003
+ }
1004
+ }
1005
+ return closest;
1006
+ }
1007
+ function useDrawerSnapPoints() {
1008
+ const {
1009
+ store
1010
+ } = useDialogRootContext();
1011
+ const {
1012
+ snapPoints,
1013
+ activeSnapPoint,
1014
+ setActiveSnapPoint,
1015
+ popupHeight
1016
+ } = useDrawerRootContext();
1017
+ const viewportElement = store.useState('viewportElement');
1018
+ const [viewportHeight, setViewportHeight] = React__namespace.useState(0);
1019
+ const [rootFontSize, setRootFontSize] = React__namespace.useState(16);
671
1020
  const measureViewportHeight = useButton.useStableCallback(() => {
672
1021
  const doc = useButton.ownerDocument(viewportElement);
673
1022
  const html = doc.documentElement;
@@ -733,728 +1082,368 @@ function useDrawerSnapPoints() {
733
1082
  const resolvedActiveSnapPoint = React__namespace.useMemo(() => {
734
1083
  if (activeSnapPoint === undefined) {
735
1084
  return resolvedSnapPoints[0];
736
- }
737
- if (activeSnapPoint === null) {
738
- return undefined;
739
- }
740
- const exactMatch = resolvedSnapPoints.find(point => Object.is(point.value, activeSnapPoint));
741
- if (exactMatch) {
742
- return exactMatch;
743
- }
744
- const maxHeight = Math.min(popupHeight, viewportHeight);
745
- const resolvedHeight = resolveSnapPointValue(activeSnapPoint, viewportHeight, rootFontSize);
746
- if (resolvedHeight === null || !Number.isFinite(resolvedHeight)) {
747
- return undefined;
748
- }
749
- const clampedHeight = clamp.clamp(resolvedHeight, 0, maxHeight);
750
- return findClosestSnapPoint(clampedHeight, resolvedSnapPoints) ?? undefined;
751
- }, [activeSnapPoint, popupHeight, resolvedSnapPoints, rootFontSize, viewportHeight]);
752
- return {
753
- snapPoints,
754
- activeSnapPoint,
755
- setActiveSnapPoint,
756
- popupHeight,
757
- viewportHeight,
758
- resolvedSnapPoints,
759
- activeSnapPointOffset: resolvedActiveSnapPoint?.offset ?? null
760
- };
761
- }
762
-
763
- const DrawerViewportContext = /*#__PURE__*/React__namespace.createContext(null);
764
- if (process.env.NODE_ENV !== "production") DrawerViewportContext.displayName = "DrawerViewportContext";
765
- function useDrawerViewportContext(optional) {
766
- const context = React__namespace.useContext(DrawerViewportContext);
767
- return context;
768
- }
769
-
770
- let drawerSwipeVarsRegistered = false;
771
-
772
- /**
773
- * Removes inheritance of high-frequency drawer swipe CSS variables, which
774
- * reduces style recalculation cost in complex drawers with deep subtrees.
775
- * Child elements that need these values can still opt-in by using `inherit`.
776
- * See https://motion.dev/blog/web-animation-performance-tier-list
777
- * under the "Improving CSS variable performance" section.
778
- */
779
- function removeCSSVariableInheritance$1() {
780
- if (drawerSwipeVarsRegistered) {
781
- return;
782
- }
783
-
784
- // Intentionally keep inheritance disabled on WebKit as well. Safari doesn't support
785
- // opting descendants back in via `--var: inherit` for custom properties registered
786
- // with `inherits: false`, but Drawer does not rely on descendant access to these vars
787
- // (unlike ScrollArea), so we keep the performance optimization enabled.
788
- if (typeof CSS !== 'undefined' && 'registerProperty' in CSS) {
789
- [DrawerPopupCssVars.swipeMovementX, DrawerPopupCssVars.swipeMovementY, DrawerPopupCssVars.snapPointOffset].forEach(name => {
790
- try {
791
- CSS.registerProperty({
792
- name,
793
- syntax: '<length>',
794
- inherits: false,
795
- initialValue: '0px'
796
- });
797
- } catch {
798
- /* ignore already-registered */
799
- }
800
- });
801
- [{
802
- name: DrawerBackdropCssVars.swipeProgress,
803
- initialValue: '0'
804
- }, {
805
- name: DrawerPopupCssVars.swipeStrength,
806
- initialValue: '1'
807
- }].forEach(({
808
- name,
809
- initialValue
810
- }) => {
811
- try {
812
- CSS.registerProperty({
813
- name,
814
- syntax: '<number>',
815
- inherits: false,
816
- initialValue
817
- });
818
- } catch {
819
- /* ignore already-registered */
820
- }
821
- });
822
- }
823
- drawerSwipeVarsRegistered = true;
824
- }
825
- const stateAttributesMapping = {
826
- ...InternalBackdrop.popupStateMapping,
827
- ...useButton.transitionStatusMapping,
828
- expanded(value) {
829
- return value ? {
830
- [DrawerPopupDataAttributes.expanded]: ''
831
- } : null;
832
- },
833
- nestedDrawerOpen(value) {
834
- return value ? {
835
- [DrawerPopupDataAttributes.nestedDrawerOpen]: ''
836
- } : null;
837
- },
838
- nestedDrawerSwiping(value) {
839
- return value ? {
840
- [DrawerPopupDataAttributes.nestedDrawerSwiping]: ''
841
- } : null;
842
- },
843
- swipeDirection(value) {
844
- return value ? {
845
- [DrawerPopupDataAttributes.swipeDirection]: value
846
- } : null;
847
- },
848
- swiping(value) {
849
- return value ? {
850
- [DrawerPopupDataAttributes.swiping]: ''
851
- } : null;
852
- }
853
- };
854
-
855
- /**
856
- * A container for the drawer contents.
857
- * Renders a `<div>` element.
858
- *
859
- * Documentation: [Base UI Drawer](https://base-ui.com/react/components/drawer)
860
- */
861
- const DrawerPopup = /*#__PURE__*/React__namespace.forwardRef(function DrawerPopup(componentProps, forwardedRef) {
862
- const {
863
- className,
864
- finalFocus,
865
- initialFocus,
866
- render,
867
- ...elementProps
868
- } = componentProps;
869
- const {
870
- store
871
- } = useDialogRootContext();
872
- const {
873
- swipeDirection,
874
- frontmostHeight,
875
- hasNestedDrawer,
876
- nestedSwiping,
877
- nestedSwipeProgressStore,
878
- onPopupHeightChange,
879
- notifyParentFrontmostHeight,
880
- notifyParentHasNestedDrawer
881
- } = useDrawerRootContext();
882
- const descriptionElementId = store.useState('descriptionElementId');
883
- const disablePointerDismissal = store.useState('disablePointerDismissal');
884
- const floatingRootContext = store.useState('floatingRootContext');
885
- const rootPopupProps = store.useState('popupProps');
886
- const modal = store.useState('modal');
887
- const mounted = store.useState('mounted');
888
- const nested = store.useState('nested');
889
- const nestedOpenDialogCount = store.useState('nestedOpenDialogCount');
890
- const transitionStatus = store.useState('transitionStatus');
891
- const open = store.useState('open');
892
- const openMethod = store.useState('openMethod');
893
- const titleElementId = store.useState('titleElementId');
894
- const role = store.useState('role');
895
- const nestedDrawerOpen = nestedOpenDialogCount > 0;
896
- const swipe = useDrawerViewportContext();
897
- const swiping = swipe?.swiping ?? false;
898
- const swipeStrength = swipe?.swipeStrength ?? null;
899
- const {
900
- snapPoints,
901
- activeSnapPoint,
902
- activeSnapPointOffset
903
- } = useDrawerSnapPoints();
904
- useDialogPortalContext();
905
- const [popupHeight, setPopupHeight] = React__namespace.useState(0);
906
- const popupHeightRef = React__namespace.useRef(0);
907
- const measureHeight = useButton.useStableCallback(() => {
908
- const popupElement = store.context.popupRef.current;
909
- if (!popupElement) {
910
- return;
911
- }
912
- const offsetHeight = popupElement.offsetHeight;
913
-
914
- // Only skip while the element is still actually stretched beyond its last measured height.
915
- if (popupHeightRef.current > 0 && frontmostHeight > popupHeightRef.current && offsetHeight > popupHeightRef.current) {
916
- return;
917
- }
918
- const keepHeightWhileNested = popupHeightRef.current > 0 && hasNestedDrawer;
919
- if (keepHeightWhileNested) {
920
- const oldHeight = popupHeightRef.current;
921
- setPopupHeight(oldHeight);
922
- onPopupHeightChange(oldHeight);
923
- return;
924
- }
925
- const nextHeight = offsetHeight;
926
- if (nextHeight === popupHeightRef.current) {
927
- return;
928
- }
929
- popupHeightRef.current = nextHeight;
930
- setPopupHeight(nextHeight);
931
- onPopupHeightChange(nextHeight);
932
- });
933
- useButton.useIsoLayoutEffect(() => {
934
- if (!mounted) {
935
- popupHeightRef.current = 0;
936
- setPopupHeight(0);
937
- onPopupHeightChange(0);
938
- return undefined;
939
- }
940
- const popupElement = store.context.popupRef.current;
941
- if (!popupElement) {
942
- return undefined;
943
- }
944
- removeCSSVariableInheritance$1();
945
- measureHeight();
946
- if (typeof ResizeObserver !== 'function') {
947
- return undefined;
948
- }
949
- const resizeObserver = new ResizeObserver(measureHeight);
950
- resizeObserver.observe(popupElement);
951
- return () => {
952
- resizeObserver.disconnect();
953
- };
954
- }, [measureHeight, mounted, nestedDrawerOpen, onPopupHeightChange, store.context.popupRef]);
955
- useButton.useIsoLayoutEffect(() => {
956
- const popupRef = store.context.popupRef;
957
- const syncNestedSwipeProgress = () => {
958
- const popupElement = popupRef.current;
959
- if (!popupElement) {
960
- return;
961
- }
962
- const progress = nestedSwipeProgressStore.getSnapshot();
963
- if (progress > 0) {
964
- popupElement.style.setProperty(DrawerBackdropCssVars.swipeProgress, `${progress}`);
965
- } else {
966
- popupElement.style.setProperty(DrawerBackdropCssVars.swipeProgress, '0');
967
- }
968
- };
969
- syncNestedSwipeProgress();
970
- const unsubscribe = nestedSwipeProgressStore.subscribe(syncNestedSwipeProgress);
971
- return () => {
972
- unsubscribe();
973
- const popupElement = popupRef.current;
974
- if (popupElement) {
975
- popupElement.style.setProperty(DrawerBackdropCssVars.swipeProgress, '0');
976
- }
977
- };
978
- }, [nestedSwipeProgressStore, store.context.popupRef]);
979
- React__namespace.useEffect(() => {
980
- if (!open) {
981
- return undefined;
982
- }
983
- notifyParentFrontmostHeight?.(frontmostHeight);
984
- return () => {
985
- notifyParentFrontmostHeight?.(0);
986
- };
987
- }, [frontmostHeight, open, notifyParentFrontmostHeight]);
988
- React__namespace.useEffect(() => {
989
- if (!notifyParentHasNestedDrawer) {
1085
+ }
1086
+ if (activeSnapPoint === null) {
990
1087
  return undefined;
991
1088
  }
992
- const present = open || transitionStatus === 'ending';
993
- notifyParentHasNestedDrawer(present);
994
- return () => {
995
- notifyParentHasNestedDrawer(false);
996
- };
997
- }, [notifyParentHasNestedDrawer, open, transitionStatus]);
998
- useButton.useOpenChangeComplete({
999
- open,
1000
- ref: store.context.popupRef,
1001
- onComplete() {
1002
- if (open) {
1003
- store.context.onOpenChangeComplete?.(true);
1004
- }
1089
+ const exactMatch = resolvedSnapPoints.find(point => Object.is(point.value, activeSnapPoint));
1090
+ if (exactMatch) {
1091
+ return exactMatch;
1005
1092
  }
1006
- });
1007
- const resolvedInitialFocus = initialFocus === undefined ? store.context.popupRef : initialFocus;
1008
- const state = {
1009
- open,
1010
- nested,
1011
- transitionStatus,
1012
- expanded: activeSnapPoint === 1,
1013
- nestedDrawerOpen,
1014
- nestedDrawerSwiping: nestedSwiping,
1015
- swipeDirection,
1016
- swiping
1017
- };
1018
- let popupHeightCssVarValue;
1019
- const shouldUseAutoHeight = !hasNestedDrawer && transitionStatus !== 'ending';
1020
- if (popupHeight && !shouldUseAutoHeight) {
1021
- popupHeightCssVarValue = `${popupHeight}px`;
1022
- }
1023
- const shouldApplySnapPoints = snapPoints && snapPoints.length > 0 && (swipeDirection === 'down' || swipeDirection === 'up');
1024
- let snapPointOffsetValue = null;
1025
- if (shouldApplySnapPoints && activeSnapPointOffset !== null) {
1026
- snapPointOffsetValue = swipeDirection === 'up' ? -activeSnapPointOffset : activeSnapPointOffset;
1027
- }
1028
- let dragStyles = swipe ? swipe.getDragStyles() : useRenderElement.EMPTY_OBJECT;
1029
- if (shouldApplySnapPoints && swipeDirection === 'down') {
1030
- const baseOffset = activeSnapPointOffset ?? 0;
1031
- const movementValue = Number.parseFloat(String(dragStyles[DrawerPopupCssVars.swipeMovementY] ?? 0));
1032
- const nextOffset = Number.isFinite(movementValue) ? baseOffset + movementValue : baseOffset;
1033
- const shouldDamp = nextOffset < 0;
1034
- if (swiping && shouldDamp && Number.isFinite(movementValue)) {
1035
- const overshoot = Math.abs(nextOffset);
1036
- const dampedOffset = -Math.sqrt(overshoot);
1037
- const dampedMovement = dampedOffset - baseOffset;
1038
- dragStyles = {
1039
- ...dragStyles,
1040
- transform: undefined,
1041
- [DrawerPopupCssVars.swipeMovementY]: `${dampedMovement}px`
1042
- };
1043
- } else {
1044
- dragStyles = {
1045
- ...dragStyles,
1046
- transform: undefined
1047
- };
1093
+ const maxHeight = Math.min(popupHeight, viewportHeight);
1094
+ const resolvedHeight = resolveSnapPointValue(activeSnapPoint, viewportHeight, rootFontSize);
1095
+ if (resolvedHeight === null || !Number.isFinite(resolvedHeight)) {
1096
+ return undefined;
1048
1097
  }
1098
+ const clampedHeight = clamp.clamp(resolvedHeight, 0, maxHeight);
1099
+ return findClosestSnapPoint(clampedHeight, resolvedSnapPoints) ?? undefined;
1100
+ }, [activeSnapPoint, popupHeight, resolvedSnapPoints, rootFontSize, viewportHeight]);
1101
+ return {
1102
+ snapPoints,
1103
+ activeSnapPoint,
1104
+ setActiveSnapPoint,
1105
+ popupHeight,
1106
+ viewportHeight,
1107
+ resolvedSnapPoints,
1108
+ activeSnapPointOffset: resolvedActiveSnapPoint?.offset ?? null
1109
+ };
1110
+ }
1111
+
1112
+ const DrawerViewportContext = /*#__PURE__*/React__namespace.createContext(null);
1113
+ if (process.env.NODE_ENV !== "production") DrawerViewportContext.displayName = "DrawerViewportContext";
1114
+ function useDrawerViewportContext(optional) {
1115
+ const context = React__namespace.useContext(DrawerViewportContext);
1116
+ return context;
1117
+ }
1118
+
1119
+ let drawerSwipeVarsRegistered = false;
1120
+
1121
+ /**
1122
+ * Removes inheritance of high-frequency drawer swipe CSS variables, which
1123
+ * reduces style recalculation cost in complex drawers with deep subtrees.
1124
+ * Child elements that need these values can still opt-in by using `inherit`.
1125
+ * See https://motion.dev/blog/web-animation-performance-tier-list
1126
+ * under the "Improving CSS variable performance" section.
1127
+ */
1128
+ function removeCSSVariableInheritance$1() {
1129
+ if (drawerSwipeVarsRegistered) {
1130
+ return;
1049
1131
  }
1050
- const element = useRenderElement.useRenderElement('div', componentProps, {
1051
- state,
1052
- props: [rootPopupProps, {
1053
- 'aria-labelledby': titleElementId,
1054
- 'aria-describedby': descriptionElementId,
1055
- role,
1056
- tabIndex: -1,
1057
- hidden: !mounted,
1058
- onKeyDown(event) {
1059
- if (COMPOSITE_KEYS.has(event.key)) {
1060
- event.stopPropagation();
1061
- }
1062
- },
1063
- style: {
1064
- ...dragStyles,
1065
- [DrawerBackdropCssVars.swipeProgress]: '0',
1066
- [DrawerPopupCssVars.nestedDrawers]: nestedOpenDialogCount,
1067
- [DrawerPopupCssVars.height]: popupHeightCssVarValue,
1068
- [DrawerPopupCssVars.snapPointOffset]: typeof snapPointOffsetValue === 'number' ? `${snapPointOffsetValue}px` : '0px',
1069
- [DrawerPopupCssVars.frontmostHeight]: frontmostHeight ? `${frontmostHeight}px` : undefined,
1070
- [DrawerPopupCssVars.swipeStrength]: typeof swipeStrength === 'number' && Number.isFinite(swipeStrength) && swipeStrength > 0 ? `${swipeStrength}` : '1'
1071
- }
1072
- }, elementProps],
1073
- ref: [forwardedRef, store.context.popupRef, store.useStateSetter('popupElement')],
1074
- stateAttributesMapping
1075
- });
1076
- return /*#__PURE__*/jsxRuntime.jsx(InternalBackdrop.FloatingFocusManager, {
1077
- context: floatingRootContext,
1078
- openInteractionType: openMethod,
1079
- disabled: !mounted,
1080
- closeOnFocusOut: !disablePointerDismissal,
1081
- initialFocus: resolvedInitialFocus,
1082
- returnFocus: finalFocus,
1083
- modal: modal !== false,
1084
- restoreFocus: "popup",
1085
- children: element
1086
- });
1087
- });
1088
- if (process.env.NODE_ENV !== "production") DrawerPopup.displayName = "DrawerPopup";
1089
1132
 
1090
- const DialogPortal = /*#__PURE__*/React__namespace.forwardRef(function DialogPortal(props, forwardedRef) {
1091
- const {
1092
- keepMounted = false,
1093
- ...portalProps
1094
- } = props;
1095
- const {
1096
- store
1097
- } = useDialogRootContext();
1098
- const mounted = store.useState('mounted');
1099
- const modal = store.useState('modal');
1100
- const open = store.useState('open');
1101
- const shouldRender = mounted || keepMounted;
1102
- if (!shouldRender) {
1103
- return null;
1133
+ // Intentionally keep inheritance disabled on WebKit as well. Safari doesn't support
1134
+ // opting descendants back in via `--var: inherit` for custom properties registered
1135
+ // with `inherits: false`, but Drawer does not rely on descendant access to these vars
1136
+ // (unlike ScrollArea), so we keep the performance optimization enabled.
1137
+ if (typeof CSS !== 'undefined' && 'registerProperty' in CSS) {
1138
+ [DrawerPopupCssVars.swipeMovementX, DrawerPopupCssVars.swipeMovementY, DrawerPopupCssVars.snapPointOffset].forEach(name => {
1139
+ try {
1140
+ CSS.registerProperty({
1141
+ name,
1142
+ syntax: '<length>',
1143
+ inherits: false,
1144
+ initialValue: '0px'
1145
+ });
1146
+ } catch {
1147
+ /* ignore already-registered */
1148
+ }
1149
+ });
1150
+ [{
1151
+ name: DrawerBackdropCssVars.swipeProgress,
1152
+ initialValue: '0'
1153
+ }, {
1154
+ name: DrawerPopupCssVars.swipeStrength,
1155
+ initialValue: '1'
1156
+ }].forEach(({
1157
+ name,
1158
+ initialValue
1159
+ }) => {
1160
+ try {
1161
+ CSS.registerProperty({
1162
+ name,
1163
+ syntax: '<number>',
1164
+ inherits: false,
1165
+ initialValue
1166
+ });
1167
+ } catch {
1168
+ /* ignore already-registered */
1169
+ }
1170
+ });
1104
1171
  }
1105
- return /*#__PURE__*/jsxRuntime.jsx(DialogPortalContext.Provider, {
1106
- value: keepMounted,
1107
- children: /*#__PURE__*/jsxRuntime.jsxs(InternalBackdrop.FloatingPortal, {
1108
- ref: forwardedRef,
1109
- ...portalProps,
1110
- children: [mounted && modal === true && /*#__PURE__*/jsxRuntime.jsx(InternalBackdrop.InternalBackdrop, {
1111
- ref: store.context.internalBackdropRef,
1112
- inert: InternalBackdrop.inertValue(!open)
1113
- }), props.children]
1114
- })
1115
- });
1116
- });
1117
- if (process.env.NODE_ENV !== "production") DialogPortal.displayName = "DialogPortal";
1172
+ drawerSwipeVarsRegistered = true;
1173
+ }
1174
+ const stateAttributesMapping = {
1175
+ ...InternalBackdrop.popupStateMapping,
1176
+ ...useButton.transitionStatusMapping,
1177
+ expanded(value) {
1178
+ return value ? {
1179
+ [DrawerPopupDataAttributes.expanded]: ''
1180
+ } : null;
1181
+ },
1182
+ nestedDrawerOpen(value) {
1183
+ return value ? {
1184
+ [DrawerPopupDataAttributes.nestedDrawerOpen]: ''
1185
+ } : null;
1186
+ },
1187
+ nestedDrawerSwiping(value) {
1188
+ return value ? {
1189
+ [DrawerPopupDataAttributes.nestedDrawerSwiping]: ''
1190
+ } : null;
1191
+ },
1192
+ swipeDirection(value) {
1193
+ return value ? {
1194
+ [DrawerPopupDataAttributes.swipeDirection]: value
1195
+ } : null;
1196
+ },
1197
+ swiping(value) {
1198
+ return value ? {
1199
+ [DrawerPopupDataAttributes.swiping]: ''
1200
+ } : null;
1201
+ }
1202
+ };
1118
1203
 
1119
1204
  /**
1120
- * A portal element that moves the popup to a different part of the DOM.
1121
- * By default, the portal element is appended to `<body>`.
1205
+ * A container for the drawer contents.
1122
1206
  * Renders a `<div>` element.
1123
1207
  *
1124
1208
  * Documentation: [Base UI Drawer](https://base-ui.com/react/components/drawer)
1125
1209
  */
1126
- const DrawerPortal = DialogPortal;
1127
-
1128
- function useDialogRoot(params) {
1210
+ const DrawerPopup = /*#__PURE__*/React__namespace.forwardRef(function DrawerPopup(componentProps, forwardedRef) {
1129
1211
  const {
1130
- store,
1131
- parentContext,
1132
- actionsRef
1133
- } = params;
1134
- const open = store.useState('open');
1212
+ className,
1213
+ finalFocus,
1214
+ initialFocus,
1215
+ render,
1216
+ ...elementProps
1217
+ } = componentProps;
1218
+ const {
1219
+ store
1220
+ } = useDialogRootContext();
1221
+ const {
1222
+ swipeDirection,
1223
+ frontmostHeight,
1224
+ hasNestedDrawer,
1225
+ nestedSwiping,
1226
+ nestedSwipeProgressStore,
1227
+ onPopupHeightChange,
1228
+ notifyParentFrontmostHeight,
1229
+ notifyParentHasNestedDrawer
1230
+ } = useDrawerRootContext();
1231
+ const descriptionElementId = store.useState('descriptionElementId');
1135
1232
  const disablePointerDismissal = store.useState('disablePointerDismissal');
1233
+ const floatingRootContext = store.useState('floatingRootContext');
1234
+ const rootPopupProps = store.useState('popupProps');
1136
1235
  const modal = store.useState('modal');
1137
- const popupElement = store.useState('popupElement');
1138
- const {
1139
- openMethod,
1140
- triggerProps
1141
- } = InternalBackdrop.useOpenInteractionType(open);
1142
- useImplicitActiveTrigger(store);
1143
- const {
1144
- forceUnmount
1145
- } = useOpenStateTransitions(open, store);
1146
- const createDialogEventDetails = useButton.useStableCallback(reason => {
1147
- const details = useButton.createChangeEventDetails(reason);
1148
- details.preventUnmountOnClose = () => {
1149
- store.set('preventUnmountingOnClose', true);
1150
- };
1151
- return details;
1152
- });
1153
- const handleImperativeClose = React__namespace.useCallback(() => {
1154
- store.setOpen(false, createDialogEventDetails(useButton.imperativeAction));
1155
- }, [store, createDialogEventDetails]);
1156
- React__namespace.useImperativeHandle(actionsRef, () => ({
1157
- unmount: forceUnmount,
1158
- close: handleImperativeClose
1159
- }), [forceUnmount, handleImperativeClose]);
1160
- const floatingRootContext = useSyncedFloatingRootContext({
1161
- popupStore: store,
1162
- onOpenChange: store.setOpen,
1163
- treatPopupAsFloatingElement: true,
1164
- noEmit: true
1165
- });
1166
- const [ownNestedOpenDialogs, setOwnNestedOpenDialogs] = React__namespace.useState(0);
1167
- const isTopmost = ownNestedOpenDialogs === 0;
1168
- const role = useRole(floatingRootContext);
1169
- const dismiss = InternalBackdrop.useDismiss(floatingRootContext, {
1170
- outsidePressEvent() {
1171
- if (store.context.internalBackdropRef.current || store.context.backdropRef.current) {
1172
- return 'intentional';
1173
- }
1174
- // Ensure `aria-hidden` on outside elements is removed immediately
1175
- // on outside press when trapping focus.
1176
- return {
1177
- mouse: modal === 'trap-focus' ? 'sloppy' : 'intentional',
1178
- touch: 'sloppy'
1179
- };
1180
- },
1181
- outsidePress(event) {
1182
- if (!store.context.outsidePressEnabledRef.current) {
1183
- return false;
1184
- }
1185
-
1186
- // For mouse events, only accept left button (button 0)
1187
- // For touch events, a single touch is equivalent to left button
1188
- if ('button' in event && event.button !== 0) {
1189
- return false;
1190
- }
1191
- if ('touches' in event && event.touches.length !== 1) {
1192
- return false;
1193
- }
1194
- const target = useButton.getTarget(event);
1195
- if (isTopmost && !disablePointerDismissal) {
1196
- const eventTarget = target;
1197
- // Only close if the click occurred on the dialog's owning backdrop.
1198
- // This supports multiple modal dialogs that aren't nested in the React tree:
1199
- // https://github.com/mui/base-ui/issues/1320
1200
- if (modal) {
1201
- return store.context.internalBackdropRef.current || store.context.backdropRef.current ? store.context.internalBackdropRef.current === eventTarget || store.context.backdropRef.current === eventTarget || useButton.contains(eventTarget, popupElement) && !eventTarget?.hasAttribute('data-base-ui-portal') : true;
1202
- }
1203
- return true;
1204
- }
1205
- return false;
1206
- },
1207
- escapeKey: isTopmost
1208
- });
1209
- InternalBackdrop.useScrollLock(open && modal === true, popupElement);
1236
+ const mounted = store.useState('mounted');
1237
+ const nested = store.useState('nested');
1238
+ const nestedOpenDialogCount = store.useState('nestedOpenDialogCount');
1239
+ const transitionStatus = store.useState('transitionStatus');
1240
+ const open = store.useState('open');
1241
+ const openMethod = store.useState('openMethod');
1242
+ const titleElementId = store.useState('titleElementId');
1243
+ const role = store.useState('role');
1244
+ const nestedDrawerOpen = nestedOpenDialogCount > 0;
1245
+ const swipe = useDrawerViewportContext();
1246
+ const swiping = swipe?.swiping ?? false;
1247
+ const swipeStrength = swipe?.swipeStrength ?? null;
1210
1248
  const {
1211
- getReferenceProps,
1212
- getFloatingProps,
1213
- getTriggerProps
1214
- } = InternalBackdrop.useInteractions([role, dismiss]);
1249
+ snapPoints,
1250
+ activeSnapPoint,
1251
+ activeSnapPointOffset
1252
+ } = useDrawerSnapPoints();
1253
+ useDialogPortalContext();
1254
+ const [popupHeight, setPopupHeight] = React__namespace.useState(0);
1255
+ const popupHeightRef = React__namespace.useRef(0);
1256
+ const measureHeight = useButton.useStableCallback(() => {
1257
+ const popupElement = store.context.popupRef.current;
1258
+ if (!popupElement) {
1259
+ return;
1260
+ }
1261
+ const offsetHeight = popupElement.offsetHeight;
1215
1262
 
1216
- // Listen for nested open/close events on this store to maintain the count
1217
- store.useContextCallback('onNestedDialogOpen', ownChildrenCount => {
1218
- setOwnNestedOpenDialogs(ownChildrenCount + 1);
1219
- });
1220
- store.useContextCallback('onNestedDialogClose', () => {
1221
- setOwnNestedOpenDialogs(0);
1263
+ // Only skip while the element is still actually stretched beyond its last measured height.
1264
+ if (popupHeightRef.current > 0 && frontmostHeight > popupHeightRef.current && offsetHeight > popupHeightRef.current) {
1265
+ return;
1266
+ }
1267
+ const keepHeightWhileNested = popupHeightRef.current > 0 && hasNestedDrawer;
1268
+ if (keepHeightWhileNested) {
1269
+ const oldHeight = popupHeightRef.current;
1270
+ setPopupHeight(oldHeight);
1271
+ onPopupHeightChange(oldHeight);
1272
+ return;
1273
+ }
1274
+ const nextHeight = offsetHeight;
1275
+ if (nextHeight === popupHeightRef.current) {
1276
+ return;
1277
+ }
1278
+ popupHeightRef.current = nextHeight;
1279
+ setPopupHeight(nextHeight);
1280
+ onPopupHeightChange(nextHeight);
1222
1281
  });
1223
-
1224
- // Notify parent of our open/close state using parent callbacks, if any
1225
- React__namespace.useEffect(() => {
1226
- if (parentContext?.onNestedDialogOpen && open) {
1227
- parentContext.onNestedDialogOpen(ownNestedOpenDialogs);
1282
+ useButton.useIsoLayoutEffect(() => {
1283
+ if (!mounted) {
1284
+ popupHeightRef.current = 0;
1285
+ setPopupHeight(0);
1286
+ onPopupHeightChange(0);
1287
+ return undefined;
1228
1288
  }
1229
- if (parentContext?.onNestedDialogClose && !open) {
1230
- parentContext.onNestedDialogClose();
1289
+ const popupElement = store.context.popupRef.current;
1290
+ if (!popupElement) {
1291
+ return undefined;
1292
+ }
1293
+ removeCSSVariableInheritance$1();
1294
+ measureHeight();
1295
+ if (typeof ResizeObserver !== 'function') {
1296
+ return undefined;
1231
1297
  }
1298
+ const resizeObserver = new ResizeObserver(measureHeight);
1299
+ resizeObserver.observe(popupElement);
1232
1300
  return () => {
1233
- if (parentContext?.onNestedDialogClose && open) {
1234
- parentContext.onNestedDialogClose();
1301
+ resizeObserver.disconnect();
1302
+ };
1303
+ }, [measureHeight, mounted, nestedDrawerOpen, onPopupHeightChange, store.context.popupRef]);
1304
+ useButton.useIsoLayoutEffect(() => {
1305
+ const popupRef = store.context.popupRef;
1306
+ const syncNestedSwipeProgress = () => {
1307
+ const popupElement = popupRef.current;
1308
+ if (!popupElement) {
1309
+ return;
1310
+ }
1311
+ const progress = nestedSwipeProgressStore.getSnapshot();
1312
+ if (progress > 0) {
1313
+ popupElement.style.setProperty(DrawerBackdropCssVars.swipeProgress, `${progress}`);
1314
+ } else {
1315
+ popupElement.style.setProperty(DrawerBackdropCssVars.swipeProgress, '0');
1235
1316
  }
1236
1317
  };
1237
- }, [open, parentContext, ownNestedOpenDialogs]);
1238
- const activeTriggerProps = React__namespace.useMemo(() => getReferenceProps(triggerProps), [getReferenceProps, triggerProps]);
1239
- const inactiveTriggerProps = React__namespace.useMemo(() => getTriggerProps(triggerProps), [getTriggerProps, triggerProps]);
1240
- const popupProps = React__namespace.useMemo(() => getFloatingProps(), [getFloatingProps]);
1241
- store.useSyncedValues({
1242
- openMethod,
1243
- activeTriggerProps,
1244
- inactiveTriggerProps,
1245
- popupProps,
1246
- floatingRootContext,
1247
- nestedOpenDialogCount: ownNestedOpenDialogs
1248
- });
1249
- }
1250
-
1251
- const selectors = {
1252
- ...popupStoreSelectors,
1253
- modal: InternalBackdrop.createSelector(state => state.modal),
1254
- nested: InternalBackdrop.createSelector(state => state.nested),
1255
- nestedOpenDialogCount: InternalBackdrop.createSelector(state => state.nestedOpenDialogCount),
1256
- disablePointerDismissal: InternalBackdrop.createSelector(state => state.disablePointerDismissal),
1257
- openMethod: InternalBackdrop.createSelector(state => state.openMethod),
1258
- descriptionElementId: InternalBackdrop.createSelector(state => state.descriptionElementId),
1259
- titleElementId: InternalBackdrop.createSelector(state => state.titleElementId),
1260
- viewportElement: InternalBackdrop.createSelector(state => state.viewportElement),
1261
- role: InternalBackdrop.createSelector(state => state.role)
1262
- };
1263
- class DialogStore extends InternalBackdrop.ReactStore {
1264
- constructor(initialState) {
1265
- super(createInitialState(initialState), {
1266
- popupRef: /*#__PURE__*/React__namespace.createRef(),
1267
- backdropRef: /*#__PURE__*/React__namespace.createRef(),
1268
- internalBackdropRef: /*#__PURE__*/React__namespace.createRef(),
1269
- outsidePressEnabledRef: {
1270
- current: true
1271
- },
1272
- triggerElements: new InternalBackdrop.PopupTriggerMap(),
1273
- onOpenChange: undefined,
1274
- onOpenChangeComplete: undefined
1275
- }, selectors);
1276
- }
1277
- setOpen = (nextOpen, eventDetails) => {
1278
- eventDetails.preventUnmountOnClose = () => {
1279
- this.set('preventUnmountingOnClose', true);
1318
+ syncNestedSwipeProgress();
1319
+ const unsubscribe = nestedSwipeProgressStore.subscribe(syncNestedSwipeProgress);
1320
+ return () => {
1321
+ unsubscribe();
1322
+ const popupElement = popupRef.current;
1323
+ if (popupElement) {
1324
+ popupElement.style.setProperty(DrawerBackdropCssVars.swipeProgress, '0');
1325
+ }
1280
1326
  };
1281
- if (!nextOpen && eventDetails.trigger == null && this.state.activeTriggerId != null) {
1282
- // When closing the dialog, pass the old trigger to the onOpenChange event
1283
- // so it's not reset too early (potentially causing focus issues in controlled scenarios).
1284
- eventDetails.trigger = this.state.activeTriggerElement ?? undefined;
1285
- }
1286
- this.context.onOpenChange?.(nextOpen, eventDetails);
1287
- if (eventDetails.isCanceled) {
1288
- return;
1327
+ }, [nestedSwipeProgressStore, store.context.popupRef]);
1328
+ React__namespace.useEffect(() => {
1329
+ if (!open) {
1330
+ return undefined;
1289
1331
  }
1290
- const details = {
1291
- open: nextOpen,
1292
- nativeEvent: eventDetails.event,
1293
- reason: eventDetails.reason,
1294
- nested: this.state.nested
1295
- };
1296
- this.state.floatingRootContext.context.events?.emit('openchange', details);
1297
- const updatedState = {
1298
- open: nextOpen
1299
- };
1300
-
1301
- // If a popup is closing, the `trigger` may be null.
1302
- // We want to keep the previous value so that exit animations are played and focus is returned correctly.
1303
- const newTriggerId = eventDetails.trigger?.id ?? null;
1304
- if (newTriggerId || nextOpen) {
1305
- updatedState.activeTriggerId = newTriggerId;
1306
- updatedState.activeTriggerElement = eventDetails.trigger ?? null;
1332
+ notifyParentFrontmostHeight?.(frontmostHeight);
1333
+ return () => {
1334
+ notifyParentFrontmostHeight?.(0);
1335
+ };
1336
+ }, [frontmostHeight, open, notifyParentFrontmostHeight]);
1337
+ React__namespace.useEffect(() => {
1338
+ if (!notifyParentHasNestedDrawer) {
1339
+ return undefined;
1307
1340
  }
1308
- this.update(updatedState);
1309
- };
1310
- }
1311
- function createInitialState(initialState = {}) {
1312
- return {
1313
- ...createInitialPopupStoreState(),
1314
- modal: true,
1315
- disablePointerDismissal: false,
1316
- popupElement: null,
1317
- viewportElement: null,
1318
- descriptionElementId: undefined,
1319
- titleElementId: undefined,
1320
- openMethod: null,
1321
- nested: false,
1322
- nestedOpenDialogCount: 0,
1323
- role: 'dialog',
1324
- ...initialState
1325
- };
1326
- }
1327
-
1328
- /**
1329
- * Groups all parts of the dialog.
1330
- * Doesn’t render its own HTML element.
1331
- *
1332
- * Documentation: [Base UI Dialog](https://base-ui.com/react/components/dialog)
1333
- */
1334
- function DialogRoot(props) {
1335
- const {
1336
- children,
1337
- open: openProp,
1338
- defaultOpen = false,
1339
- onOpenChange,
1340
- onOpenChangeComplete,
1341
- disablePointerDismissal = false,
1342
- modal = true,
1343
- actionsRef,
1344
- handle,
1345
- triggerId: triggerIdProp,
1346
- defaultTriggerId: defaultTriggerIdProp = null
1347
- } = props;
1348
- const parentDialogRootContext = useDialogRootContext(true);
1349
- const nested = Boolean(parentDialogRootContext);
1350
- const store = useRenderElement.useRefWithInit(() => {
1351
- return handle?.store ?? new DialogStore({
1352
- open: defaultOpen,
1353
- openProp,
1354
- activeTriggerId: defaultTriggerIdProp,
1355
- triggerIdProp,
1356
- modal,
1357
- disablePointerDismissal,
1358
- nested
1359
- });
1360
- }).current;
1361
-
1362
- // Support initially open state when uncontrolled
1363
- InternalBackdrop.useOnFirstRender(() => {
1364
- if (openProp === undefined && store.state.open === false && defaultOpen === true) {
1365
- store.update({
1366
- open: true,
1367
- activeTriggerId: defaultTriggerIdProp
1368
- });
1341
+ const present = open || transitionStatus === 'ending';
1342
+ notifyParentHasNestedDrawer(present);
1343
+ return () => {
1344
+ notifyParentHasNestedDrawer(false);
1345
+ };
1346
+ }, [notifyParentHasNestedDrawer, open, transitionStatus]);
1347
+ useButton.useOpenChangeComplete({
1348
+ open,
1349
+ ref: store.context.popupRef,
1350
+ onComplete() {
1351
+ if (open) {
1352
+ store.context.onOpenChangeComplete?.(true);
1353
+ }
1369
1354
  }
1370
1355
  });
1371
- store.useControlledProp('openProp', openProp);
1372
- store.useControlledProp('triggerIdProp', triggerIdProp);
1373
- store.useSyncedValues({
1374
- disablePointerDismissal,
1356
+ const resolvedInitialFocus = initialFocus === undefined ? store.context.popupRef : initialFocus;
1357
+ const state = {
1358
+ open,
1375
1359
  nested,
1376
- modal
1377
- });
1378
- store.useContextCallback('onOpenChange', onOpenChange);
1379
- store.useContextCallback('onOpenChangeComplete', onOpenChangeComplete);
1380
- const payload = store.useState('payload');
1381
- useDialogRoot({
1382
- store,
1383
- actionsRef,
1384
- parentContext: parentDialogRootContext?.store.context,
1385
- onOpenChange,
1386
- triggerIdProp
1360
+ transitionStatus,
1361
+ expanded: activeSnapPoint === 1,
1362
+ nestedDrawerOpen,
1363
+ nestedDrawerSwiping: nestedSwiping,
1364
+ swipeDirection,
1365
+ swiping
1366
+ };
1367
+ let popupHeightCssVarValue;
1368
+ const shouldUseAutoHeight = !hasNestedDrawer && transitionStatus !== 'ending';
1369
+ if (popupHeight && !shouldUseAutoHeight) {
1370
+ popupHeightCssVarValue = `${popupHeight}px`;
1371
+ }
1372
+ const shouldApplySnapPoints = snapPoints && snapPoints.length > 0 && (swipeDirection === 'down' || swipeDirection === 'up');
1373
+ let snapPointOffsetValue = null;
1374
+ if (shouldApplySnapPoints && activeSnapPointOffset !== null) {
1375
+ snapPointOffsetValue = swipeDirection === 'up' ? -activeSnapPointOffset : activeSnapPointOffset;
1376
+ }
1377
+ let dragStyles = swipe ? swipe.getDragStyles() : useRenderElement.EMPTY_OBJECT;
1378
+ if (shouldApplySnapPoints && swipeDirection === 'down') {
1379
+ const baseOffset = activeSnapPointOffset ?? 0;
1380
+ const movementValue = Number.parseFloat(String(dragStyles[DrawerPopupCssVars.swipeMovementY] ?? 0));
1381
+ const nextOffset = Number.isFinite(movementValue) ? baseOffset + movementValue : baseOffset;
1382
+ const shouldDamp = nextOffset < 0;
1383
+ if (swiping && shouldDamp && Number.isFinite(movementValue)) {
1384
+ const overshoot = Math.abs(nextOffset);
1385
+ const dampedOffset = -Math.sqrt(overshoot);
1386
+ const dampedMovement = dampedOffset - baseOffset;
1387
+ dragStyles = {
1388
+ ...dragStyles,
1389
+ transform: undefined,
1390
+ [DrawerPopupCssVars.swipeMovementY]: `${dampedMovement}px`
1391
+ };
1392
+ } else {
1393
+ dragStyles = {
1394
+ ...dragStyles,
1395
+ transform: undefined
1396
+ };
1397
+ }
1398
+ }
1399
+ const element = useRenderElement.useRenderElement('div', componentProps, {
1400
+ state,
1401
+ props: [rootPopupProps, {
1402
+ 'aria-labelledby': titleElementId,
1403
+ 'aria-describedby': descriptionElementId,
1404
+ role,
1405
+ tabIndex: -1,
1406
+ hidden: !mounted,
1407
+ onKeyDown(event) {
1408
+ if (CSPContext.COMPOSITE_KEYS.has(event.key)) {
1409
+ event.stopPropagation();
1410
+ }
1411
+ },
1412
+ style: {
1413
+ ...dragStyles,
1414
+ [DrawerBackdropCssVars.swipeProgress]: '0',
1415
+ [DrawerPopupCssVars.nestedDrawers]: nestedOpenDialogCount,
1416
+ [DrawerPopupCssVars.height]: popupHeightCssVarValue,
1417
+ [DrawerPopupCssVars.snapPointOffset]: typeof snapPointOffsetValue === 'number' ? `${snapPointOffsetValue}px` : '0px',
1418
+ [DrawerPopupCssVars.frontmostHeight]: frontmostHeight ? `${frontmostHeight}px` : undefined,
1419
+ [DrawerPopupCssVars.swipeStrength]: typeof swipeStrength === 'number' && Number.isFinite(swipeStrength) && swipeStrength > 0 ? `${swipeStrength}` : '1'
1420
+ }
1421
+ }, elementProps],
1422
+ ref: [forwardedRef, store.context.popupRef, store.useStateSetter('popupElement')],
1423
+ stateAttributesMapping
1387
1424
  });
1388
- const contextValue = React__namespace.useMemo(() => ({
1389
- store
1390
- }), [store]);
1391
- return /*#__PURE__*/jsxRuntime.jsx(DialogRootContext.Provider, {
1392
- value: contextValue,
1393
- children: typeof children === 'function' ? children({
1394
- payload
1395
- }) : children
1425
+ return /*#__PURE__*/jsxRuntime.jsx(InternalBackdrop.FloatingFocusManager, {
1426
+ context: floatingRootContext,
1427
+ openInteractionType: openMethod,
1428
+ disabled: !mounted,
1429
+ closeOnFocusOut: !disablePointerDismissal,
1430
+ initialFocus: resolvedInitialFocus,
1431
+ returnFocus: finalFocus,
1432
+ modal: modal !== false,
1433
+ restoreFocus: "popup",
1434
+ children: element
1396
1435
  });
1397
- }
1436
+ });
1437
+ if (process.env.NODE_ENV !== "production") DrawerPopup.displayName = "DrawerPopup";
1398
1438
 
1399
1439
  /**
1400
- * A button that opens the dialog.
1401
- * Renders a `<button>` element.
1440
+ * A portal element that moves the popup to a different part of the DOM.
1441
+ * By default, the portal element is appended to `<body>`.
1442
+ * Renders a `<div>` element.
1402
1443
  *
1403
- * Documentation: [Base UI Dialog](https://base-ui.com/react/components/dialog)
1444
+ * Documentation: [Base UI Drawer](https://base-ui.com/react/components/drawer)
1404
1445
  */
1405
- const DialogTrigger = /*#__PURE__*/React__namespace.forwardRef(function DialogTrigger(componentProps, forwardedRef) {
1406
- const {
1407
- render,
1408
- className,
1409
- disabled = false,
1410
- nativeButton = true,
1411
- id: idProp,
1412
- payload,
1413
- handle,
1414
- ...elementProps
1415
- } = componentProps;
1416
- const dialogRootContext = useDialogRootContext(true);
1417
- const store = handle?.store ?? dialogRootContext?.store;
1418
- if (!store) {
1419
- throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: <Dialog.Trigger> must be used within <Dialog.Root> or provided with a handle.' : useRenderElement.formatErrorMessage(79));
1420
- }
1421
- const thisTriggerId = useButton.useBaseUiId(idProp);
1422
- const floatingContext = store.useState('floatingRootContext');
1423
- const isOpenedByThisTrigger = store.useState('isOpenedByTrigger', thisTriggerId);
1424
- const triggerElementRef = React__namespace.useRef(null);
1425
- const {
1426
- registerTrigger,
1427
- isMountedByThisTrigger
1428
- } = useTriggerDataForwarding(thisTriggerId, triggerElementRef, store, {
1429
- payload
1430
- });
1431
- const {
1432
- getButtonProps,
1433
- buttonRef
1434
- } = useButton.useButton({
1435
- disabled,
1436
- native: nativeButton
1437
- });
1438
- const click = InternalBackdrop.useClick(floatingContext, {
1439
- enabled: floatingContext != null
1440
- });
1441
- const localInteractionProps = InternalBackdrop.useInteractions([click]);
1442
- const state = {
1443
- disabled,
1444
- open: isOpenedByThisTrigger
1445
- };
1446
- const rootTriggerProps = store.useState('triggerProps', isMountedByThisTrigger);
1447
- return useRenderElement.useRenderElement('button', componentProps, {
1448
- state,
1449
- ref: [buttonRef, forwardedRef, registerTrigger, triggerElementRef],
1450
- props: [localInteractionProps.getReferenceProps(), rootTriggerProps, {
1451
- [InternalBackdrop.CLICK_TRIGGER_IDENTIFIER]: '',
1452
- id: thisTriggerId
1453
- }, elementProps, getButtonProps],
1454
- stateAttributesMapping: InternalBackdrop.triggerOpenStateMapping
1455
- });
1456
- });
1457
- if (process.env.NODE_ENV !== "production") DialogTrigger.displayName = "DialogTrigger";
1446
+ const DrawerPortal = DialogPortal;
1458
1447
 
1459
1448
  var _DrawerProviderReport, _DrawerProviderReport2;
1460
1449
  /**
@@ -1766,20 +1755,6 @@ let ScrollAreaScrollbarDataAttributes = /*#__PURE__*/function (ScrollAreaScrollb
1766
1755
  return ScrollAreaScrollbarDataAttributes;
1767
1756
  }({});
1768
1757
 
1769
- const DISABLE_SCROLLBAR_CLASS_NAME = 'base-ui-disable-scrollbar';
1770
- const styleDisableScrollbar = {
1771
- className: DISABLE_SCROLLBAR_CLASS_NAME,
1772
- getElement(nonce) {
1773
- return /*#__PURE__*/jsxRuntime.jsx("style", {
1774
- nonce: nonce,
1775
- href: DISABLE_SCROLLBAR_CLASS_NAME,
1776
- precedence: "base-ui:low",
1777
- children: `.${DISABLE_SCROLLBAR_CLASS_NAME}{scrollbar-width:none}.${DISABLE_SCROLLBAR_CLASS_NAME}::-webkit-scrollbar{display:none}`
1778
- });
1779
- }
1780
- };
1781
- if (process.env.NODE_ENV !== "production") styleDisableScrollbar.getElement.displayName = "styleDisableScrollbar.getElement";
1782
-
1783
1758
  let ScrollAreaRootDataAttributes = /*#__PURE__*/function (ScrollAreaRootDataAttributes) {
1784
1759
  /**
1785
1760
  * Present when the user scrolls inside the scroll area.
@@ -1834,22 +1809,6 @@ const scrollAreaStateAttributesMapping = {
1834
1809
  cornerHidden: () => null
1835
1810
  };
1836
1811
 
1837
- /**
1838
- * @internal
1839
- */
1840
- const CSPContext = /*#__PURE__*/React__namespace.createContext(undefined);
1841
- if (process.env.NODE_ENV !== "production") CSPContext.displayName = "CSPContext";
1842
- const DEFAULT_CSP_CONTEXT_VALUE = {
1843
- disableStyleElements: false
1844
- };
1845
-
1846
- /**
1847
- * @internal
1848
- */
1849
- function useCSPContext() {
1850
- return React__namespace.useContext(CSPContext) ?? DEFAULT_CSP_CONTEXT_VALUE;
1851
- }
1852
-
1853
1812
  const DEFAULT_COORDS = {
1854
1813
  x: 0,
1855
1814
  y: 0
@@ -1889,7 +1848,7 @@ const ScrollAreaRoot = /*#__PURE__*/React__namespace.forwardRef(function ScrollA
1889
1848
  const {
1890
1849
  nonce,
1891
1850
  disableStyleElements
1892
- } = useCSPContext();
1851
+ } = CSPContext.useCSPContext();
1893
1852
  const [hovering, setHovering] = React__namespace.useState(false);
1894
1853
  const [scrollingX, setScrollingX] = React__namespace.useState(false);
1895
1854
  const [scrollingY, setScrollingY] = React__namespace.useState(false);
@@ -2072,7 +2031,7 @@ const ScrollAreaRoot = /*#__PURE__*/React__namespace.forwardRef(function ScrollA
2072
2031
  }), [handlePointerDown, handlePointerMove, handlePointerUp, handleScroll, cornerSize, thumbSize, hasMeasuredScrollbar, touchModality, scrollingX, setScrollingX, scrollingY, setScrollingY, hovering, setHovering, rootId, hiddenState, overflowEdges, state, overflowEdgeThreshold]);
2073
2032
  return /*#__PURE__*/jsxRuntime.jsxs(ScrollAreaRootContext.Provider, {
2074
2033
  value: contextValue,
2075
- children: [!disableStyleElements && styleDisableScrollbar.getElement(nonce), element]
2034
+ children: [!disableStyleElements && CSPContext.styleDisableScrollbar.getElement(nonce), element]
2076
2035
  });
2077
2036
  });
2078
2037
  if (process.env.NODE_ENV !== "production") ScrollAreaRoot.displayName = "ScrollAreaRoot";
@@ -2459,7 +2418,7 @@ const ScrollAreaViewport = /*#__PURE__*/React__namespace.forwardRef(function Scr
2459
2418
  // https://accessibilityinsights.io/info-examples/web/scrollable-region-focusable/
2460
2419
  // Keep non-scrollable viewports out of tab order.
2461
2420
  tabIndex: hiddenState.x && hiddenState.y ? -1 : 0,
2462
- className: styleDisableScrollbar.className,
2421
+ className: CSPContext.styleDisableScrollbar.className,
2463
2422
  style: {
2464
2423
  overflow: 'scroll'
2465
2424
  },
@@ -2532,9 +2491,11 @@ function mergeHiddenState(prevState, nextState) {
2532
2491
  return nextState;
2533
2492
  }
2534
2493
 
2535
- exports.COMPOSITE_KEYS = COMPOSITE_KEYS;
2536
2494
  exports.DRAWER_CONTENT_ATTRIBUTE = DRAWER_CONTENT_ATTRIBUTE;
2495
+ exports.DialogPortal = DialogPortal;
2496
+ exports.DialogRoot = DialogRoot;
2537
2497
  exports.DialogStore = DialogStore;
2498
+ exports.DialogTrigger = DialogTrigger;
2538
2499
  exports.DrawerBackdrop = DrawerBackdrop;
2539
2500
  exports.DrawerBackdropCssVars = DrawerBackdropCssVars;
2540
2501
  exports.DrawerContent = DrawerContent;