Skip to content

Commit

Permalink
refactor: background scaling (#409)
Browse files Browse the repository at this point in the history
  • Loading branch information
emilkowalski authored Sep 13, 2024
1 parent 0d39391 commit 0f4e97f
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 65 deletions.
8 changes: 6 additions & 2 deletions src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { DrawerDirection } from './types';
interface DrawerContextValue {
drawerRef: React.RefObject<HTMLDivElement>;
overlayRef: React.RefObject<HTMLDivElement>;
scaleBackground: (open: boolean) => void;
onPress: (event: React.PointerEvent<HTMLDivElement>) => void;
onRelease: (event: React.PointerEvent<HTMLDivElement>) => void;
onDrag: (event: React.PointerEvent<HTMLDivElement>) => void;
Expand All @@ -25,12 +24,14 @@ interface DrawerContextValue {
openProp?: boolean;
onOpenChange?: (o: boolean) => void;
direction?: DrawerDirection;
shouldScaleBackground: boolean;
setBackgroundColorOnScale: boolean;
noBodyStyles: boolean;
}

export const DrawerContext = React.createContext<DrawerContextValue>({
drawerRef: { current: null },
overlayRef: { current: null },
scaleBackground: () => {},
onPress: () => {},
onRelease: () => {},
onDrag: () => {},
Expand All @@ -51,6 +52,9 @@ export const DrawerContext = React.createContext<DrawerContextValue>({
setActiveSnapPoint: () => {},
closeDrawer: () => {},
direction: 'bottom',
shouldScaleBackground: false,
setBackgroundColorOnScale: true,
noBodyStyles: false,
});

export const useDrawerContext = () => {
Expand Down
27 changes: 26 additions & 1 deletion src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DrawerDirection } from './types';
import { AnyFunction, DrawerDirection } from './types';

interface Style {
[key: string]: string;
Expand Down Expand Up @@ -90,3 +90,28 @@ export function getTranslate(element: HTMLElement, direction: DrawerDirection):
export function dampenValue(v: number) {
return 8 * (Math.log(v + 1) - 2);
}

export function assignStyle(element: HTMLElement | null | undefined, style: Partial<CSSStyleDeclaration>) {
if (!element) return () => {};

const prevStyle = element.style.cssText;
Object.assign(element.style, style);

return () => {
element.style.cssText = prevStyle;
};
}

/**
* Receives functions as arguments and returns a new function that calls all.
*/
export function chain<T>(...fns: T[]) {
return (...args: T extends AnyFunction ? Parameters<T> : never) => {
for (const fn of fns) {
if (typeof fn === 'function') {
// @ts-ignore
fn(...args);
}
}
};
}
68 changes: 6 additions & 62 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from './constants';
import { DrawerDirection } from './types';
import { useControllableState } from './use-controllable-state';
import { useScaleBackground } from './use-scale-background';

export interface WithFadeFromProps {
snapPoints: (number | string)[];
Expand Down Expand Up @@ -330,12 +331,6 @@ export function Root({
}
}

React.useEffect(() => {
return () => {
scaleBackground(false);
};
}, []);

React.useEffect(() => {
function onVisualViewportChange() {
if (!drawerRef.current) return;
Expand Down Expand Up @@ -397,7 +392,6 @@ export function Root({
cancelDrag();
onClose?.();

scaleBackground(false);
setIsOpen(false);

setTimeout(() => {
Expand Down Expand Up @@ -538,7 +532,6 @@ export function Root({
});

openTime.current = new Date();
scaleBackground(true);
}
}, [isOpen]);

Expand All @@ -555,58 +548,6 @@ export function Root({
}
}, [isOpen]);

function scaleBackground(open: boolean) {
const wrapper = document.querySelector('[data-vaul-drawer-wrapper]');

if (!wrapper || !shouldScaleBackground) return;

if (open) {
if (setBackgroundColorOnScale) {
if (!noBodyStyles) {
// setting original styles initially
set(document.body, {
background: document.body.style.backgroundColor || document.body.style.background,
});
// setting body styles, with cache ignored, so that we can get correct original styles in reset
set(
document.body,
{
background: 'black',
},
true,
);
}
}

set(wrapper, {
borderRadius: `${BORDER_RADIUS}px`,
overflow: 'hidden',
...(isVertical(direction)
? {
transform: `scale(${getScale()}) translate3d(0, calc(env(safe-area-inset-top) + 14px), 0)`,
transformOrigin: 'top',
}
: {
transform: `scale(${getScale()}) translate3d(calc(env(safe-area-inset-top) + 14px), 0, 0)`,
transformOrigin: 'left',
}),
transitionProperty: 'transform, border-radius',
transitionDuration: `${TRANSITIONS.DURATION}s`,
transitionTimingFunction: `cubic-bezier(${TRANSITIONS.EASE.join(',')})`,
});
} else {
// Exit
reset(wrapper, 'overflow');
reset(wrapper, 'transform');
reset(wrapper, 'borderRadius');
set(wrapper, {
transitionProperty: 'transform, border-radius',
transitionDuration: `${TRANSITIONS.DURATION}s`,
transitionTimingFunction: `cubic-bezier(${TRANSITIONS.EASE.join(',')})`,
});
}
}

function onNestedOpenChange(o: boolean) {
const scale = o ? (window.innerWidth - NESTED_DISPLACEMENT) / window.innerWidth : 1;
const y = o ? -NESTED_DISPLACEMENT : 0;
Expand Down Expand Up @@ -683,7 +624,6 @@ export function Root({
setActiveSnapPoint,
drawerRef,
overlayRef,
scaleBackground,
onOpenChange,
onPress,
onRelease,
Expand All @@ -700,6 +640,9 @@ export function Root({
modal,
snapPointsOffset,
direction,
shouldScaleBackground,
setBackgroundColorOnScale,
noBodyStyles,
}}
>
{children}
Expand Down Expand Up @@ -755,7 +698,8 @@ export const Content = React.forwardRef<HTMLDivElement, ContentProps>(function (
const pointerStartRef = React.useRef<{ x: number; y: number } | null>(null);
const wasBeyondThePointRef = React.useRef(false);
const hasSnapPoints = snapPoints && snapPoints.length > 0;

useScaleBackground();

const isDeltaInDirection = (delta: { x: number; y: number }, direction: DrawerDirection, threshold = 0) => {
if (wasBeyondThePointRef.current) return true;

Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ export interface SnapPoint {
fraction: number;
height: number;
}

export type AnyFunction = (...args: any) => any;
51 changes: 51 additions & 0 deletions src/use-scale-background.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import { useDrawerContext } from './context';
import { assignStyle, chain, isVertical } from './helpers';
import { BORDER_RADIUS, TRANSITIONS, WINDOW_TOP_OFFSET } from './constants';

const noop = () => () => {};

export function useScaleBackground() {
const { direction, isOpen, shouldScaleBackground, setBackgroundColorOnScale, noBodyStyles } = useDrawerContext();
const timeoutIdRef = React.useRef<number | null>(null);

function getScale() {
return (window.innerWidth - WINDOW_TOP_OFFSET) / window.innerWidth;
}

React.useEffect(() => {
if (isOpen && shouldScaleBackground) {
if (timeoutIdRef.current) clearTimeout(timeoutIdRef.current);
const wrapper = document.querySelector('[data-vaul-drawer-wrapper]') as HTMLElement;

const bodyAndWrapperCleanup = chain(
setBackgroundColorOnScale && !noBodyStyles ? assignStyle(document.body, { background: 'black' }) : noop,
assignStyle(wrapper, {
transformOrigin: isVertical(direction) ? 'top' : 'left',
transitionProperty: 'transform, border-radius',
transitionDuration: `${TRANSITIONS.DURATION}s`,
transitionTimingFunction: `cubic-bezier(${TRANSITIONS.EASE.join(',')})`,
}),
);

const wrapperStylesCleanup = assignStyle(wrapper, {
borderRadius: `${BORDER_RADIUS}px`,
overflow: 'hidden',
...(isVertical(direction)
? {
transform: `scale(${getScale()}) translate3d(0, calc(env(safe-area-inset-top) + 14px), 0)`,
}
: {
transform: `scale(${getScale()}) translate3d(calc(env(safe-area-inset-top) + 14px), 0, 0)`,
}),
});

return () => {
wrapperStylesCleanup();
timeoutIdRef.current = window.setTimeout(() => {
bodyAndWrapperCleanup();
}, TRANSITIONS.DURATION * 1000);
};
}
}, [isOpen, shouldScaleBackground]);
}

0 comments on commit 0f4e97f

Please sign in to comment.