Skip to content

Commit

Permalink
Remove @react-aria/utils dependency
Browse files Browse the repository at this point in the history
  • Loading branch information
Temzasse committed Oct 27, 2024
1 parent d97061c commit 3031ff4
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 30 deletions.
14 changes: 10 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@
"typecheck": "tsc --noEmit --skipLibCheck",
"release": "npm run build && npm run build:verify && np"
},
"dependencies": {
"@react-aria/utils": "3.25.3"
},
"peerDependencies": {
"framer-motion": ">=10",
"react": ">=16"
Expand Down
4 changes: 2 additions & 2 deletions src/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ export function useEventCallbacks(

export function useDimensions() {
const [dimensions, setDimensions] = useState({
height: isBrowser ? window.innerHeight : 0,
width: isBrowser ? window.innerWidth : 0,
height: !IS_SSR ? window.innerHeight : 0,
width: !IS_SSR ? window.innerWidth : 0,
});

useIsomorphicLayoutEffect(() => {
Expand Down
101 changes: 80 additions & 21 deletions src/use-prevent-scroll.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,69 @@
// Source: https://github.com/adobe/react-spectrum/blob/main/packages/@react-aria/overlays/src/usePreventScroll.ts
import {
chain,
getScrollParent,
isIOS,
useLayoutEffect,
} from '@react-aria/utils';
// This code originates from https://github.com/adobe/react-spectrum/blob/main/packages/%40react-aria/overlays/src/usePreventScroll.ts

import { useIsomorphicLayoutEffect } from './hooks';
import { isIOS } from './utils';

const KEYBOARD_BUFFER = 24;

interface PreventScrollOptions {
/** Whether the scroll lock is disabled. */
isDisabled?: boolean;
}

const visualViewport =
typeof window !== 'undefined' ? (window as any).visualViewport : undefined;
function chain(...callbacks: any[]): (...args: any[]) => void {
return (...args: any[]) => {
for (const callback of callbacks) {
if (typeof callback === 'function') {
// eslint-disable-next-line n/no-callback-literal
callback(...args);
}
}
};
}

const visualViewport = typeof document !== 'undefined' && window.visualViewport;

export function isScrollable(
node: Element | null,
checkForOverflow?: boolean
): boolean {
if (!node) {
return false;
}

const style = window.getComputedStyle(node);

let isScrollable = /(auto|scroll)/.test(
style.overflow + style.overflowX + style.overflowY
);

if (isScrollable && checkForOverflow) {
isScrollable =
node.scrollHeight !== node.clientHeight ||
node.scrollWidth !== node.clientWidth;
}

return isScrollable;
}

export function getScrollParent(
node: Element,
checkForOverflow?: boolean
): Element {
let scrollableNode: Element | null = node;

if (isScrollable(scrollableNode, checkForOverflow)) {
scrollableNode = scrollableNode.parentElement;
}

while (scrollableNode && !isScrollable(scrollableNode, checkForOverflow)) {
scrollableNode = scrollableNode.parentElement;
}

return (
scrollableNode || document.scrollingElement || document.documentElement
);
}

// HTML input types that do not cause the software keyboard to appear.
const nonTextInputTypes = new Set([
Expand All @@ -29,7 +80,7 @@ const nonTextInputTypes = new Set([

// The number of active usePreventScroll calls. Used to determine whether to revert back to the original page style/scroll position
let preventScrollCount = 0;
let restore: any;
let restore: () => void;

/**
* Prevents scrolling on the document body on mount, and
Expand All @@ -39,7 +90,7 @@ let restore: any;
export function usePreventScroll(options: PreventScrollOptions = {}) {
const { isDisabled } = options;

useLayoutEffect(() => {
useIsomorphicLayoutEffect(() => {
if (isDisabled) {
return;
}
Expand All @@ -56,7 +107,7 @@ export function usePreventScroll(options: PreventScrollOptions = {}) {
return () => {
preventScrollCount--;
if (preventScrollCount === 0) {
restore();
restore?.();
}
};
}, [isDisabled]);
Expand Down Expand Up @@ -107,7 +158,7 @@ function preventScrollMobileSafari() {

const onTouchStart = (e: TouchEvent) => {
// Store the nearest scrollable parent element from the element that the user touched.
scrollable = getScrollParent(e.target as Element);
scrollable = getScrollParent(e.target as Element, true);
if (
scrollable === document.documentElement &&
scrollable === document.body
Expand All @@ -128,6 +179,7 @@ function preventScrollMobileSafari() {

// Prevent scrolling the window.
if (
!scrollable ||
scrollable === document.documentElement ||
scrollable === document.body
) {
Expand All @@ -143,7 +195,6 @@ function preventScrollMobileSafari() {
const scrollTop = scrollable.scrollTop;
const bottom = scrollable.scrollHeight - scrollable.clientHeight;

// Fix for: https://github.com/adobe/react-spectrum/pull/3780/files
if (bottom === 0) {
return;
}
Expand Down Expand Up @@ -198,9 +249,7 @@ function preventScrollMobileSafari() {
// measure the correct position to scroll to.
visualViewport.addEventListener(
'resize',
() => {
scrollIntoView(target);
},
() => scrollIntoView(target),
{ once: true }
);
}
Expand Down Expand Up @@ -261,6 +310,7 @@ function preventScrollMobileSafari() {

// Sets a CSS property on an element, and returns a function to revert it to the previous value.
function setStyle(element: any, style: string, value: string) {
// https://github.com/microsoft/TypeScript/issues/17827#issuecomment-391663310
const cur = element.style[style];
element.style[style] = value;

Expand All @@ -276,9 +326,12 @@ function addEvent<K extends keyof GlobalEventHandlersEventMap>(
handler: (this: Document, ev: GlobalEventHandlersEventMap[K]) => any,
options?: boolean | AddEventListenerOptions
) {
target.addEventListener(event, handler as any, options);
// @ts-expect-error
target.addEventListener(event, handler, options);

return () => {
target.removeEventListener(event, handler as any, options);
// @ts-expect-error
target.removeEventListener(event, handler, options);
};
}

Expand All @@ -294,12 +347,18 @@ function scrollIntoView(target: Element) {
) {
const scrollableTop = scrollable.getBoundingClientRect().top;
const targetTop = target.getBoundingClientRect().top;
if (targetTop > scrollableTop + target.clientHeight) {
const targetBottom = target.getBoundingClientRect().bottom;
// Buffer is needed for some edge cases
const keyboardHeight =
scrollable.getBoundingClientRect().bottom + KEYBOARD_BUFFER;

if (targetBottom > keyboardHeight) {
scrollable.scrollTop += targetTop - scrollableTop;
}
}

target = scrollable.parentElement as any;
// @ts-expect-error
target = scrollable.parentElement;
}
}

Expand Down
41 changes: 41 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,44 @@ export function isTouchDevice() {
if (typeof window === 'undefined') return false;
return 'ontouchstart' in window || navigator.maxTouchPoints > 0;
}

function testPlatform(re: RegExp) {
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
return typeof window !== 'undefined' && window.navigator != null
? re.test(
// @ts-expect-error
// eslint-disable-next-line @typescript-eslint/dot-notation
window.navigator['userAgentData']?.platform || window.navigator.platform
)
: false;
}

function cached(fn: () => boolean) {
let res: boolean | null = null;
return () => {
if (res == null) {
res = fn();
}
return res;
};
}

const isMac = cached(function () {
return testPlatform(/^Mac/i);
});

const isIPhone = cached(function () {
return testPlatform(/^iPhone/i);
});

const isIPad = cached(function () {
return (
testPlatform(/^iPad/i) ||
// iPadOS 13 lies and says it's a Mac, but we can distinguish by detecting touch support.
(isMac() && navigator.maxTouchPoints > 1)
);
});

export const isIOS = cached(function () {
return isIPhone() || isIPad();
});

0 comments on commit 3031ff4

Please sign in to comment.