mirror of
https://github.com/zhigang1992/react.git
synced 2026-01-31 09:08:41 +08:00
95 lines
2.5 KiB
TypeScript
95 lines
2.5 KiB
TypeScript
import React, { useEffect, useRef, useState } from 'react'
|
|
import withDefaults from '../utils/with-defaults'
|
|
import useRealShape from '../utils/use-real-shape'
|
|
|
|
interface Props {
|
|
isExpanded?: boolean
|
|
delay?: number
|
|
}
|
|
|
|
const defaultProps = {
|
|
isExpanded: false,
|
|
delay: 200,
|
|
}
|
|
|
|
export type ExpandProps = Props & typeof defaultProps
|
|
|
|
const Expand: React.FC<React.PropsWithChildren<ExpandProps>> = ({
|
|
isExpanded,
|
|
delay,
|
|
children,
|
|
}) => {
|
|
const [height, setHeight] = useState<string>(isExpanded ? 'auto' : '0')
|
|
const [selfExpanded, setSelfExpanded] = useState<boolean>(isExpanded)
|
|
const [visible, setVisible] = useState<boolean>(isExpanded)
|
|
const contentRef = useRef<HTMLDivElement>(null)
|
|
const entryTimer = useRef<number>()
|
|
const leaveTimer = useRef<number>()
|
|
const resetTimer = useRef<number>()
|
|
const [state, updateShape] = useRealShape<HTMLDivElement>(contentRef)
|
|
|
|
useEffect(() => setHeight(`${state.height}px`), [state.height])
|
|
useEffect(() => {
|
|
// show element or reset height.
|
|
// force an update once manually, even if the element does not change.
|
|
// (the height of the element might be "auto")
|
|
if (isExpanded) {
|
|
setVisible(isExpanded)
|
|
} else {
|
|
updateShape()
|
|
setHeight(`${state.height}px`)
|
|
}
|
|
|
|
// show expand animation
|
|
entryTimer.current = window.setTimeout(() => {
|
|
setSelfExpanded(isExpanded)
|
|
clearTimeout(entryTimer.current)
|
|
}, 30)
|
|
|
|
// Reset height after animation
|
|
if (isExpanded) {
|
|
resetTimer.current = window.setTimeout(() => {
|
|
setHeight('auto')
|
|
clearTimeout(resetTimer.current)
|
|
}, delay)
|
|
} else {
|
|
leaveTimer.current = window.setTimeout(() => {
|
|
setVisible(isExpanded)
|
|
clearTimeout(leaveTimer.current)
|
|
}, delay / 2)
|
|
}
|
|
|
|
return () => {
|
|
clearTimeout(entryTimer.current)
|
|
clearTimeout(leaveTimer.current)
|
|
clearTimeout(resetTimer.current)
|
|
}
|
|
}, [isExpanded])
|
|
|
|
return (
|
|
<div className={`container ${selfExpanded ? 'expanded' : ''}`}>
|
|
<div ref={contentRef} className="content">
|
|
{children}
|
|
</div>
|
|
|
|
<style jsx>{`
|
|
.container {
|
|
padding: 0;
|
|
margin: 0;
|
|
height: 0;
|
|
overflow: hidden;
|
|
visibility: ${visible ? 'visible' : 'hidden'};
|
|
transition: height ${delay}ms ease;
|
|
}
|
|
|
|
.expanded {
|
|
height: ${height};
|
|
visibility: visible;
|
|
}
|
|
`}</style>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default withDefaults(Expand, defaultProps)
|