Skip to content

Commit

Permalink
Cleanup (#74)
Browse files Browse the repository at this point in the history
* init

* polish

* clean
  • Loading branch information
emilkowalski authored Aug 29, 2023
1 parent 53dbcee commit 6a73547
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 50 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}
50 changes: 30 additions & 20 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function Root({
closeThreshold = CLOSE_THRESHOLD,
scrollLockTimeout = SCROLL_LOCK_TIMEOUT,
dismissible = true,
fadeFromIndex = snapPoints?.length - 1,
fadeFromIndex = snapPoints && snapPoints?.length - 1,
activeSnapPoint: activeSnapPointProp,
setActiveSnapPoint: setActiveSnapPointProp,
modal = true,
Expand All @@ -82,7 +82,7 @@ function Root({
const dragStartTime = React.useRef<Date | null>(null);
const dragEndTime = React.useRef<Date | null>(null);
const lastTimeDragPrevented = React.useRef<Date | null>(null);
const nestedOpenChangeTimer = React.useRef<NodeJS.Timeout>(null);
const nestedOpenChangeTimer = React.useRef<NodeJS.Timeout | null>(null);
const pointerStartY = React.useRef(0);
const keyboardIsOpen = React.useRef(false);
const previousDiffFromInitial = React.useRef(0);
Expand Down Expand Up @@ -138,11 +138,11 @@ function Root({
function shouldDrag(el: EventTarget, isDraggingDown: boolean) {
let element = el as HTMLElement;
const date = new Date();
const highlightedText = window.getSelection().toString();
const highlightedText = window.getSelection()?.toString();
const swipeAmount = drawerRef.current ? getTranslateY(drawerRef.current) : null;

// Don't drag if there's highlighted text
if (highlightedText.length > 0) {
if (highlightedText && highlightedText.length > 0) {
return false;
}

Expand Down Expand Up @@ -231,7 +231,7 @@ function Root({

const opacityValue = 1 - percentageDragged;

if (shouldFade || activeSnapPointIndex === fadeFromIndex - 1) {
if (shouldFade || (fadeFromIndex && activeSnapPointIndex === fadeFromIndex - 1)) {
changeThemeColorOnDrag(percentageDragged);
onDragProp?.(event, percentageDragged);

Expand Down Expand Up @@ -277,7 +277,7 @@ function Root({

const focusedElement = document.activeElement as HTMLElement;
if ((!isInView(focusedElement) && isInput(focusedElement)) || keyboardIsOpen.current) {
const visualViewportHeight = window.visualViewport.height;
const visualViewportHeight = window.visualViewport?.height || 0;
// This is the height of the keyboard
let diffFromInitial = window.innerHeight - visualViewportHeight;
const drawerHeight = drawerRef.current?.getBoundingClientRect().height || 0;
Expand All @@ -288,8 +288,8 @@ function Root({
keyboardIsOpen.current = !keyboardIsOpen.current;
}

if (snapPoints && snapPoints.length > 0) {
const activeSnapPointHeight = snapPointsOffset[activeSnapPointIndex];
if (snapPoints && snapPoints.length > 0 && snapPointsOffset && activeSnapPointIndex) {
const activeSnapPointHeight = snapPointsOffset[activeSnapPointIndex] || 0;
diffFromInitial += activeSnapPointHeight;
}

Expand Down Expand Up @@ -317,12 +317,12 @@ function Root({
}
}

window.visualViewport.addEventListener('resize', onVisualViewportChange);
return () => window.visualViewport.removeEventListener('resize', onVisualViewportChange);
}, [activeSnapPointIndex]);
window.visualViewport?.addEventListener('resize', onVisualViewportChange);
return () => window.visualViewport?.removeEventListener('resize', onVisualViewportChange);
}, [activeSnapPointIndex, snapPoints, snapPointsOffset]);

function closeDrawer() {
if (!dismissible) return;
if (!dismissible || !drawerRef.current) return;
drawerRef.current.setAttribute('vaul-closed-by-dragging', 'true');
setIsOpen(false);

Expand Down Expand Up @@ -352,6 +352,7 @@ function Root({
}, [isOpen, shouldScaleBackground]);

function resetDrawer() {
if (!drawerRef.current) return;
const wrapper = document.querySelector('[vaul-drawer-wrapper]');
const currentSwipeAmount = getTranslateY(drawerRef.current);

Expand All @@ -366,7 +367,7 @@ function Root({
});

// Don't reset background if swiped upwards
if (shouldScaleBackground && currentSwipeAmount > 0 && isOpen) {
if (shouldScaleBackground && currentSwipeAmount && currentSwipeAmount > 0 && isOpen) {
set(
wrapper,
{
Expand All @@ -384,7 +385,7 @@ function Root({
}

function onRelease(event: React.PointerEvent<HTMLDivElement>) {
if (!isDragging) return;
if (!isDragging || !drawerRef.current) return;

setIsDragging(false);

Expand All @@ -411,7 +412,11 @@ function Root({
}

if (snapPoints) {
onReleaseSnapPoints({ draggedDistance: distMoved, closeDrawer, velocity });
onReleaseSnapPoints({
draggedDistance: distMoved,
closeDrawer,
velocity,
});
return;
}

Expand Down Expand Up @@ -482,14 +487,17 @@ function Root({
function onNestedOpenChange(o: boolean) {
const scale = o ? (window.innerWidth - NESTED_DISPLACEMENT) / window.innerWidth : 1;
const y = o ? -NESTED_DISPLACEMENT : 0;
window.clearTimeout(nestedOpenChangeTimer.current);

if (nestedOpenChangeTimer.current) {
window.clearTimeout(nestedOpenChangeTimer.current);
}

set(drawerRef.current, {
transition: `transform ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(',')})`,
transform: `scale(${scale}) translateY(${y}px)`,
});

if (!o) {
if (!o && drawerRef.current) {
nestedOpenChangeTimer.current = setTimeout(() => {
set(drawerRef.current, {
transition: 'none',
Expand Down Expand Up @@ -526,7 +534,7 @@ function Root({
return (
<DialogPrimitive.Root
open={isOpen}
onOpenChange={(o) => {
onOpenChange={(o: boolean) => {
if (!o && snapPoints) {
closeDrawer();
}
Expand Down Expand Up @@ -610,12 +618,14 @@ const Content = React.forwardRef<HTMLDivElement, ContentProps>(function (
snapPoints,
} = useDrawerContext();
const composedRef = useComposedRefs(ref, drawerRef);
const animationEndTimer = React.useRef<NodeJS.Timeout>(null);
const animationEndTimer = React.useRef<NodeJS.Timeout | null>(null);

return (
<DialogPrimitive.Content
onAnimationStart={(e) => {
window.clearTimeout(animationEndTimer.current);
if (animationEndTimer.current) {
window.clearTimeout(animationEndTimer.current);
}
setIsAnimating(true);

animationEndTimer.current = setTimeout(() => {
Expand Down
71 changes: 41 additions & 30 deletions src/use-snap-points.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { dampenValue, set } from './helpers';
import { set } from './helpers';
import { TRANSITIONS, VELOCITY_THRESHOLD } from './constants';
import { useControllableState } from './use-controllable-state';

Expand All @@ -18,13 +18,12 @@ export function useSnapPoints({
drawerRef: React.RefObject<HTMLDivElement>;
overlayRef: React.RefObject<HTMLDivElement>;
}) {
const [activeSnapPoint, setActiveSnapPoint] = useControllableState({
const [activeSnapPoint, setActiveSnapPoint] = useControllableState<string | number | null>({
prop: activeSnapPointProp,
defaultProp: snapPoints?.[0],
onChange: setActiveSnapPointProp,
});

const hasWindow = typeof window !== 'undefined';
const isLastSnapPoint = React.useMemo(
() => activeSnapPoint === snapPoints?.[snapPoints.length - 1] ?? null,
[snapPoints, activeSnapPoint],
Expand All @@ -41,6 +40,7 @@ export function useSnapPoints({
const snapPointsOffset = React.useMemo(
() =>
snapPoints?.map((snapPoint) => {
const hasWindow = typeof window !== 'undefined';
const isPx = typeof snapPoint === 'string';
let snapPointAsNumber = 0;

Expand All @@ -56,40 +56,48 @@ export function useSnapPoints({
);

const activeSnapPointOffset = React.useMemo(
() => snapPointsOffset?.[activeSnapPointIndex] ?? null,
[snapPointsOffset, activeSnapPoint],
() => (activeSnapPointIndex !== null ? snapPointsOffset?.[activeSnapPointIndex] : null),
[snapPointsOffset, activeSnapPointIndex],
);

function snapToPoint(height: number) {
const newSnapPointIndex = snapPointsOffset?.findIndex((snapPointHeight) => snapPointHeight === height) ?? null;
const snapToPoint = React.useCallback(
(height: number) => {
const newSnapPointIndex = snapPointsOffset?.findIndex((snapPointHeight) => snapPointHeight === height) ?? null;

set(drawerRef.current, {
transition: `transform ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(',')})`,
transform: `translateY(${height}px)`,
});

if (newSnapPointIndex !== snapPointsOffset.length - 1 && newSnapPointIndex !== fadeFromIndex) {
set(overlayRef.current, {
transition: `opacity ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(',')})`,
opacity: '0',
});
} else {
set(overlayRef.current, {
transition: `opacity ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(',')})`,
opacity: '1',
set(drawerRef.current, {
transition: `transform ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(',')})`,
transform: `translateY(${height}px)`,
});
}

setActiveSnapPoint(snapPoints?.[newSnapPointIndex] ?? null);
}
if (
snapPointsOffset &&
newSnapPointIndex !== snapPointsOffset.length - 1 &&
newSnapPointIndex !== fadeFromIndex
) {
set(overlayRef.current, {
transition: `opacity ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(',')})`,
opacity: '0',
});
} else {
set(overlayRef.current, {
transition: `opacity ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(',')})`,
opacity: '1',
});
}

setActiveSnapPoint(newSnapPointIndex !== null ? snapPoints?.[newSnapPointIndex] : null);
},
[drawerRef, snapPoints, snapPointsOffset, fadeFromIndex, overlayRef, setActiveSnapPoint],
);

React.useEffect(() => {
if (activeSnapPointProp) {
const newIndex = snapPoints?.findIndex((snapPoint) => snapPoint === activeSnapPointProp) ?? null;

snapToPoint(snapPointsOffset[newIndex]);
if (snapPointsOffset && newIndex && snapPointsOffset[newIndex]) {
snapToPoint(snapPointsOffset[newIndex] as number);
}
}
}, [activeSnapPointProp]);
}, [activeSnapPointProp, snapPoints, snapPointsOffset, snapToPoint]);

function onRelease({
draggedDistance,
Expand All @@ -100,6 +108,7 @@ export function useSnapPoints({
closeDrawer: () => void;
velocity: number;
}) {
if (typeof activeSnapPointOffset !== 'number') return;
const currentPosition = activeSnapPointOffset - draggedDistance;
const isOverlaySnapPoint = activeSnapPointIndex === fadeFromIndex - 1;
const isFirst = activeSnapPointIndex === 0;
Expand All @@ -115,13 +124,15 @@ export function useSnapPoints({
return;
}

if (velocity > 2 && draggedDistance > 0) {
snapToPoint(snapPointsOffset[snapPoints.length - 1]);
if (velocity > 2 && draggedDistance > 0 && snapPointsOffset && snapPoints) {
snapToPoint(snapPointsOffset[snapPoints.length - 1] as number);
return;
}

// Find the closest snap point to the current position
const closestSnapPoint = snapPointsOffset?.reduce((prev, curr) => {
if (typeof prev !== 'number' || typeof curr !== 'number') return prev;

return Math.abs(curr - currentPosition) < Math.abs(prev - currentPosition) ? curr : prev;
});

Expand Down Expand Up @@ -159,7 +170,7 @@ export function useSnapPoints({
}

function getPercentageDragged(absDraggedDistance: number, isDraggingDown: boolean) {
if (!snapPoints) return null;
if (!snapPoints || !activeSnapPointIndex || !snapPointsOffset) return null;
// If this is true we are dragging to a snap point that is supposed to have an overlay
const isOverlaySnapPoint = activeSnapPointIndex === fadeFromIndex - 1;
const isOverlaySnapPointOrHigher = activeSnapPointIndex >= fadeFromIndex;
Expand Down

0 comments on commit 6a73547

Please sign in to comment.