Skip to content

Instantly share code, notes, and snippets.

@deyvisonborges
Created August 21, 2024 20:52
Show Gist options
  • Save deyvisonborges/c7de5ba1252d2cd3d9d60902a65dd6b4 to your computer and use it in GitHub Desktop.
Save deyvisonborges/c7de5ba1252d2cd3d9d60902a65dd6b4 to your computer and use it in GitHub Desktop.
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