
This raises the content inside the modal wrapper and stops propagation of clicks. This fixes #5527 which closed the modal when people were trying to select a different tab in the modal content. On desktop, you can still close the modal by clicking outside of the content. However, on mobile, the modal fills the entire screen so there is no way to click outside and close the modal. So this also includes a change that adds a close button to the bottom right corner (only shown on mobile).
94 lines
2.8 KiB
JavaScript
94 lines
2.8 KiB
JavaScript
// __SDEFILE__ - This file is a dependency for the stand-alone environment
|
|
import { useState, useEffect, useContext } from 'react'
|
|
import { useSwipeable } from 'react-swipeable'
|
|
import { ModalContext } from 'shared/context/modal-context.mjs'
|
|
import { CloseIcon } from 'shared/components/icons.mjs'
|
|
|
|
const slideClasses = {
|
|
left: '-translate-x-full',
|
|
right: 'translate-x-full',
|
|
top: '-translate-y-full',
|
|
bottom: 'translate-y-full',
|
|
}
|
|
|
|
export const ModalWrapper = ({
|
|
children = null,
|
|
flex = 'row',
|
|
justify = 'center',
|
|
items = 'center',
|
|
bg = 'base-100 lg:bg-base-300',
|
|
bgOpacity = '100 lg:bg-opacity-95',
|
|
bare = false,
|
|
keepOpenOnClick = false,
|
|
slideFrom = 'left',
|
|
keepOpenOnSwipe = false,
|
|
fullWidth = false,
|
|
}) => {
|
|
const { clearModal } = useContext(ModalContext)
|
|
const [animate, setAnimate] = useState('in')
|
|
|
|
const close = (evt) => {
|
|
// Only process the first swipe event
|
|
if (evt?.event) evt.event.stopPropagation()
|
|
setAnimate('out')
|
|
window.setTimeout(clearModal, 150)
|
|
}
|
|
|
|
const swipeActions = {}
|
|
if (!keepOpenOnSwipe) {
|
|
if (slideFrom === 'left') swipeActions.onSwipedLeft = close
|
|
else if (slideFrom === 'right') swipeActions.onSwipedRight = close
|
|
else if (slideFrom === 'top') swipeActions.onSwipedUp = close
|
|
else if (slideFrom === 'bottom') swipeActions.onSwipedDown = close
|
|
}
|
|
|
|
const swipeHandlers = useSwipeable({
|
|
...swipeActions,
|
|
trackMouse: true,
|
|
})
|
|
|
|
useEffect(() => {
|
|
// only turn off animation if it's animating in
|
|
if (animate === 'in') setAnimate(false)
|
|
}, [animate])
|
|
|
|
// CSS classes for animation
|
|
const animation = animate
|
|
? `lg:opacity-0 ${slideClasses[slideFrom]} lg:translate-x-0 lg:translate-y-0`
|
|
: 'opacity-100 translate-none'
|
|
|
|
const stopClick = (evt) => evt.stopPropagation()
|
|
|
|
return (
|
|
<div
|
|
ref={swipeHandlers.ref}
|
|
onMouseDown={swipeHandlers.onMouseDown}
|
|
className={`fixed top-0 left-0 m-0 p-0 shadow w-full h-screen
|
|
transform-all duration-150 ${animation}
|
|
bg-${bg} bg-opacity-${bgOpacity} z-50 hover:cursor-pointer
|
|
flex flex-${flex} justify-${justify} items-${items} lg:p-12`}
|
|
onClick={keepOpenOnClick ? null : close}
|
|
>
|
|
{bare ? (
|
|
children
|
|
) : (
|
|
<div
|
|
onClick={stopClick}
|
|
className={`z-30 bg-base-100 p-4 lg:px-8 lg:rounded-lg lg:shadow-lg max-h-full overflow-auto hover:cursor-default ${
|
|
fullWidth ? 'w-full' : ''
|
|
}`}
|
|
>
|
|
{children}
|
|
{!keepOpenOnClick && (
|
|
<button
|
|
className="fixed bottom-2 right-2 btn btn-neutral btn-circle lg:hidden"
|
|
onClick={keepOpenOnClick ? null : close}
|
|
>
|
|
<CloseIcon className="w-8 h-8" />
|
|
</button>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|