@samline/drawer 2.0.4 → 2.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@ function __insertCSS(code) {
10
10
 
11
11
  import * as DialogPrimitive from '@radix-ui/react-dialog';
12
12
  import * as React from 'react';
13
- import React__default, { useLayoutEffect, useEffect, useMemo } from 'react';
13
+ import React__default, { useRef, useLayoutEffect, useEffect, useMemo } from 'react';
14
14
  import { createRoot } from 'react-dom/client';
15
15
 
16
16
  const DEFAULT_OPTIONS = {
@@ -193,30 +193,38 @@ const nonTextInputTypes = new Set([
193
193
  'submit',
194
194
  'reset'
195
195
  ]);
196
- // The number of active usePreventScroll calls. Used to determine whether to revert back to the original page style/scroll position
197
- let preventScrollCount = 0;
198
- let restore;
196
+ const activePreventScrollLocks = new Set();
197
+ let activePreventScrollRestore = null;
198
+ function acquirePreventScrollLock(lockId) {
199
+ activePreventScrollLocks.add(lockId);
200
+ if (activePreventScrollLocks.size === 1 && isIOS()) {
201
+ activePreventScrollRestore = preventScrollMobileSafari();
202
+ }
203
+ }
204
+ function releasePreventScrollLock(lockId) {
205
+ activePreventScrollLocks.delete(lockId);
206
+ if (activePreventScrollLocks.size === 0) {
207
+ activePreventScrollRestore == null ? void 0 : activePreventScrollRestore();
208
+ activePreventScrollRestore = null;
209
+ }
210
+ }
199
211
  /**
200
212
  * Prevents scrolling on the document body on mount, and
201
213
  * restores it on unmount. Also ensures that content does not
202
214
  * shift due to the scrollbars disappearing.
203
215
  */ function usePreventScroll(options = {}) {
204
216
  let { isDisabled } = options;
217
+ const lockIdRef = useRef();
218
+ if (!lockIdRef.current) {
219
+ lockIdRef.current = Symbol('drawer-prevent-scroll-lock');
220
+ }
205
221
  useIsomorphicLayoutEffect(()=>{
206
222
  if (isDisabled) {
207
223
  return;
208
224
  }
209
- preventScrollCount++;
210
- if (preventScrollCount === 1) {
211
- if (isIOS()) {
212
- restore = preventScrollMobileSafari();
213
- }
214
- }
225
+ acquirePreventScrollLock(lockIdRef.current);
215
226
  return ()=>{
216
- preventScrollCount--;
217
- if (preventScrollCount === 0) {
218
- restore == null ? void 0 : restore();
219
- }
227
+ releasePreventScrollLock(lockIdRef.current);
220
228
  };
221
229
  }, [
222
230
  isDisabled
@@ -951,12 +959,23 @@ function useScaleBackground() {
951
959
  const { direction, isOpen, shouldScaleBackground, setBackgroundColorOnScale, noBodyStyles } = useDrawerContext();
952
960
  const timeoutIdRef = React__default.useRef(null);
953
961
  const initialBackgroundColor = useMemo(()=>document.body.style.backgroundColor, []);
962
+ React__default.useEffect(()=>{
963
+ return ()=>{
964
+ if (timeoutIdRef.current !== null) {
965
+ window.clearTimeout(timeoutIdRef.current);
966
+ timeoutIdRef.current = null;
967
+ }
968
+ };
969
+ }, []);
954
970
  function getScale() {
955
971
  return (window.innerWidth - WINDOW_TOP_OFFSET) / window.innerWidth;
956
972
  }
957
973
  React__default.useEffect(()=>{
958
974
  if (isOpen && shouldScaleBackground) {
959
- if (timeoutIdRef.current) clearTimeout(timeoutIdRef.current);
975
+ if (timeoutIdRef.current !== null) {
976
+ clearTimeout(timeoutIdRef.current);
977
+ timeoutIdRef.current = null;
978
+ }
960
979
  const wrapper = document.querySelector('[data-drawer-wrapper]');
961
980
  if (!wrapper) return;
962
981
  chain(setBackgroundColorOnScale && !noBodyStyles ? assignStyle(document.body, {
@@ -984,6 +1003,7 @@ function useScaleBackground() {
984
1003
  } else {
985
1004
  document.body.style.removeProperty('background');
986
1005
  }
1006
+ timeoutIdRef.current = null;
987
1007
  }, TRANSITIONS.DURATION * 1000);
988
1008
  };
989
1009
  }
@@ -995,6 +1015,14 @@ function useScaleBackground() {
995
1015
  }
996
1016
 
997
1017
  let previousBodyPosition = null;
1018
+ const activeBodyPositionLocks = new Set();
1019
+ let bodyPositionTimeoutId = null;
1020
+ function clearBodyPositionTimeout() {
1021
+ if (bodyPositionTimeoutId !== null) {
1022
+ window.clearTimeout(bodyPositionTimeoutId);
1023
+ bodyPositionTimeoutId = null;
1024
+ }
1025
+ }
998
1026
  /**
999
1027
  * This hook is necessary to prevent buggy behavior on iOS devices (need to test on Android).
1000
1028
  * I won't get into too much detail about what bugs it solves, but so far I've found that setting the body to `position: fixed` is the most reliable way to prevent those bugs.
@@ -1002,9 +1030,18 @@ let previousBodyPosition = null;
1002
1030
  */ function usePositionFixed({ isOpen, modal, nested, hasBeenOpened, preventScrollRestoration, noBodyStyles }) {
1003
1031
  const [activeUrl, setActiveUrl] = React__default.useState(()=>typeof window !== 'undefined' ? window.location.href : '');
1004
1032
  const scrollPos = React__default.useRef(0);
1033
+ const lockIdRef = React__default.useRef();
1034
+ if (!lockIdRef.current) {
1035
+ lockIdRef.current = Symbol('drawer-body-position-lock');
1036
+ }
1005
1037
  const setPositionFixed = React__default.useCallback(()=>{
1006
1038
  // All browsers on iOS will return true here.
1007
1039
  if (!isSafari()) return;
1040
+ const lockId = lockIdRef.current;
1041
+ if (activeBodyPositionLocks.has(lockId)) {
1042
+ return;
1043
+ }
1044
+ activeBodyPositionLocks.add(lockId);
1008
1045
  // If previousBodyPosition is already set, don't set it again.
1009
1046
  if (previousBodyPosition === null && isOpen && !noBodyStyles) {
1010
1047
  previousBodyPosition = {
@@ -1023,7 +1060,8 @@ let previousBodyPosition = null;
1023
1060
  right: '0px',
1024
1061
  height: 'auto'
1025
1062
  });
1026
- window.setTimeout(()=>window.requestAnimationFrame(()=>{
1063
+ clearBodyPositionTimeout();
1064
+ bodyPositionTimeoutId = window.setTimeout(()=>window.requestAnimationFrame(()=>{
1027
1065
  // Attempt to check if the bottom bar appeared due to the position change
1028
1066
  const bottomBarHeight = innerHeight - window.innerHeight;
1029
1067
  if (bottomBarHeight && scrollPos.current >= innerHeight) {
@@ -1033,12 +1071,19 @@ let previousBodyPosition = null;
1033
1071
  }), 300);
1034
1072
  }
1035
1073
  }, [
1036
- isOpen
1074
+ isOpen,
1075
+ noBodyStyles
1037
1076
  ]);
1038
1077
  const restorePositionSetting = React__default.useCallback(()=>{
1039
1078
  // All browsers on iOS will return true here.
1040
1079
  if (!isSafari()) return;
1080
+ const lockId = lockIdRef.current;
1081
+ activeBodyPositionLocks.delete(lockId);
1082
+ if (activeBodyPositionLocks.size > 0) {
1083
+ return;
1084
+ }
1041
1085
  if (previousBodyPosition !== null && !noBodyStyles) {
1086
+ clearBodyPositionTimeout();
1042
1087
  // Convert the position from "px" to Int
1043
1088
  const y = -parseInt(document.body.style.top, 10);
1044
1089
  const x = -parseInt(document.body.style.left, 10);
@@ -1054,7 +1099,9 @@ let previousBodyPosition = null;
1054
1099
  previousBodyPosition = null;
1055
1100
  }
1056
1101
  }, [
1057
- activeUrl
1102
+ activeUrl,
1103
+ noBodyStyles,
1104
+ setActiveUrl
1058
1105
  ]);
1059
1106
  React__default.useEffect(()=>{
1060
1107
  function onScroll() {
@@ -1404,8 +1451,15 @@ function getDragPermission({ targetTagName, hasNoDragAttribute, direction, timeS
1404
1451
  };
1405
1452
  }
1406
1453
 
1454
+ const useSafeLayoutEffect$1 = typeof window === 'undefined' ? React__default.useEffect : React__default.useLayoutEffect;
1407
1455
  function Root({ open: openProp, onOpenChange, children, onDrag: onDragProp, onRelease: onReleaseProp, snapPoints, shouldScaleBackground = false, setBackgroundColorOnScale = true, closeThreshold = CLOSE_THRESHOLD, scrollLockTimeout = SCROLL_LOCK_TIMEOUT, dismissible = true, handleOnly = false, fadeFromIndex = snapPoints && snapPoints.length - 1, activeSnapPoint: activeSnapPointProp, setActiveSnapPoint: setActiveSnapPointProp, fixed, modal = true, onClose, nested, noBodyStyles = false, direction = 'bottom', defaultOpen = false, disablePreventScroll = true, snapToSequentialPoint = false, preventScrollRestoration = false, repositionInputs = true, onAnimationEnd, container, autoFocus = false }) {
1408
1456
  var _drawerRef_current, _drawerRef_current1;
1457
+ const animationEndTimeoutRef = React__default.useRef(null);
1458
+ const nonModalPointerEventsRafRef = React__default.useRef(null);
1459
+ const shouldAnimateRafRef = React__default.useRef(null);
1460
+ const snapPointsResetTimeoutRef = React__default.useRef(null);
1461
+ const justReleasedTimeoutRef = React__default.useRef(null);
1462
+ const touchEndHandlerRef = React__default.useRef(null);
1409
1463
  const [isOpen = false, setIsOpen] = useControllableState({
1410
1464
  defaultProp: defaultOpen,
1411
1465
  prop: openProp,
@@ -1414,13 +1468,20 @@ function Root({ open: openProp, onOpenChange, children, onDrag: onDragProp, onRe
1414
1468
  if (!o && !nested) {
1415
1469
  restorePositionSetting();
1416
1470
  }
1417
- setTimeout(()=>{
1471
+ if (animationEndTimeoutRef.current !== null) {
1472
+ window.clearTimeout(animationEndTimeoutRef.current);
1473
+ }
1474
+ animationEndTimeoutRef.current = window.setTimeout(()=>{
1418
1475
  onAnimationEnd == null ? void 0 : onAnimationEnd(o);
1419
1476
  }, TRANSITIONS.DURATION * 1000);
1420
1477
  if (o && !modal) {
1421
1478
  if (typeof window !== 'undefined') {
1422
- window.requestAnimationFrame(()=>{
1479
+ if (nonModalPointerEventsRafRef.current !== null) {
1480
+ window.cancelAnimationFrame(nonModalPointerEventsRafRef.current);
1481
+ }
1482
+ nonModalPointerEventsRafRef.current = window.requestAnimationFrame(()=>{
1423
1483
  document.body.style.pointerEvents = 'auto';
1484
+ nonModalPointerEventsRafRef.current = null;
1424
1485
  });
1425
1486
  }
1426
1487
  }
@@ -1498,19 +1559,23 @@ function Root({ open: openProp, onOpenChange, children, onDrag: onDragProp, onRe
1498
1559
  noBodyStyles,
1499
1560
  autoFocus
1500
1561
  });
1501
- React__default.useEffect(()=>{
1562
+ useSafeLayoutEffect$1(()=>{
1502
1563
  var _drawerRef_current;
1503
1564
  if (!isOpen || !modal || autoFocus || typeof document === 'undefined') {
1504
1565
  return;
1505
1566
  }
1506
1567
  const activeElement = document.activeElement;
1507
- if (!(activeElement instanceof HTMLElement)) {
1568
+ if (!activeElement || activeElement === document.body) {
1508
1569
  return;
1509
1570
  }
1510
- if (((_drawerRef_current = drawerRef.current) == null ? void 0 : _drawerRef_current.contains(activeElement)) || activeElement.closest('[data-drawer]')) {
1571
+ const activeElementNode = activeElement;
1572
+ if (((_drawerRef_current = drawerRef.current) == null ? void 0 : _drawerRef_current.contains(activeElementNode)) || (activeElementNode.closest == null ? void 0 : activeElementNode.closest.call(activeElementNode, '[data-drawer]'))) {
1511
1573
  return;
1512
1574
  }
1513
- activeElement.blur();
1575
+ if (typeof activeElementNode.blur !== 'function') {
1576
+ return;
1577
+ }
1578
+ activeElementNode.blur();
1514
1579
  }, [
1515
1580
  autoFocus,
1516
1581
  isOpen,
@@ -1529,7 +1594,15 @@ function Root({ open: openProp, onOpenChange, children, onDrag: onDragProp, onRe
1529
1594
  dragStartTime.current = new Date();
1530
1595
  // iOS doesn't trigger mouseUp after scrolling so we need to listen to touched in order to disallow dragging
1531
1596
  if (isIOS()) {
1532
- window.addEventListener('touchend', ()=>isAllowedToDrag.current = false, {
1597
+ if (touchEndHandlerRef.current) {
1598
+ window.removeEventListener('touchend', touchEndHandlerRef.current);
1599
+ }
1600
+ const handleTouchEnd = ()=>{
1601
+ isAllowedToDrag.current = false;
1602
+ touchEndHandlerRef.current = null;
1603
+ };
1604
+ touchEndHandlerRef.current = handleTouchEnd;
1605
+ window.addEventListener('touchend', handleTouchEnd, {
1533
1606
  once: true
1534
1607
  });
1535
1608
  }
@@ -1646,9 +1719,15 @@ function Root({ open: openProp, onOpenChange, children, onDrag: onDragProp, onRe
1646
1719
  }
1647
1720
  }
1648
1721
  React__default.useEffect(()=>{
1649
- window.requestAnimationFrame(()=>{
1722
+ shouldAnimateRafRef.current = window.requestAnimationFrame(()=>{
1650
1723
  shouldAnimate.current = true;
1651
1724
  });
1725
+ return ()=>{
1726
+ if (shouldAnimateRafRef.current !== null) {
1727
+ window.cancelAnimationFrame(shouldAnimateRafRef.current);
1728
+ shouldAnimateRafRef.current = null;
1729
+ }
1730
+ };
1652
1731
  }, []);
1653
1732
  React__default.useEffect(()=>{
1654
1733
  var _window_visualViewport;
@@ -1698,10 +1777,14 @@ function Root({ open: openProp, onOpenChange, children, onDrag: onDragProp, onRe
1698
1777
  if (!fromWithin) {
1699
1778
  setIsOpen(false);
1700
1779
  }
1701
- setTimeout(()=>{
1780
+ if (snapPointsResetTimeoutRef.current !== null) {
1781
+ window.clearTimeout(snapPointsResetTimeoutRef.current);
1782
+ }
1783
+ snapPointsResetTimeoutRef.current = window.setTimeout(()=>{
1702
1784
  if (snapPoints) {
1703
1785
  setActiveSnapPoint(snapPoints[0]);
1704
1786
  }
1787
+ snapPointsResetTimeoutRef.current = null;
1705
1788
  }, TRANSITIONS.DURATION * 1000); // seconds to ms
1706
1789
  }
1707
1790
  function resetDrawer() {
@@ -1775,8 +1858,12 @@ function Root({ open: openProp, onOpenChange, children, onDrag: onDragProp, onRe
1775
1858
  if (releaseResult.shouldPreventFocus) {
1776
1859
  // `justReleased` is needed to prevent the drawer from focusing on an input when the drag ends, as it's not the intent most of the time.
1777
1860
  setJustReleased(true);
1778
- setTimeout(()=>{
1861
+ if (justReleasedTimeoutRef.current !== null) {
1862
+ window.clearTimeout(justReleasedTimeoutRef.current);
1863
+ }
1864
+ justReleasedTimeoutRef.current = window.setTimeout(()=>{
1779
1865
  setJustReleased(false);
1866
+ justReleasedTimeoutRef.current = null;
1780
1867
  }, 200);
1781
1868
  }
1782
1869
  if (releaseResult.action === 'close') {
@@ -1853,12 +1940,45 @@ function Root({ open: openProp, onOpenChange, children, onDrag: onDragProp, onRe
1853
1940
  });
1854
1941
  }
1855
1942
  }
1943
+ React__default.useEffect(()=>{
1944
+ return ()=>{
1945
+ if (animationEndTimeoutRef.current !== null) {
1946
+ window.clearTimeout(animationEndTimeoutRef.current);
1947
+ }
1948
+ if (nonModalPointerEventsRafRef.current !== null) {
1949
+ window.cancelAnimationFrame(nonModalPointerEventsRafRef.current);
1950
+ }
1951
+ if (snapPointsResetTimeoutRef.current !== null) {
1952
+ window.clearTimeout(snapPointsResetTimeoutRef.current);
1953
+ }
1954
+ if (justReleasedTimeoutRef.current !== null) {
1955
+ window.clearTimeout(justReleasedTimeoutRef.current);
1956
+ }
1957
+ if (nestedOpenChangeTimer.current) {
1958
+ window.clearTimeout(nestedOpenChangeTimer.current);
1959
+ }
1960
+ if (touchEndHandlerRef.current) {
1961
+ window.removeEventListener('touchend', touchEndHandlerRef.current);
1962
+ touchEndHandlerRef.current = null;
1963
+ }
1964
+ };
1965
+ }, []);
1856
1966
  React__default.useEffect(()=>{
1857
1967
  if (!modal) {
1858
1968
  // Need to do this manually unfortunately
1859
- window.requestAnimationFrame(()=>{
1969
+ if (nonModalPointerEventsRafRef.current !== null) {
1970
+ window.cancelAnimationFrame(nonModalPointerEventsRafRef.current);
1971
+ }
1972
+ nonModalPointerEventsRafRef.current = window.requestAnimationFrame(()=>{
1860
1973
  document.body.style.pointerEvents = 'auto';
1974
+ nonModalPointerEventsRafRef.current = null;
1861
1975
  });
1976
+ return ()=>{
1977
+ if (nonModalPointerEventsRafRef.current !== null) {
1978
+ window.cancelAnimationFrame(nonModalPointerEventsRafRef.current);
1979
+ nonModalPointerEventsRafRef.current = null;
1980
+ }
1981
+ };
1862
1982
  }
1863
1983
  }, [
1864
1984
  modal
@@ -1942,15 +2062,25 @@ const Content = /*#__PURE__*/ React__default.forwardRef(function({ onPointerDown
1942
2062
  const pointerStartRef = React__default.useRef(null);
1943
2063
  const lastKnownPointerEventRef = React__default.useRef(null);
1944
2064
  const wasBeyondThePointRef = React__default.useRef(false);
2065
+ const delayedSnapPointsRafRef = React__default.useRef(null);
1945
2066
  const hasSnapPoints = snapPoints && snapPoints.length > 0;
1946
2067
  useScaleBackground();
1947
2068
  React__default.useEffect(()=>{
1948
2069
  if (hasSnapPoints) {
1949
- window.requestAnimationFrame(()=>{
2070
+ delayedSnapPointsRafRef.current = window.requestAnimationFrame(()=>{
1950
2071
  setDelayedSnapPoints(true);
2072
+ delayedSnapPointsRafRef.current = null;
1951
2073
  });
1952
2074
  }
1953
- }, []);
2075
+ return ()=>{
2076
+ if (delayedSnapPointsRafRef.current !== null) {
2077
+ window.cancelAnimationFrame(delayedSnapPointsRafRef.current);
2078
+ delayedSnapPointsRafRef.current = null;
2079
+ }
2080
+ };
2081
+ }, [
2082
+ hasSnapPoints
2083
+ ]);
1954
2084
  function handleOnPointerUp(event) {
1955
2085
  pointerStartRef.current = null;
1956
2086
  wasBeyondThePointRef.current = false;
@@ -2051,6 +2181,7 @@ const DOUBLE_TAP_TIMEOUT = 120;
2051
2181
  const Handle = /*#__PURE__*/ React__default.forwardRef(function({ preventCycle = false, children, ...rest }, ref) {
2052
2182
  const { closeDrawer, isDragging, snapPoints, activeSnapPoint, setActiveSnapPoint, dismissible, handleOnly, isOpen, onPress, onDrag } = useDrawerContext();
2053
2183
  const closeTimeoutIdRef = React__default.useRef(null);
2184
+ const cycleTimeoutIdRef = React__default.useRef(null);
2054
2185
  const shouldCancelInteractionRef = React__default.useRef(false);
2055
2186
  function handleStartCycle() {
2056
2187
  // Stop if this is the second click of a double click
@@ -2058,7 +2189,7 @@ const Handle = /*#__PURE__*/ React__default.forwardRef(function({ preventCycle =
2058
2189
  handleCancelInteraction();
2059
2190
  return;
2060
2191
  }
2061
- window.setTimeout(()=>{
2192
+ cycleTimeoutIdRef.current = window.setTimeout(()=>{
2062
2193
  handleCycleSnapPoints();
2063
2194
  }, DOUBLE_TAP_TIMEOUT);
2064
2195
  }
@@ -2091,9 +2222,19 @@ const Handle = /*#__PURE__*/ React__default.forwardRef(function({ preventCycle =
2091
2222
  function handleCancelInteraction() {
2092
2223
  if (closeTimeoutIdRef.current) {
2093
2224
  window.clearTimeout(closeTimeoutIdRef.current);
2225
+ closeTimeoutIdRef.current = null;
2226
+ }
2227
+ if (cycleTimeoutIdRef.current) {
2228
+ window.clearTimeout(cycleTimeoutIdRef.current);
2229
+ cycleTimeoutIdRef.current = null;
2094
2230
  }
2095
2231
  shouldCancelInteractionRef.current = false;
2096
2232
  }
2233
+ React__default.useEffect(()=>{
2234
+ return ()=>{
2235
+ handleCancelInteraction();
2236
+ };
2237
+ }, []);
2097
2238
  return /*#__PURE__*/ React__default.createElement("div", {
2098
2239
  onClick: handleStartCycle,
2099
2240
  onPointerCancel: handleCancelInteraction,
@@ -2441,6 +2582,7 @@ function openAncestorChain(parentId) {
2441
2582
  openAncestorChain(nextParentId);
2442
2583
  }
2443
2584
  if (!parentRuntime.controller.getSnapshot().state.isOpen) {
2585
+ releaseHiddenFocusBeforeOpen(parentRuntime.options);
2444
2586
  parentRuntime.controller.setOpen(true);
2445
2587
  notifyOpenStateChange(parentRuntime, true);
2446
2588
  renderVanillaDrawer(parentRuntime.id);
@@ -2460,6 +2602,7 @@ function bindTriggerElement(runtime) {
2460
2602
  }
2461
2603
  const triggerElement = runtime.options.triggerElement;
2462
2604
  const handleClick = ()=>{
2605
+ releaseHiddenFocusBeforeOpen(runtime.options);
2463
2606
  runtime.controller.setOpen(true);
2464
2607
  renderVanillaDrawer(runtime.id);
2465
2608
  };
@@ -2495,6 +2638,16 @@ function notifyOpenStateChange(runtime, open) {
2495
2638
  function canUseDOM() {
2496
2639
  return typeof window !== 'undefined' && typeof document !== 'undefined';
2497
2640
  }
2641
+ function releaseHiddenFocusBeforeOpen(options) {
2642
+ if (!canUseDOM() || options.modal === false || options.autoFocus) {
2643
+ return;
2644
+ }
2645
+ const activeElement = document.activeElement;
2646
+ if (!activeElement || activeElement === document.body || typeof activeElement.blur !== 'function') {
2647
+ return;
2648
+ }
2649
+ activeElement.blur();
2650
+ }
2498
2651
  function getRuntimeDrawerElement(runtime) {
2499
2652
  if (!runtime.element) {
2500
2653
  return null;
@@ -2545,6 +2698,9 @@ function renderVanillaDrawer(id) {
2545
2698
  open: snapshot.state.isOpen,
2546
2699
  onOpenChange: (open)=>{
2547
2700
  const previousOpen = runtime.controller.getSnapshot().state.isOpen;
2701
+ if (open) {
2702
+ releaseHiddenFocusBeforeOpen(runtime.options);
2703
+ }
2548
2704
  runtime.controller.setOpen(open);
2549
2705
  if (previousOpen !== open) {
2550
2706
  notifyOpenStateChange(runtime, open);
@@ -2597,6 +2753,9 @@ function buildVanillaController(id) {
2597
2753
  open
2598
2754
  }).getSnapshot();
2599
2755
  }
2756
+ if (open) {
2757
+ releaseHiddenFocusBeforeOpen(runtime.options);
2758
+ }
2600
2759
  const previousOpen = runtime.controller.getSnapshot().state.isOpen;
2601
2760
  const snapshot = runtime.controller.setOpen(open);
2602
2761
  if (previousOpen !== open) {
@@ -2690,6 +2849,9 @@ function createDrawer(options = {}) {
2690
2849
  notifyOpenStateChange(existing, snapshot.state.isOpen);
2691
2850
  }
2692
2851
  }
2852
+ if (nextOptions.open && !previousOpen) {
2853
+ releaseHiddenFocusBeforeOpen(nextOptions);
2854
+ }
2693
2855
  renderVanillaDrawer(id);
2694
2856
  if (nextOptions.parentId && nextOptions.open) {
2695
2857
  openAncestorChain(nextOptions.parentId);