@react-aria/overlays 3.23.4 → 3.25.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 (69) hide show
  1. package/dist/Overlay.main.js.map +1 -1
  2. package/dist/Overlay.module.js.map +1 -1
  3. package/dist/PortalProvider.main.js +1 -1
  4. package/dist/PortalProvider.main.js.map +1 -1
  5. package/dist/PortalProvider.mjs +1 -1
  6. package/dist/PortalProvider.module.js +1 -1
  7. package/dist/PortalProvider.module.js.map +1 -1
  8. package/dist/ariaHideOutside.main.js +2 -1
  9. package/dist/ariaHideOutside.main.js.map +1 -1
  10. package/dist/ariaHideOutside.mjs +2 -1
  11. package/dist/ariaHideOutside.module.js +2 -1
  12. package/dist/ariaHideOutside.module.js.map +1 -1
  13. package/dist/calculatePosition.main.js +32 -23
  14. package/dist/calculatePosition.main.js.map +1 -1
  15. package/dist/calculatePosition.mjs +32 -23
  16. package/dist/calculatePosition.module.js +32 -23
  17. package/dist/calculatePosition.module.js.map +1 -1
  18. package/dist/types.d.ts +5 -5
  19. package/dist/types.d.ts.map +1 -1
  20. package/dist/useCloseOnScroll.main.js.map +1 -1
  21. package/dist/useCloseOnScroll.module.js.map +1 -1
  22. package/dist/useModal.main.js +1 -1
  23. package/dist/useModal.main.js.map +1 -1
  24. package/dist/useModal.mjs +1 -1
  25. package/dist/useModal.module.js +1 -1
  26. package/dist/useModal.module.js.map +1 -1
  27. package/dist/useModalOverlay.main.js +1 -1
  28. package/dist/useModalOverlay.main.js.map +1 -1
  29. package/dist/useModalOverlay.mjs +1 -1
  30. package/dist/useModalOverlay.module.js +1 -1
  31. package/dist/useModalOverlay.module.js.map +1 -1
  32. package/dist/useOverlay.main.js +2 -2
  33. package/dist/useOverlay.main.js.map +1 -1
  34. package/dist/useOverlay.mjs +2 -2
  35. package/dist/useOverlay.module.js +2 -2
  36. package/dist/useOverlay.module.js.map +1 -1
  37. package/dist/useOverlayPosition.main.js +18 -21
  38. package/dist/useOverlayPosition.main.js.map +1 -1
  39. package/dist/useOverlayPosition.mjs +18 -21
  40. package/dist/useOverlayPosition.module.js +18 -21
  41. package/dist/useOverlayPosition.module.js.map +1 -1
  42. package/dist/useOverlayTrigger.main.js +1 -1
  43. package/dist/useOverlayTrigger.main.js.map +1 -1
  44. package/dist/useOverlayTrigger.mjs +1 -1
  45. package/dist/useOverlayTrigger.module.js +1 -1
  46. package/dist/useOverlayTrigger.module.js.map +1 -1
  47. package/dist/usePopover.main.js +2 -1
  48. package/dist/usePopover.main.js.map +1 -1
  49. package/dist/usePopover.mjs +2 -1
  50. package/dist/usePopover.module.js +2 -1
  51. package/dist/usePopover.module.js.map +1 -1
  52. package/dist/usePreventScroll.main.js +13 -26
  53. package/dist/usePreventScroll.main.js.map +1 -1
  54. package/dist/usePreventScroll.mjs +13 -26
  55. package/dist/usePreventScroll.module.js +13 -26
  56. package/dist/usePreventScroll.module.js.map +1 -1
  57. package/package.json +14 -14
  58. package/src/Overlay.tsx +1 -1
  59. package/src/PortalProvider.tsx +1 -1
  60. package/src/ariaHideOutside.ts +10 -3
  61. package/src/calculatePosition.ts +27 -30
  62. package/src/useCloseOnScroll.ts +2 -2
  63. package/src/useModal.tsx +2 -2
  64. package/src/useModalOverlay.ts +1 -1
  65. package/src/useOverlay.ts +2 -2
  66. package/src/useOverlayPosition.ts +20 -23
  67. package/src/useOverlayTrigger.ts +2 -2
  68. package/src/usePopover.ts +3 -2
  69. package/src/usePreventScroll.ts +16 -32
@@ -11,7 +11,6 @@ import {useLayoutEffect as $7mMvr$useLayoutEffect, isIOS as $7mMvr$isIOS, chain
11
11
  * OF ANY KIND, either express or implied. See the License for the specific language
12
12
  * governing permissions and limitations under the License.
13
13
  */
14
- // @ts-ignore
15
14
  const $49c51c25361d4cd2$var$visualViewport = typeof document !== 'undefined' && window.visualViewport;
16
15
  // HTML input types that do not cause the software keyboard to appear.
17
16
  const $49c51c25361d4cd2$var$nonTextInputTypes = new Set([
@@ -103,31 +102,15 @@ function $49c51c25361d4cd2$var$preventScrollMobileSafari() {
103
102
  // because it must be set before the touchstart event.
104
103
  if (scrollable.scrollHeight === scrollable.clientHeight && scrollable.scrollWidth === scrollable.clientWidth) e.preventDefault();
105
104
  };
106
- let onTouchEnd = (e)=>{
107
- let target = e.target;
108
- // Apply this change if we're not already focused on the target element
109
- if ($49c51c25361d4cd2$var$willOpenKeyboard(target) && target !== document.activeElement) {
110
- e.preventDefault();
111
- setupStyles();
112
- // Apply a transform to trick Safari into thinking the input is at the top of the page
113
- // so it doesn't try to scroll it into view. When tapping on an input, this needs to
114
- // be done before the "focus" event, so we have to focus the element ourselves.
115
- target.style.transform = 'translateY(-2000px)';
116
- target.focus();
117
- requestAnimationFrame(()=>{
118
- target.style.transform = '';
119
- });
120
- }
105
+ let onTouchEnd = ()=>{
121
106
  if (restoreScrollableStyles) restoreScrollableStyles();
122
107
  };
123
108
  let onFocus = (e)=>{
124
109
  let target = e.target;
125
110
  if ($49c51c25361d4cd2$var$willOpenKeyboard(target)) {
126
111
  setupStyles();
127
- // Transform also needs to be applied in the focus event in cases where focus moves
128
- // other than tapping on an input directly, e.g. the next/previous buttons in the
129
- // software keyboard. In these cases, it seems applying the transform in the focus event
130
- // is good enough, whereas when tapping an input, it must be done before the focus event. 🤷‍♂️
112
+ // Apply a transform to trick Safari into thinking the input is at the top of the page
113
+ // so it doesn't try to scroll it into view.
131
114
  target.style.transform = 'translateY(-2000px)';
132
115
  requestAnimationFrame(()=>{
133
116
  target.style.transform = '';
@@ -194,22 +177,26 @@ function $49c51c25361d4cd2$var$setStyle(element, style, value) {
194
177
  }
195
178
  // Adds an event listener to an element, and returns a function to remove it.
196
179
  function $49c51c25361d4cd2$var$addEvent(target, event, handler, options) {
180
+ // internal function, so it's ok to ignore the difficult to fix type error
181
+ // @ts-ignore
197
182
  target.addEventListener(event, handler, options);
198
183
  return ()=>{
184
+ // @ts-ignore
199
185
  target.removeEventListener(event, handler, options);
200
186
  };
201
187
  }
202
188
  function $49c51c25361d4cd2$var$scrollIntoView(target) {
203
189
  let root = document.scrollingElement || document.documentElement;
204
- while(target && target !== root){
190
+ let nextTarget = target;
191
+ while(nextTarget && nextTarget !== root){
205
192
  // Find the parent scrollable element and adjust the scroll position if the target is not already in view.
206
- let scrollable = (0, $7mMvr$getScrollParent)(target);
207
- if (scrollable !== document.documentElement && scrollable !== document.body && scrollable !== target) {
193
+ let scrollable = (0, $7mMvr$getScrollParent)(nextTarget);
194
+ if (scrollable !== document.documentElement && scrollable !== document.body && scrollable !== nextTarget) {
208
195
  let scrollableTop = scrollable.getBoundingClientRect().top;
209
- let targetTop = target.getBoundingClientRect().top;
210
- if (targetTop > scrollableTop + target.clientHeight) scrollable.scrollTop += targetTop - scrollableTop;
196
+ let targetTop = nextTarget.getBoundingClientRect().top;
197
+ if (targetTop > scrollableTop + nextTarget.clientHeight) scrollable.scrollTop += targetTop - scrollableTop;
211
198
  }
212
- target = scrollable.parentElement;
199
+ nextTarget = scrollable.parentElement;
213
200
  }
214
201
  }
215
202
  function $49c51c25361d4cd2$var$willOpenKeyboard(target) {
@@ -1 +1 @@
1
- {"mappings":"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","sources":["packages/@react-aria/overlays/src/usePreventScroll.ts"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {chain, getScrollParent, isIOS, useLayoutEffect} from '@react-aria/utils';\n\ninterface PreventScrollOptions {\n /** Whether the scroll lock is disabled. */\n isDisabled?: boolean\n}\n\n// @ts-ignore\nconst visualViewport = typeof document !== 'undefined' && window.visualViewport;\n\n// HTML input types that do not cause the software keyboard to appear.\nconst nonTextInputTypes = new Set([\n 'checkbox',\n 'radio',\n 'range',\n 'color',\n 'file',\n 'image',\n 'button',\n 'submit',\n 'reset'\n]);\n\n// The number of active usePreventScroll calls. Used to determine whether to revert back to the original page style/scroll position\nlet preventScrollCount = 0;\nlet restore;\n\n/**\n * Prevents scrolling on the document body on mount, and\n * restores it on unmount. Also ensures that content does not\n * shift due to the scrollbars disappearing.\n */\nexport function usePreventScroll(options: PreventScrollOptions = {}) {\n let {isDisabled} = options;\n\n useLayoutEffect(() => {\n if (isDisabled) {\n return;\n }\n\n preventScrollCount++;\n if (preventScrollCount === 1) {\n if (isIOS()) {\n restore = preventScrollMobileSafari();\n } else {\n restore = preventScrollStandard();\n }\n }\n\n return () => {\n preventScrollCount--;\n if (preventScrollCount === 0) {\n restore();\n }\n };\n }, [isDisabled]);\n}\n\n// For most browsers, all we need to do is set `overflow: hidden` on the root element, and\n// add some padding to prevent the page from shifting when the scrollbar is hidden.\nfunction preventScrollStandard() {\n return chain(\n setStyle(document.documentElement, 'paddingRight', `${window.innerWidth - document.documentElement.clientWidth}px`),\n setStyle(document.documentElement, 'overflow', 'hidden')\n );\n}\n\n// Mobile Safari is a whole different beast. Even with overflow: hidden,\n// it still scrolls the page in many situations:\n//\n// 1. When the bottom toolbar and address bar are collapsed, page scrolling is always allowed.\n// 2. When the keyboard is visible, the viewport does not resize. Instead, the keyboard covers part of\n// it, so it becomes scrollable.\n// 3. When tapping on an input, the page always scrolls so that the input is centered in the visual viewport.\n// This may cause even fixed position elements to scroll off the screen.\n// 4. When using the next/previous buttons in the keyboard to navigate between inputs, the whole page always\n// scrolls, even if the input is inside a nested scrollable element that could be scrolled instead.\n//\n// In order to work around these cases, and prevent scrolling without jankiness, we do a few things:\n//\n// 1. Prevent default on `touchmove` events that are not in a scrollable element. This prevents touch scrolling\n// on the window.\n// 2. Set `overscroll-behavior: contain` on nested scrollable regions so they do not scroll the page when at\n// the top or bottom. Work around a bug where this does not work when the element does not actually overflow\n// by preventing default in a `touchmove` event.\n// 3. Prevent default on `touchend` events on input elements and handle focusing the element ourselves.\n// 4. When focusing an input, apply a transform to trick Safari into thinking the input is at the top\n// of the page, which prevents it from scrolling the page. After the input is focused, scroll the element\n// into view ourselves, without scrolling the whole page.\n// 5. Offset the body by the scroll position using a negative margin and scroll to the top. This should appear the\n// same visually, but makes the actual scroll position always zero. This is required to make all of the\n// above work or Safari will still try to scroll the page when focusing an input.\n// 6. As a last resort, handle window scroll events, and scroll back to the top. This can happen when attempting\n// to navigate to an input with the next/previous buttons that's outside a modal.\nfunction preventScrollMobileSafari() {\n let scrollable: Element;\n let restoreScrollableStyles;\n let onTouchStart = (e: TouchEvent) => {\n // Store the nearest scrollable parent element from the element that the user touched.\n scrollable = getScrollParent(e.target as Element, true);\n if (scrollable === document.documentElement && scrollable === document.body) {\n return;\n }\n\n // Prevent scrolling up when at the top and scrolling down when at the bottom\n // of a nested scrollable area, otherwise mobile Safari will start scrolling\n // the window instead.\n if (scrollable instanceof HTMLElement && window.getComputedStyle(scrollable).overscrollBehavior === 'auto') {\n restoreScrollableStyles = setStyle(scrollable, 'overscrollBehavior', 'contain');\n }\n };\n\n let onTouchMove = (e: TouchEvent) => {\n // Prevent scrolling the window.\n if (!scrollable || scrollable === document.documentElement || scrollable === document.body) {\n e.preventDefault();\n return;\n }\n\n // overscroll-behavior should prevent scroll chaining, but currently does not\n // if the element doesn't actually overflow. https://bugs.webkit.org/show_bug.cgi?id=243452\n // This checks that both the width and height do not overflow, otherwise we might\n // block horizontal scrolling too. In that case, adding `touch-action: pan-x` to\n // the element will prevent vertical page scrolling. We can't add that automatically\n // because it must be set before the touchstart event.\n if (scrollable.scrollHeight === scrollable.clientHeight && scrollable.scrollWidth === scrollable.clientWidth) {\n e.preventDefault();\n }\n };\n\n let onTouchEnd = (e: TouchEvent) => {\n let target = e.target as HTMLElement;\n\n // Apply this change if we're not already focused on the target element\n if (willOpenKeyboard(target) && target !== document.activeElement) {\n e.preventDefault();\n setupStyles();\n\n // Apply a transform to trick Safari into thinking the input is at the top of the page\n // so it doesn't try to scroll it into view. When tapping on an input, this needs to\n // be done before the \"focus\" event, so we have to focus the element ourselves.\n target.style.transform = 'translateY(-2000px)';\n target.focus();\n requestAnimationFrame(() => {\n target.style.transform = '';\n });\n }\n\n if (restoreScrollableStyles) {\n restoreScrollableStyles();\n }\n };\n\n let onFocus = (e: FocusEvent) => {\n let target = e.target as HTMLElement;\n if (willOpenKeyboard(target)) {\n setupStyles();\n\n // Transform also needs to be applied in the focus event in cases where focus moves\n // other than tapping on an input directly, e.g. the next/previous buttons in the\n // software keyboard. In these cases, it seems applying the transform in the focus event\n // is good enough, whereas when tapping an input, it must be done before the focus event. 🤷‍♂️\n target.style.transform = 'translateY(-2000px)';\n requestAnimationFrame(() => {\n target.style.transform = '';\n\n // This will have prevented the browser from scrolling the focused element into view,\n // so we need to do this ourselves in a way that doesn't cause the whole page to scroll.\n if (visualViewport) {\n if (visualViewport.height < window.innerHeight) {\n // If the keyboard is already visible, do this after one additional frame\n // to wait for the transform to be removed.\n requestAnimationFrame(() => {\n scrollIntoView(target);\n });\n } else {\n // Otherwise, wait for the visual viewport to resize before scrolling so we can\n // measure the correct position to scroll to.\n visualViewport.addEventListener('resize', () => scrollIntoView(target), {once: true});\n }\n }\n });\n }\n };\n\n let restoreStyles = null;\n let setupStyles = () => {\n if (restoreStyles) {\n return;\n }\n\n let onWindowScroll = () => {\n // Last resort. If the window scrolled, scroll it back to the top.\n // It should always be at the top because the body will have a negative margin (see below).\n window.scrollTo(0, 0);\n };\n\n // Record the original scroll position so we can restore it.\n // Then apply a negative margin to the body to offset it by the scroll position. This will\n // enable us to scroll the window to the top, which is required for the rest of this to work.\n let scrollX = window.pageXOffset;\n let scrollY = window.pageYOffset;\n\n restoreStyles = chain(\n addEvent(window, 'scroll', onWindowScroll),\n setStyle(document.documentElement, 'paddingRight', `${window.innerWidth - document.documentElement.clientWidth}px`),\n setStyle(document.documentElement, 'overflow', 'hidden'),\n setStyle(document.body, 'marginTop', `-${scrollY}px`),\n () => {\n window.scrollTo(scrollX, scrollY);\n }\n );\n\n // Scroll to the top. The negative margin on the body will make this appear the same.\n window.scrollTo(0, 0);\n };\n\n let removeEvents = chain(\n addEvent(document, 'touchstart', onTouchStart, {passive: false, capture: true}),\n addEvent(document, 'touchmove', onTouchMove, {passive: false, capture: true}),\n addEvent(document, 'touchend', onTouchEnd, {passive: false, capture: true}),\n addEvent(document, 'focus', onFocus, true)\n );\n\n return () => {\n // Restore styles and scroll the page back to where it was.\n restoreScrollableStyles?.();\n restoreStyles?.();\n removeEvents();\n };\n}\n\n// Sets a CSS property on an element, and returns a function to revert it to the previous value.\nfunction setStyle(element: HTMLElement, style: string, value: string) {\n let cur = element.style[style];\n element.style[style] = value;\n\n return () => {\n element.style[style] = cur;\n };\n}\n\n// Adds an event listener to an element, and returns a function to remove it.\nfunction addEvent<K extends keyof GlobalEventHandlersEventMap>(\n target: EventTarget,\n event: K,\n handler: (this: Document, ev: GlobalEventHandlersEventMap[K]) => any,\n options?: boolean | AddEventListenerOptions\n) {\n target.addEventListener(event, handler, options);\n return () => {\n target.removeEventListener(event, handler, options);\n };\n}\n\nfunction scrollIntoView(target: Element) {\n let root = document.scrollingElement || document.documentElement;\n while (target && target !== root) {\n // Find the parent scrollable element and adjust the scroll position if the target is not already in view.\n let scrollable = getScrollParent(target);\n if (scrollable !== document.documentElement && scrollable !== document.body && scrollable !== target) {\n let scrollableTop = scrollable.getBoundingClientRect().top;\n let targetTop = target.getBoundingClientRect().top;\n if (targetTop > scrollableTop + target.clientHeight) {\n scrollable.scrollTop += targetTop - scrollableTop;\n }\n }\n\n target = scrollable.parentElement;\n }\n}\n\nfunction willOpenKeyboard(target: Element) {\n return (\n (target instanceof HTMLInputElement && !nonTextInputTypes.has(target.type)) ||\n target instanceof HTMLTextAreaElement ||\n (target instanceof HTMLElement && target.isContentEditable)\n );\n}\n"],"names":[],"version":3,"file":"usePreventScroll.module.js.map"}
1
+ {"mappings":";;AAAA;;;;;;;;;;CAUC;AASD,MAAM,uCAAiB,OAAO,aAAa,eAAe,OAAO,cAAc;AAE/E,sEAAsE;AACtE,MAAM,0CAAoB,IAAI,IAAI;IAChC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD;AAED,mIAAmI;AACnI,IAAI,2CAAqB;AACzB,IAAI;AAOG,SAAS,0CAAiB,UAAgC,CAAC,CAAC;IACjE,IAAI,cAAC,UAAU,EAAC,GAAG;IAEnB,CAAA,GAAA,sBAAc,EAAE;QACd,IAAI,YACF;QAGF;QACA,IAAI,6CAAuB;YACzB,IAAI,CAAA,GAAA,YAAI,KACN,gCAAU;iBAEV,gCAAU;;QAId,OAAO;YACL;YACA,IAAI,6CAAuB,GACzB;QAEJ;IACF,GAAG;QAAC;KAAW;AACjB;AAEA,0FAA0F;AAC1F,mFAAmF;AACnF,SAAS;IACP,OAAO,CAAA,GAAA,YAAI,EACT,+BAAS,SAAS,eAAe,EAAE,gBAAgB,GAAG,OAAO,UAAU,GAAG,SAAS,eAAe,CAAC,WAAW,CAAC,EAAE,CAAC,GAClH,+BAAS,SAAS,eAAe,EAAE,YAAY;AAEnD;AAEA,wEAAwE;AACxE,gDAAgD;AAChD,EAAE;AACF,8FAA8F;AAC9F,sGAAsG;AACtG,mCAAmC;AACnC,6GAA6G;AAC7G,2EAA2E;AAC3E,4GAA4G;AAC5G,sGAAsG;AACtG,EAAE;AACF,oGAAoG;AACpG,EAAE;AACF,+GAA+G;AAC/G,oBAAoB;AACpB,4GAA4G;AAC5G,+GAA+G;AAC/G,mDAAmD;AACnD,uGAAuG;AACvG,qGAAqG;AACrG,4GAA4G;AAC5G,4DAA4D;AAC5D,kHAAkH;AAClH,0GAA0G;AAC1G,oFAAoF;AACpF,gHAAgH;AAChH,oFAAoF;AACpF,SAAS;IACP,IAAI;IACJ,IAAI;IACJ,IAAI,eAAe,CAAC;QAClB,sFAAsF;QACtF,aAAa,CAAA,GAAA,sBAAc,EAAE,EAAE,MAAM,EAAa;QAClD,IAAI,eAAe,SAAS,eAAe,IAAI,eAAe,SAAS,IAAI,EACzE;QAGF,6EAA6E;QAC7E,4EAA4E;QAC5E,sBAAsB;QACtB,IAAI,sBAAsB,eAAe,OAAO,gBAAgB,CAAC,YAAY,kBAAkB,KAAK,QAClG,0BAA0B,+BAAS,YAAY,sBAAsB;IAEzE;IAEA,IAAI,cAAc,CAAC;QACjB,gCAAgC;QAChC,IAAI,CAAC,cAAc,eAAe,SAAS,eAAe,IAAI,eAAe,SAAS,IAAI,EAAE;YAC1F,EAAE,cAAc;YAChB;QACF;QAEA,6EAA6E;QAC7E,2FAA2F;QAC3F,iFAAiF;QACjF,gFAAgF;QAChF,oFAAoF;QACpF,sDAAsD;QACtD,IAAI,WAAW,YAAY,KAAK,WAAW,YAAY,IAAI,WAAW,WAAW,KAAK,WAAW,WAAW,EAC1G,EAAE,cAAc;IAEpB;IAEA,IAAI,aAAa;QACf,IAAI,yBACF;IAEJ;IAEA,IAAI,UAAU,CAAC;QACb,IAAI,SAAS,EAAE,MAAM;QACrB,IAAI,uCAAiB,SAAS;YAC5B;YAEA,sFAAsF;YACtF,4CAA4C;YAC5C,OAAO,KAAK,CAAC,SAAS,GAAG;YACzB,sBAAsB;gBACpB,OAAO,KAAK,CAAC,SAAS,GAAG;gBAEzB,qFAAqF;gBACrF,wFAAwF;gBACxF,IAAI;oBACF,IAAI,qCAAe,MAAM,GAAG,OAAO,WAAW,EAC5C,yEAAyE;oBACzE,2CAA2C;oBAC3C,sBAAsB;wBACpB,qCAAe;oBACjB;yBAEA,+EAA+E;oBAC/E,6CAA6C;oBAC7C,qCAAe,gBAAgB,CAAC,UAAU,IAAM,qCAAe,SAAS;wBAAC,MAAM;oBAAI;;YAGzF;QACF;IACF;IAEA,IAAI,gBAAqC;IACzC,IAAI,cAAc;QAChB,IAAI,eACF;QAGF,IAAI,iBAAiB;YACnB,kEAAkE;YAClE,2FAA2F;YAC3F,OAAO,QAAQ,CAAC,GAAG;QACrB;QAEA,4DAA4D;QAC5D,0FAA0F;QAC1F,6FAA6F;QAC7F,IAAI,UAAU,OAAO,WAAW;QAChC,IAAI,UAAU,OAAO,WAAW;QAEhC,gBAAgB,CAAA,GAAA,YAAI,EAClB,+BAAS,QAAQ,UAAU,iBAC3B,+BAAS,SAAS,eAAe,EAAE,gBAAgB,GAAG,OAAO,UAAU,GAAG,SAAS,eAAe,CAAC,WAAW,CAAC,EAAE,CAAC,GAClH,+BAAS,SAAS,eAAe,EAAE,YAAY,WAC/C,+BAAS,SAAS,IAAI,EAAE,aAAa,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,GACpD;YACE,OAAO,QAAQ,CAAC,SAAS;QAC3B;QAGF,qFAAqF;QACrF,OAAO,QAAQ,CAAC,GAAG;IACrB;IAEA,IAAI,eAAe,CAAA,GAAA,YAAI,EACrB,+BAAS,UAAU,cAAc,cAAc;QAAC,SAAS;QAAO,SAAS;IAAI,IAC7E,+BAAS,UAAU,aAAa,aAAa;QAAC,SAAS;QAAO,SAAS;IAAI,IAC3E,+BAAS,UAAU,YAAY,YAAY;QAAC,SAAS;QAAO,SAAS;IAAI,IACzE,+BAAS,UAAU,SAAS,SAAS;IAGvC,OAAO;QACL,2DAA2D;QAC3D,oCAAA,8CAAA;QACA,0BAAA,oCAAA;QACA;IACF;AACF;AAEA,gGAAgG;AAChG,SAAS,+BAAS,OAAoB,EAAE,KAAa,EAAE,KAAa;IAClE,IAAI,MAAM,QAAQ,KAAK,CAAC,MAAM;IAC9B,QAAQ,KAAK,CAAC,MAAM,GAAG;IAEvB,OAAO;QACL,QAAQ,KAAK,CAAC,MAAM,GAAG;IACzB;AACF;AAEA,6EAA6E;AAC7E,SAAS,+BACP,MAAyB,EACzB,KAAQ,EACR,OAA6E,EAC7E,OAA2C;IAE3C,0EAA0E;IAC1E,aAAa;IACb,OAAO,gBAAgB,CAAC,OAAO,SAAS;IACxC,OAAO;QACL,aAAa;QACb,OAAO,mBAAmB,CAAC,OAAO,SAAS;IAC7C;AACF;AAEA,SAAS,qCAAe,MAAe;IACrC,IAAI,OAAO,SAAS,gBAAgB,IAAI,SAAS,eAAe;IAChE,IAAI,aAA6B;IACjC,MAAO,cAAc,eAAe,KAAM;QACxC,0GAA0G;QAC1G,IAAI,aAAa,CAAA,GAAA,sBAAc,EAAE;QACjC,IAAI,eAAe,SAAS,eAAe,IAAI,eAAe,SAAS,IAAI,IAAI,eAAe,YAAY;YACxG,IAAI,gBAAgB,WAAW,qBAAqB,GAAG,GAAG;YAC1D,IAAI,YAAY,WAAW,qBAAqB,GAAG,GAAG;YACtD,IAAI,YAAY,gBAAgB,WAAW,YAAY,EACrD,WAAW,SAAS,IAAI,YAAY;QAExC;QAEA,aAAa,WAAW,aAAa;IACvC;AACF;AAEA,SAAS,uCAAiB,MAAe;IACvC,OACE,AAAC,kBAAkB,oBAAoB,CAAC,wCAAkB,GAAG,CAAC,OAAO,IAAI,KACzE,kBAAkB,uBACjB,kBAAkB,eAAe,OAAO,iBAAiB;AAE9D","sources":["packages/@react-aria/overlays/src/usePreventScroll.ts"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {chain, getScrollParent, isIOS, useLayoutEffect} from '@react-aria/utils';\n\ninterface PreventScrollOptions {\n /** Whether the scroll lock is disabled. */\n isDisabled?: boolean\n}\n\nconst visualViewport = typeof document !== 'undefined' && window.visualViewport;\n\n// HTML input types that do not cause the software keyboard to appear.\nconst nonTextInputTypes = new Set([\n 'checkbox',\n 'radio',\n 'range',\n 'color',\n 'file',\n 'image',\n 'button',\n 'submit',\n 'reset'\n]);\n\n// The number of active usePreventScroll calls. Used to determine whether to revert back to the original page style/scroll position\nlet preventScrollCount = 0;\nlet restore;\n\n/**\n * Prevents scrolling on the document body on mount, and\n * restores it on unmount. Also ensures that content does not\n * shift due to the scrollbars disappearing.\n */\nexport function usePreventScroll(options: PreventScrollOptions = {}) {\n let {isDisabled} = options;\n\n useLayoutEffect(() => {\n if (isDisabled) {\n return;\n }\n\n preventScrollCount++;\n if (preventScrollCount === 1) {\n if (isIOS()) {\n restore = preventScrollMobileSafari();\n } else {\n restore = preventScrollStandard();\n }\n }\n\n return () => {\n preventScrollCount--;\n if (preventScrollCount === 0) {\n restore();\n }\n };\n }, [isDisabled]);\n}\n\n// For most browsers, all we need to do is set `overflow: hidden` on the root element, and\n// add some padding to prevent the page from shifting when the scrollbar is hidden.\nfunction preventScrollStandard() {\n return chain(\n setStyle(document.documentElement, 'paddingRight', `${window.innerWidth - document.documentElement.clientWidth}px`),\n setStyle(document.documentElement, 'overflow', 'hidden')\n );\n}\n\n// Mobile Safari is a whole different beast. Even with overflow: hidden,\n// it still scrolls the page in many situations:\n//\n// 1. When the bottom toolbar and address bar are collapsed, page scrolling is always allowed.\n// 2. When the keyboard is visible, the viewport does not resize. Instead, the keyboard covers part of\n// it, so it becomes scrollable.\n// 3. When tapping on an input, the page always scrolls so that the input is centered in the visual viewport.\n// This may cause even fixed position elements to scroll off the screen.\n// 4. When using the next/previous buttons in the keyboard to navigate between inputs, the whole page always\n// scrolls, even if the input is inside a nested scrollable element that could be scrolled instead.\n//\n// In order to work around these cases, and prevent scrolling without jankiness, we do a few things:\n//\n// 1. Prevent default on `touchmove` events that are not in a scrollable element. This prevents touch scrolling\n// on the window.\n// 2. Set `overscroll-behavior: contain` on nested scrollable regions so they do not scroll the page when at\n// the top or bottom. Work around a bug where this does not work when the element does not actually overflow\n// by preventing default in a `touchmove` event.\n// 3. Prevent default on `touchend` events on input elements and handle focusing the element ourselves.\n// 4. When focusing an input, apply a transform to trick Safari into thinking the input is at the top\n// of the page, which prevents it from scrolling the page. After the input is focused, scroll the element\n// into view ourselves, without scrolling the whole page.\n// 5. Offset the body by the scroll position using a negative margin and scroll to the top. This should appear the\n// same visually, but makes the actual scroll position always zero. This is required to make all of the\n// above work or Safari will still try to scroll the page when focusing an input.\n// 6. As a last resort, handle window scroll events, and scroll back to the top. This can happen when attempting\n// to navigate to an input with the next/previous buttons that's outside a modal.\nfunction preventScrollMobileSafari() {\n let scrollable: Element;\n let restoreScrollableStyles;\n let onTouchStart = (e: TouchEvent) => {\n // Store the nearest scrollable parent element from the element that the user touched.\n scrollable = getScrollParent(e.target as Element, true);\n if (scrollable === document.documentElement && scrollable === document.body) {\n return;\n }\n\n // Prevent scrolling up when at the top and scrolling down when at the bottom\n // of a nested scrollable area, otherwise mobile Safari will start scrolling\n // the window instead.\n if (scrollable instanceof HTMLElement && window.getComputedStyle(scrollable).overscrollBehavior === 'auto') {\n restoreScrollableStyles = setStyle(scrollable, 'overscrollBehavior', 'contain');\n }\n };\n\n let onTouchMove = (e: TouchEvent) => {\n // Prevent scrolling the window.\n if (!scrollable || scrollable === document.documentElement || scrollable === document.body) {\n e.preventDefault();\n return;\n }\n\n // overscroll-behavior should prevent scroll chaining, but currently does not\n // if the element doesn't actually overflow. https://bugs.webkit.org/show_bug.cgi?id=243452\n // This checks that both the width and height do not overflow, otherwise we might\n // block horizontal scrolling too. In that case, adding `touch-action: pan-x` to\n // the element will prevent vertical page scrolling. We can't add that automatically\n // because it must be set before the touchstart event.\n if (scrollable.scrollHeight === scrollable.clientHeight && scrollable.scrollWidth === scrollable.clientWidth) {\n e.preventDefault();\n }\n };\n\n let onTouchEnd = () => {\n if (restoreScrollableStyles) {\n restoreScrollableStyles();\n }\n };\n\n let onFocus = (e: FocusEvent) => {\n let target = e.target as HTMLElement;\n if (willOpenKeyboard(target)) {\n setupStyles();\n\n // Apply a transform to trick Safari into thinking the input is at the top of the page\n // so it doesn't try to scroll it into view.\n target.style.transform = 'translateY(-2000px)';\n requestAnimationFrame(() => {\n target.style.transform = '';\n\n // This will have prevented the browser from scrolling the focused element into view,\n // so we need to do this ourselves in a way that doesn't cause the whole page to scroll.\n if (visualViewport) {\n if (visualViewport.height < window.innerHeight) {\n // If the keyboard is already visible, do this after one additional frame\n // to wait for the transform to be removed.\n requestAnimationFrame(() => {\n scrollIntoView(target);\n });\n } else {\n // Otherwise, wait for the visual viewport to resize before scrolling so we can\n // measure the correct position to scroll to.\n visualViewport.addEventListener('resize', () => scrollIntoView(target), {once: true});\n }\n }\n });\n }\n };\n\n let restoreStyles: null | (() => void) = null;\n let setupStyles = () => {\n if (restoreStyles) {\n return;\n }\n\n let onWindowScroll = () => {\n // Last resort. If the window scrolled, scroll it back to the top.\n // It should always be at the top because the body will have a negative margin (see below).\n window.scrollTo(0, 0);\n };\n\n // Record the original scroll position so we can restore it.\n // Then apply a negative margin to the body to offset it by the scroll position. This will\n // enable us to scroll the window to the top, which is required for the rest of this to work.\n let scrollX = window.pageXOffset;\n let scrollY = window.pageYOffset;\n\n restoreStyles = chain(\n addEvent(window, 'scroll', onWindowScroll),\n setStyle(document.documentElement, 'paddingRight', `${window.innerWidth - document.documentElement.clientWidth}px`),\n setStyle(document.documentElement, 'overflow', 'hidden'),\n setStyle(document.body, 'marginTop', `-${scrollY}px`),\n () => {\n window.scrollTo(scrollX, scrollY);\n }\n );\n\n // Scroll to the top. The negative margin on the body will make this appear the same.\n window.scrollTo(0, 0);\n };\n\n let removeEvents = chain(\n addEvent(document, 'touchstart', onTouchStart, {passive: false, capture: true}),\n addEvent(document, 'touchmove', onTouchMove, {passive: false, capture: true}),\n addEvent(document, 'touchend', onTouchEnd, {passive: false, capture: true}),\n addEvent(document, 'focus', onFocus, true)\n );\n\n return () => {\n // Restore styles and scroll the page back to where it was.\n restoreScrollableStyles?.();\n restoreStyles?.();\n removeEvents();\n };\n}\n\n// Sets a CSS property on an element, and returns a function to revert it to the previous value.\nfunction setStyle(element: HTMLElement, style: string, value: string) {\n let cur = element.style[style];\n element.style[style] = value;\n\n return () => {\n element.style[style] = cur;\n };\n}\n\n// Adds an event listener to an element, and returns a function to remove it.\nfunction addEvent<K extends keyof GlobalEventHandlersEventMap>(\n target: Document | Window,\n event: K,\n handler: (this: Document | Window, ev: GlobalEventHandlersEventMap[K]) => any,\n options?: boolean | AddEventListenerOptions\n) {\n // internal function, so it's ok to ignore the difficult to fix type error\n // @ts-ignore\n target.addEventListener(event, handler, options);\n return () => {\n // @ts-ignore\n target.removeEventListener(event, handler, options);\n };\n}\n\nfunction scrollIntoView(target: Element) {\n let root = document.scrollingElement || document.documentElement;\n let nextTarget: Element | null = target;\n while (nextTarget && nextTarget !== root) {\n // Find the parent scrollable element and adjust the scroll position if the target is not already in view.\n let scrollable = getScrollParent(nextTarget);\n if (scrollable !== document.documentElement && scrollable !== document.body && scrollable !== nextTarget) {\n let scrollableTop = scrollable.getBoundingClientRect().top;\n let targetTop = nextTarget.getBoundingClientRect().top;\n if (targetTop > scrollableTop + nextTarget.clientHeight) {\n scrollable.scrollTop += targetTop - scrollableTop;\n }\n }\n\n nextTarget = scrollable.parentElement;\n }\n}\n\nfunction willOpenKeyboard(target: Element) {\n return (\n (target instanceof HTMLInputElement && !nonTextInputTypes.has(target.type)) ||\n target instanceof HTMLTextAreaElement ||\n (target instanceof HTMLElement && target.isContentEditable)\n );\n}\n"],"names":[],"version":3,"file":"usePreventScroll.module.js.map"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-aria/overlays",
3
- "version": "3.23.4",
3
+ "version": "3.25.0",
4
4
  "description": "Spectrum UI components in React",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/main.js",
@@ -22,24 +22,24 @@
22
22
  "url": "https://github.com/adobe/react-spectrum"
23
23
  },
24
24
  "dependencies": {
25
- "@react-aria/focus": "^3.18.4",
26
- "@react-aria/i18n": "^3.12.3",
27
- "@react-aria/interactions": "^3.22.4",
28
- "@react-aria/ssr": "^3.9.6",
29
- "@react-aria/utils": "^3.25.3",
30
- "@react-aria/visually-hidden": "^3.8.17",
31
- "@react-stately/overlays": "^3.6.11",
32
- "@react-types/button": "^3.10.0",
33
- "@react-types/overlays": "^3.8.10",
34
- "@react-types/shared": "^3.25.0",
25
+ "@react-aria/focus": "^3.19.1",
26
+ "@react-aria/i18n": "^3.12.5",
27
+ "@react-aria/interactions": "^3.23.0",
28
+ "@react-aria/ssr": "^3.9.7",
29
+ "@react-aria/utils": "^3.27.0",
30
+ "@react-aria/visually-hidden": "^3.8.19",
31
+ "@react-stately/overlays": "^3.6.13",
32
+ "@react-types/button": "^3.10.2",
33
+ "@react-types/overlays": "^3.8.12",
34
+ "@react-types/shared": "^3.27.0",
35
35
  "@swc/helpers": "^0.5.0"
36
36
  },
37
37
  "peerDependencies": {
38
- "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0",
39
- "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0"
38
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
39
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
40
40
  },
41
41
  "publishConfig": {
42
42
  "access": "public"
43
43
  },
44
- "gitHead": "8e0a28d188cdbdbd2b32296fa034b1b02ddde229"
44
+ "gitHead": "09e7f44bebdc9d89122926b2b439a0a38a2814ea"
45
45
  }
package/src/Overlay.tsx CHANGED
@@ -39,7 +39,7 @@ export interface OverlayProps {
39
39
  isExiting?: boolean
40
40
  }
41
41
 
42
- export const OverlayContext = React.createContext(null);
42
+ export const OverlayContext = React.createContext<{contain: boolean, setContain: React.Dispatch<React.SetStateAction<boolean>>} | null>(null);
43
43
 
44
44
  /**
45
45
  * A container which renders an overlay such as a popover or modal in a portal,
@@ -23,7 +23,7 @@ export function UNSTABLE_PortalProvider(props: PortalProviderProps & {children:
23
23
  let {getContainer} = props;
24
24
  let {getContainer: ctxGetContainer} = useUNSTABLE_PortalContext();
25
25
  return (
26
- <PortalContext.Provider value={{getContainer: getContainer === null ? null : getContainer ?? ctxGetContainer}}>
26
+ <PortalContext.Provider value={{getContainer: getContainer === null ? undefined : getContainer ?? ctxGetContainer}}>
27
27
  {props.children}
28
28
  </PortalContext.Provider>
29
29
  );
@@ -13,7 +13,11 @@
13
13
  // Keeps a ref count of all hidden elements. Added to when hiding an element, and
14
14
  // subtracted from when showing it again. When it reaches zero, aria-hidden is removed.
15
15
  let refCountMap = new WeakMap<Element, number>();
16
- let observerStack = [];
16
+ interface ObserverWrapper {
17
+ observe: () => void,
18
+ disconnect: () => void
19
+ }
20
+ let observerStack: Array<ObserverWrapper> = [];
17
21
 
18
22
  /**
19
23
  * Hides all elements in the DOM outside the given targets from screen readers using aria-hidden,
@@ -40,7 +44,7 @@ export function ariaHideOutside(targets: Element[], root = document.body) {
40
44
  // For that case we want to hide the cells inside as well (https://bugs.webkit.org/show_bug.cgi?id=222623).
41
45
  if (
42
46
  visibleNodes.has(node) ||
43
- (hiddenNodes.has(node.parentElement) && node.parentElement.getAttribute('role') !== 'row')
47
+ (node.parentElement && hiddenNodes.has(node.parentElement) && node.parentElement.getAttribute('role') !== 'row')
44
48
  ) {
45
49
  return NodeFilter.FILTER_REJECT;
46
50
  }
@@ -133,7 +137,7 @@ export function ariaHideOutside(targets: Element[], root = document.body) {
133
137
 
134
138
  observer.observe(root, {childList: true, subtree: true});
135
139
 
136
- let observerWrapper = {
140
+ let observerWrapper: ObserverWrapper = {
137
141
  observe() {
138
142
  observer.observe(root, {childList: true, subtree: true});
139
143
  },
@@ -149,6 +153,9 @@ export function ariaHideOutside(targets: Element[], root = document.body) {
149
153
 
150
154
  for (let node of hiddenNodes) {
151
155
  let count = refCountMap.get(node);
156
+ if (count == null) {
157
+ continue;
158
+ }
152
159
  if (count === 1) {
153
160
  node.removeAttribute('aria-hidden');
154
161
  refCountMap.delete(node);
@@ -64,10 +64,10 @@ interface PositionOpts {
64
64
  type HeightGrowthDirection = 'top' | 'bottom';
65
65
 
66
66
  export interface PositionResult {
67
- position?: Position,
67
+ position: Position,
68
68
  arrowOffsetLeft?: number,
69
69
  arrowOffsetTop?: number,
70
- maxHeight?: number,
70
+ maxHeight: number,
71
71
  placement: PlacementAxis
72
72
  }
73
73
 
@@ -102,13 +102,12 @@ const TOTAL_SIZE = {
102
102
 
103
103
  const PARSED_PLACEMENT_CACHE = {};
104
104
 
105
- // @ts-ignore
106
- let visualViewport = typeof document !== 'undefined' && window.visualViewport;
105
+ let visualViewport = typeof document !== 'undefined' ? window.visualViewport : null;
107
106
 
108
107
  function getContainerDimensions(containerNode: Element): Dimensions {
109
108
  let width = 0, height = 0, totalWidth = 0, totalHeight = 0, top = 0, left = 0;
110
109
  let scroll: Position = {};
111
- let isPinchZoomedIn = visualViewport?.scale > 1;
110
+ let isPinchZoomedIn = (visualViewport?.scale ?? 1) > 1;
112
111
 
113
112
  if (containerNode.tagName === 'BODY') {
114
113
  let documentElement = document.documentElement;
@@ -141,8 +140,8 @@ function getContainerDimensions(containerNode: Element): Dimensions {
141
140
  // before pinch zoom happens
142
141
  scroll.top = 0;
143
142
  scroll.left = 0;
144
- top = visualViewport.pageTop;
145
- left = visualViewport.pageLeft;
143
+ top = visualViewport?.pageTop ?? 0;
144
+ left = visualViewport?.pageLeft ?? 0;
146
145
  }
147
146
 
148
147
  return {width, height, totalWidth, totalHeight, scroll, top, left};
@@ -174,7 +173,7 @@ function getDelta(
174
173
  padding: number,
175
174
  containerOffsetWithBoundary: Offset
176
175
  ) {
177
- let containerScroll = containerDimensions.scroll[axis];
176
+ let containerScroll = containerDimensions.scroll[axis] ?? 0;
178
177
  // The height/width of the boundary. Matches the axis along which we are adjusting the overlay position
179
178
  let boundarySize = boundaryDimensions[AXIS_SIZE[axis]];
180
179
  // Calculate the edges of the boundary (accomodating for the boundary padding) and the edges of the overlay.
@@ -240,26 +239,26 @@ function computePosition(
240
239
  let position: Position = {};
241
240
 
242
241
  // button position
243
- position[crossAxis] = childOffset[crossAxis];
242
+ position[crossAxis] = childOffset[crossAxis] ?? 0;
244
243
  if (crossPlacement === 'center') {
245
244
  // + (button size / 2) - (overlay size / 2)
246
245
  // at this point the overlay center should match the button center
247
- position[crossAxis] += (childOffset[crossSize] - overlaySize[crossSize]) / 2;
246
+ position[crossAxis]! += ((childOffset[crossSize] ?? 0) - (overlaySize[crossSize] ?? 0)) / 2;
248
247
  } else if (crossPlacement !== crossAxis) {
249
248
  // + (button size) - (overlay size)
250
249
  // at this point the overlay bottom should match the button bottom
251
- position[crossAxis] += (childOffset[crossSize] - overlaySize[crossSize]);
250
+ position[crossAxis]! += (childOffset[crossSize] ?? 0) - (overlaySize[crossSize] ?? 0);
252
251
  }/* else {
253
252
  the overlay top should match the button top
254
253
  } */
255
254
 
256
- position[crossAxis] += crossOffset;
255
+ position[crossAxis]! += crossOffset;
257
256
 
258
257
  // overlay top overlapping arrow with button bottom
259
258
  const minPosition = childOffset[crossAxis] - overlaySize[crossSize] + arrowSize + arrowBoundaryOffset;
260
259
  // overlay bottom overlapping arrow with button top
261
260
  const maxPosition = childOffset[crossAxis] + childOffset[crossSize] - arrowSize - arrowBoundaryOffset;
262
- position[crossAxis] = clamp(position[crossAxis], minPosition, maxPosition);
261
+ position[crossAxis] = clamp(position[crossAxis]!, minPosition, maxPosition);
263
262
 
264
263
  // Floor these so the position isn't placed on a partial pixel, only whole pixels. Shouldn't matter if it was floored or ceiled, so chose one.
265
264
  if (placement === axis) {
@@ -288,19 +287,19 @@ function getMaxHeight(
288
287
  const containerHeight = (isContainerPositioned ? containerOffsetWithBoundary.height : boundaryDimensions[TOTAL_SIZE.height]);
289
288
  // For cases where position is set via "bottom" instead of "top", we need to calculate the true overlay top with respect to the boundary. Reverse calculate this with the same method
290
289
  // used in computePosition.
291
- let overlayTop = position.top != null ? containerOffsetWithBoundary.top + position.top : containerOffsetWithBoundary.top + (containerHeight - position.bottom - overlayHeight);
290
+ let overlayTop = position.top != null ? containerOffsetWithBoundary.top + position.top : containerOffsetWithBoundary.top + (containerHeight - (position.bottom ?? 0) - overlayHeight);
292
291
  let maxHeight = heightGrowthDirection !== 'top' ?
293
292
  // We want the distance between the top of the overlay to the bottom of the boundary
294
293
  Math.max(0,
295
- (boundaryDimensions.height + boundaryDimensions.top + boundaryDimensions.scroll.top) // this is the bottom of the boundary
294
+ (boundaryDimensions.height + boundaryDimensions.top + (boundaryDimensions.scroll.top ?? 0)) // this is the bottom of the boundary
296
295
  - overlayTop // this is the top of the overlay
297
- - (margins.top + margins.bottom + padding) // save additional space for margin and padding
296
+ - ((margins.top ?? 0) + (margins.bottom ?? 0) + padding) // save additional space for margin and padding
298
297
  )
299
298
  // We want the distance between the bottom of the overlay to the top of the boundary
300
299
  : Math.max(0,
301
300
  (overlayTop + overlayHeight) // this is the bottom of the overlay
302
- - (boundaryDimensions.top + boundaryDimensions.scroll.top) // this is the top of the boundary
303
- - (margins.top + margins.bottom + padding) // save additional space for margin and padding
301
+ - (boundaryDimensions.top + (boundaryDimensions.scroll.top ?? 0)) // this is the top of the boundary
302
+ - ((margins.top ?? 0) + (margins.bottom ?? 0) + padding) // save additional space for margin and padding
304
303
  );
305
304
  return Math.min(boundaryDimensions.height - (padding * 2), maxHeight);
306
305
  }
@@ -315,10 +314,10 @@ function getAvailableSpace(
315
314
  ) {
316
315
  let {placement, axis, size} = placementInfo;
317
316
  if (placement === axis) {
318
- return Math.max(0, childOffset[axis] - boundaryDimensions[axis] - boundaryDimensions.scroll[axis] + containerOffsetWithBoundary[axis] - margins[axis] - margins[FLIPPED_DIRECTION[axis]] - padding);
317
+ return Math.max(0, childOffset[axis] - boundaryDimensions[axis] - (boundaryDimensions.scroll[axis] ?? 0) + containerOffsetWithBoundary[axis] - (margins[axis] ?? 0) - margins[FLIPPED_DIRECTION[axis]] - padding);
319
318
  }
320
319
 
321
- return Math.max(0, boundaryDimensions[size] + boundaryDimensions[axis] + boundaryDimensions.scroll[axis] - containerOffsetWithBoundary[axis] - childOffset[axis] - childOffset[size] - margins[axis] - margins[FLIPPED_DIRECTION[axis]] - padding);
320
+ return Math.max(0, boundaryDimensions[size] + boundaryDimensions[axis] + boundaryDimensions.scroll[axis] - containerOffsetWithBoundary[axis] - childOffset[axis] - childOffset[size] - (margins[axis] ?? 0) - margins[FLIPPED_DIRECTION[axis]] - padding);
322
321
  }
323
322
 
324
323
  export function calculatePositionInternal(
@@ -389,8 +388,8 @@ export function calculatePositionInternal(
389
388
  }
390
389
  }
391
390
 
392
- let delta = getDelta(crossAxis, position[crossAxis], overlaySize[crossSize], boundaryDimensions, containerDimensions, padding, containerOffsetWithBoundary);
393
- position[crossAxis] += delta;
391
+ let delta = getDelta(crossAxis, position[crossAxis]!, overlaySize[crossSize], boundaryDimensions, containerDimensions, padding, containerOffsetWithBoundary);
392
+ position[crossAxis]! += delta;
394
393
 
395
394
  let maxHeight = getMaxHeight(
396
395
  position,
@@ -410,8 +409,8 @@ export function calculatePositionInternal(
410
409
  overlaySize.height = Math.min(overlaySize.height, maxHeight);
411
410
 
412
411
  position = computePosition(childOffset, boundaryDimensions, overlaySize, placementInfo, normalizedOffset, crossOffset, containerOffsetWithBoundary, isContainerPositioned, arrowSize, arrowBoundaryOffset);
413
- delta = getDelta(crossAxis, position[crossAxis], overlaySize[crossSize], boundaryDimensions, containerDimensions, padding, containerOffsetWithBoundary);
414
- position[crossAxis] += delta;
412
+ delta = getDelta(crossAxis, position[crossAxis]!, overlaySize[crossSize], boundaryDimensions, containerDimensions, padding, containerOffsetWithBoundary);
413
+ position[crossAxis]! += delta;
415
414
 
416
415
  let arrowPosition: Position = {};
417
416
 
@@ -420,12 +419,12 @@ export function calculatePositionInternal(
420
419
  // childOffset[crossAxis] + .5 * childOffset[crossSize] = absolute position with respect to the trigger's coordinate system that would place the arrow in the center of the trigger
421
420
  // position[crossAxis] - margins[AXIS[crossAxis]] = value use to transform the position to a value with respect to the overlay's coordinate system. A child element's (aka arrow) position absolute's "0"
422
421
  // is positioned after the margin of its parent (aka overlay) so we need to subtract it to get the proper coordinate transform
423
- let preferredArrowPosition = childOffset[crossAxis] + .5 * childOffset[crossSize] - position[crossAxis] - margins[AXIS[crossAxis]];
422
+ let preferredArrowPosition = childOffset[crossAxis] + .5 * childOffset[crossSize] - position[crossAxis]! - margins[AXIS[crossAxis]];
424
423
 
425
424
  // Min/Max position limits for the arrow with respect to the overlay
426
425
  const arrowMinPosition = arrowSize / 2 + arrowBoundaryOffset;
427
426
  // overlaySize[crossSize] - margins = true size of the overlay
428
- const overlayMargin = AXIS[crossAxis] === 'left' ? margins.left + margins.right : margins.top + margins.bottom;
427
+ const overlayMargin = AXIS[crossAxis] === 'left' ? (margins.left ?? 0) + (margins.right ?? 0) : (margins.top ?? 0) + (margins.bottom ?? 0);
429
428
  const arrowMaxPosition = overlaySize[crossSize] - overlayMargin - (arrowSize / 2) - arrowBoundaryOffset;
430
429
 
431
430
  // Min/Max position limits for the arrow with respect to the trigger/overlay anchor element
@@ -479,8 +478,8 @@ export function calculatePosition(opts: PositionOpts): PositionResult {
479
478
 
480
479
  let overlaySize: Offset = getOffset(overlayNode);
481
480
  let margins = getMargins(overlayNode);
482
- overlaySize.width += margins.left + margins.right;
483
- overlaySize.height += margins.top + margins.bottom;
481
+ overlaySize.width += (margins.left ?? 0) + (margins.right ?? 0);
482
+ overlaySize.height += (margins.top ?? 0) + (margins.bottom ?? 0);
484
483
 
485
484
  let scrollSize = getScroll(scrollNode);
486
485
  let boundaryDimensions = getContainerDimensions(boundaryElement);
@@ -590,9 +589,7 @@ function isContainingBlock(node: Element): boolean {
590
589
  /transform|perspective/.test(style.willChange) ||
591
590
  style.filter !== 'none' ||
592
591
  style.contain === 'paint' ||
593
- // @ts-ignore
594
592
  ('backdropFilter' in style && style.backdropFilter !== 'none') ||
595
- // @ts-ignore
596
593
  ('WebkitBackdropFilter' in style && style.WebkitBackdropFilter !== 'none')
597
594
  );
598
595
  }
@@ -23,7 +23,7 @@ export const onCloseMap: WeakMap<Element, () => void> = new WeakMap();
23
23
  interface CloseOnScrollOptions {
24
24
  triggerRef: RefObject<Element | null>,
25
25
  isOpen?: boolean,
26
- onClose?: () => void
26
+ onClose?: (() => void) | null
27
27
  }
28
28
 
29
29
  /** @private */
@@ -35,7 +35,7 @@ export function useCloseOnScroll(opts: CloseOnScrollOptions) {
35
35
  return;
36
36
  }
37
37
 
38
- let onScroll = (e: MouseEvent) => {
38
+ let onScroll = (e: Event) => {
39
39
  // Ignore if scrolling an scrollable region outside the trigger's tree.
40
40
  let target = e.target;
41
41
  // window is not a Node and doesn't have contain, but window contains everything
package/src/useModal.tsx CHANGED
@@ -79,7 +79,7 @@ export function useModalProvider(): ModalProviderAria {
79
79
  let context = useContext(Context);
80
80
  return {
81
81
  modalProviderProps: {
82
- 'aria-hidden': context && context.modalCount > 0 ? true : null
82
+ 'aria-hidden': context && context.modalCount > 0 ? true : undefined
83
83
  }
84
84
  };
85
85
  }
@@ -123,7 +123,7 @@ export interface OverlayContainerProps extends ModalProviderProps {
123
123
  * nested modal is opened. Only the top-most modal or overlay should
124
124
  * be accessible at once.
125
125
  */
126
- export function OverlayContainer(props: OverlayContainerProps): React.ReactPortal {
126
+ export function OverlayContainer(props: OverlayContainerProps): React.ReactPortal | null {
127
127
  let isSSR = useIsSSR();
128
128
  let {portalContainer = isSSR ? null : document.body, ...rest} = props;
129
129
 
@@ -57,7 +57,7 @@ export function useModalOverlay(props: AriaModalOverlayProps, state: OverlayTrig
57
57
  useOverlayFocusContain();
58
58
 
59
59
  useEffect(() => {
60
- if (state.isOpen) {
60
+ if (state.isOpen && ref.current) {
61
61
  return ariaHideOutside([ref.current]);
62
62
  }
63
63
  }, [state.isOpen, ref]);
package/src/useOverlay.ts CHANGED
@@ -120,7 +120,7 @@ export function useOverlay(props: AriaOverlayProps, ref: RefObject<Element | nul
120
120
  };
121
121
 
122
122
  // Handle clicking outside the overlay to close it
123
- useInteractOutside({ref, onInteractOutside: isDismissable && isOpen ? onInteractOutside : null, onInteractOutsideStart});
123
+ useInteractOutside({ref, onInteractOutside: isDismissable && isOpen ? onInteractOutside : undefined, onInteractOutsideStart});
124
124
 
125
125
  let {focusWithinProps} = useFocusWithin({
126
126
  isDisabled: !shouldCloseOnBlur,
@@ -139,7 +139,7 @@ export function useOverlay(props: AriaOverlayProps, ref: RefObject<Element | nul
139
139
  }
140
140
 
141
141
  if (!shouldCloseOnInteractOutside || shouldCloseOnInteractOutside(e.relatedTarget as Element)) {
142
- onClose();
142
+ onClose?.();
143
143
  }
144
144
  }
145
145
  });
@@ -48,7 +48,7 @@ export interface AriaPositionProps extends PositionProps {
48
48
  */
49
49
  shouldUpdatePosition?: boolean,
50
50
  /** Handler that is called when the overlay should close. */
51
- onClose?: () => void,
51
+ onClose?: (() => void) | null,
52
52
  /**
53
53
  * The maxHeight specified for the overlay element.
54
54
  * By default, it will take all space up to the current viewport height.
@@ -67,7 +67,7 @@ export interface PositionAria {
67
67
  /** Props for the overlay tip arrow if any. */
68
68
  arrowProps: DOMAttributes,
69
69
  /** Placement of the overlay with respect to the overlay trigger. */
70
- placement: PlacementAxis,
70
+ placement: PlacementAxis | null,
71
71
  /** Updates the position of the overlay. */
72
72
  updatePosition(): void
73
73
  }
@@ -77,8 +77,7 @@ interface ScrollAnchor {
77
77
  offset: number
78
78
  }
79
79
 
80
- // @ts-ignore
81
- let visualViewport = typeof document !== 'undefined' && window.visualViewport;
80
+ let visualViewport = typeof document !== 'undefined' ? window.visualViewport : null;
82
81
 
83
82
  /**
84
83
  * Handles positioning overlays like popovers and menus relative to a trigger
@@ -103,13 +102,7 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
103
102
  maxHeight,
104
103
  arrowBoundaryOffset = 0
105
104
  } = props;
106
- let [position, setPosition] = useState<PositionResult>({
107
- position: {},
108
- arrowOffsetLeft: undefined,
109
- arrowOffsetTop: undefined,
110
- maxHeight: undefined,
111
- placement: undefined
112
- });
105
+ let [position, setPosition] = useState<PositionResult | null>(null);
113
106
 
114
107
  let deps = [
115
108
  shouldUpdatePosition,
@@ -154,17 +147,17 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
154
147
  // changes, the focused element appears to stay in the same position.
155
148
  let anchor: ScrollAnchor | null = null;
156
149
  if (scrollRef.current && scrollRef.current.contains(document.activeElement)) {
157
- let anchorRect = document.activeElement.getBoundingClientRect();
150
+ let anchorRect = document.activeElement?.getBoundingClientRect();
158
151
  let scrollRect = scrollRef.current.getBoundingClientRect();
159
152
  // Anchor from the top if the offset is in the top half of the scrollable element,
160
153
  // otherwise anchor from the bottom.
161
154
  anchor = {
162
155
  type: 'top',
163
- offset: anchorRect.top - scrollRect.top
156
+ offset: (anchorRect?.top ?? 0) - scrollRect.top
164
157
  };
165
158
  if (anchor.offset > scrollRect.height / 2) {
166
159
  anchor.type = 'bottom';
167
- anchor.offset = anchorRect.bottom - scrollRect.bottom;
160
+ anchor.offset = (anchorRect?.bottom ?? 0) - scrollRect.bottom;
168
161
  }
169
162
  }
170
163
 
@@ -192,6 +185,10 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
192
185
  arrowBoundaryOffset
193
186
  });
194
187
 
188
+ if (!position.position) {
189
+ return;
190
+ }
191
+
195
192
  // Modify overlay styles directly so positioning happens immediately without the need of a second render
196
193
  // This is so we don't have to delay autoFocus scrolling or delay applying preventScroll for popovers
197
194
  overlay.style.top = '';
@@ -199,11 +196,11 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
199
196
  overlay.style.left = '';
200
197
  overlay.style.right = '';
201
198
 
202
- Object.keys(position.position).forEach(key => overlay.style[key] = position.position[key] + 'px');
203
- overlay.style.maxHeight = position.maxHeight != null ? position.maxHeight + 'px' : undefined;
199
+ Object.keys(position.position).forEach(key => overlay.style[key] = (position.position!)[key] + 'px');
200
+ overlay.style.maxHeight = position.maxHeight != null ? position.maxHeight + 'px' : '';
204
201
 
205
202
  // Restore scroll position relative to anchor element.
206
- if (anchor) {
203
+ if (anchor && document.activeElement && scrollRef.current) {
207
204
  let anchorRect = document.activeElement.getBoundingClientRect();
208
205
  let scrollRect = scrollRef.current.getBoundingClientRect();
209
206
  let newOffset = anchorRect[anchor.type] - scrollRect[anchor.type];
@@ -268,7 +265,7 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
268
265
 
269
266
  let close = useCallback(() => {
270
267
  if (!isResizing.current) {
271
- onClose();
268
+ onClose?.();
272
269
  }
273
270
  }, [onClose, isResizing]);
274
271
 
@@ -285,17 +282,17 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
285
282
  style: {
286
283
  position: 'absolute',
287
284
  zIndex: 100000, // should match the z-index in ModalTrigger
288
- ...position.position,
289
- maxHeight: position.maxHeight ?? '100vh'
285
+ ...position?.position,
286
+ maxHeight: position?.maxHeight ?? '100vh'
290
287
  }
291
288
  },
292
- placement: position.placement,
289
+ placement: position?.placement ?? null,
293
290
  arrowProps: {
294
291
  'aria-hidden': 'true',
295
292
  role: 'presentation',
296
293
  style: {
297
- left: position.arrowOffsetLeft,
298
- top: position.arrowOffsetTop
294
+ left: position?.arrowOffsetLeft,
295
+ top: position?.arrowOffsetTop
299
296
  }
300
297
  },
301
298
  updatePosition