
BottomSheet组件
import React, { useEffect, useState, memo, type ReactElement } from 'react'
import { CloseOutlined } from '@ant-design/icons'
import style from "./BottomSheet.module.scss"
interface IProps {
title?: ReactElement,
children?: ReactElement,
visible: boolean,
onClose?: () => void
}
const minHeight = 48
const BottomSheet = (props: IProps) => {
const [height, setHeight] = useState(minHeight)
const [dragging, setDragging] = useState(false)
useEffect(() => {
if (dragging) {
document.addEventListener('mousemove', handleMouseMove)
document.addEventListener('mouseup', handleMouseUp)
}
return () => {
document.removeEventListener('mousemove', handleMouseMove)
document.removeEventListener('mouseup', handleMouseUp)
}
}, [dragging])
const handleMouseMove = (e: MouseEvent) => {
const maxHeight = window.innerHeight * 0.8
const newHeight = window.innerHeight - e.clientY
const moveHeight = Math.max(minHeight, Math.min(newHeight, maxHeight))
setHeight(moveHeight)
}
const handleMouseUp = () => {
setDragging(false)
}
const handleMouseDown = (e: React.MouseEvent) => {
e.preventDefault()
setDragging(true)
}
const closeSheet = () => {
props.onClose?.()
}
return (
<div className={style.sheet} style={{ height: props.visible ? height : 0 }}>
<div className={style.sheet_title} onMouseDown={handleMouseDown}>
<div className={style.sheet_title_handle} />
{props.title}
<CloseOutlined className={style.sheet_title_close} onClick={closeSheet} />
</div>
<div className={style.sheet_content}>
{props.children}
</div>
</div>
)
}
export default memo(BottomSheet)
.sheet {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
background-color: #fff;
border-radius: 16px 16px 0 0;
box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.2);
display: flex;
flex-direction: column;
&_title {
position: relative;
width: 100%;
height: 48px;
min-height: 48px;
padding: 8px 20px 0 20px;
border-bottom: 1px solid #DCDFE6;
cursor: move;
&_handle {
width: 41px;
height: 5px;
background-color: #DCDFE6;
border-radius: 4px;
margin: 0 auto;
}
&_close {
position: absolute;
top: 50%;
right: 20px;
transform: translateY(-50%);
font-size: 14px;
color: #303133;
}
}
&_content {
padding: 20px;
flex: 1;
overflow: auto;
}
}
使用
<BottomSheet visible={visible} title={<InputTitle />} onClose={() => setVisible(false)}>
<InputSetup />
</BottomSheet>