Created
August 21, 2024 20:52
-
-
Save deyvisonborges/c7de5ba1252d2cd3d9d60902a65dd6b4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useState, useEffect, useCallback, MouseEvent, TouchEvent } from 'react' | |
import * as S from './styles' | |
export default function DraggableBottomSheet({ isOpen }: { isOpen: boolean }) { | |
const [open, setIsOpen] = useState(isOpen) | |
const [isAnimating, setIsAnimating] = useState(false) // Controle da animação | |
const [isDragging, setIsDragging] = useState(false) | |
const [startY, setStartY] = useState(0) | |
const [translateY, setTranslateY] = useState(0) | |
const [shouldClose, setShouldClose] = useState(false) | |
useEffect(() => { | |
if (isOpen) { | |
setIsOpen(true) | |
setTranslateY(0) | |
setIsAnimating(false) | |
} else { | |
setIsOpen(false) | |
setTranslateY(0) | |
} | |
}, [isOpen]) | |
const handleDragStart = useCallback( | |
(event: MouseEvent<HTMLDivElement> | TouchEvent<HTMLDivElement>) => { | |
setIsDragging(true) | |
setIsAnimating(false) // Garante que a animação não é executada enquanto está arrastando | |
if (event.type === 'mousedown') { | |
setStartY((event as MouseEvent).clientY) | |
} else if (event.type === 'touchstart') { | |
setStartY((event as TouchEvent).touches[0].clientY) | |
} | |
}, | |
[] | |
) | |
const handleDragMove = useCallback( | |
(event: MouseEvent<HTMLDivElement> | TouchEvent<HTMLDivElement>) => { | |
if (!isDragging) return | |
let currentY: number | |
if (event.type === 'mousemove') { | |
currentY = (event as MouseEvent).clientY | |
} else if (event.type === 'touchmove') { | |
currentY = (event as TouchEvent).touches[0].clientY | |
} else { | |
return | |
} | |
const deltaY = currentY - startY | |
if (deltaY > 0) { | |
setTranslateY(deltaY) | |
setShouldClose(deltaY > 150) | |
} | |
}, | |
[isDragging, startY] | |
) | |
const handleDragEnd = () => { | |
setIsDragging(true) | |
if (shouldClose) { | |
setIsAnimating(true) // Inicia a animação de fechamento | |
} else { | |
setTranslateY(0) // Volta ao topo se o movimento foi insuficiente | |
} | |
document.body.style.overflow = '' | |
} | |
const handleAnimationEnd = () => { | |
if (shouldClose && isAnimating) { | |
setIsOpen(false) // Oculta após a animação | |
setIsAnimating(false) // Finaliza a animação | |
setTranslateY(100) | |
} | |
} | |
return ( | |
<S.Wrapper | |
isOpen={open} | |
isDragging={isDragging} | |
isAnimating={isAnimating} // Controla a visibilidade para a animação | |
translateY={translateY} // Passando a prop translateY para o styled-component | |
onAnimationEnd={handleAnimationEnd} // Dispara quando a animação termina | |
> | |
<S.Top | |
onTouchStart={handleDragStart} | |
onTouchMove={handleDragMove} | |
onTouchEnd={handleDragEnd} | |
> | |
<S.TopLine /> | |
</S.Top> | |
<div> | |
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p> | |
</div> | |
</S.Wrapper> | |
) | |
} | |
import styled, { css } from 'styled-components' | |
export const Wrapper = styled.div<{ | |
isOpen: boolean | |
isDragging: boolean | |
isAnimating: boolean | |
translateY: number // Nova prop | |
}>` | |
@media (max-width: 1024px) { | |
${({ theme, isOpen, isAnimating, isDragging, translateY }) => css` | |
background-color: red; | |
border-radius: ${theme.v2.borderRadiusLargecontainer}px | |
${theme.v2.borderRadiusLargecontainer}px 0 0; | |
display: flex; | |
flex-direction: column; | |
min-height: 100vh; | |
height: 100%; | |
overflow-y: auto; | |
pointer-events: ${isOpen || isAnimating ? 'all' : 'none'}; | |
position: fixed; | |
inset: 0; | |
width: 100%; | |
z-index: 1000; | |
visibility: ${isOpen || isAnimating ? 'visible' : 'hidden'}; | |
transform: translateY(${translateY}px); /* Usando a prop diretamente */ | |
${isOpen && | |
css` | |
${!isDragging && 'animation: appearToBottom 0.3s forwards'}; | |
`} | |
${isAnimating && | |
css` | |
animation: appearToTop 0.3s forwards; | |
`} | |
@keyframes appearToBottom { | |
from { | |
transform: translateY(100%); | |
} | |
to { | |
transform: translateY(0); | |
} | |
} | |
@keyframes appearToTop { | |
from { | |
transform: translateY(0); | |
} | |
to { | |
transform: translateY(100%); | |
} | |
} | |
`} | |
} | |
` | |
export const Top = styled.div` | |
${({ theme }) => css` | |
align-items: center; | |
display: flex; | |
justify-content: center; | |
padding-top: ${theme.v2.paddingInsetThreepulse}px; | |
padding-bottom: ${theme.v2.paddingInsetTwopulse}px; | |
cursor: grabbing; | |
`} | |
` | |
export const TopLine = styled.i` | |
${({ theme }) => css` | |
background-color: ${theme.v2.colorBorderNeutralAlternative}; | |
border-radius: 10px; | |
display: block; | |
height: 4px; | |
width: 24px; | |
`} | |
` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment