Files
react/components/shared/expand.tsx
witt 7facec3849 feat(scaleable): add scaleable props to each component (#531)
* feat(scaleable): add scaleable props to each component

* chore(scaleable): update the exported type

* feat: apply scaleable to components

chore: remove with-default

test: improve testcase for scaleable

chore: resolve test warning

ci: upgrade nodejs to latest lts

docs: fix type error in document site

* docs: update documents to be compatible with scaleable

chore: fix build errors

* chore: remove all size-related attributes

docs: improve guide document

* docs: add scaleable documentation

test: update snapshots

chore: remove unused

* feat: add scaleable to grid components

* docs: improve docs

* test: update snapshots

* fix(grid): fix basic component props
2021-08-13 17:10:57 +08:00

94 lines
2.5 KiB
TypeScript

import React, { useEffect, useRef, useState } from 'react'
import useRealShape from '../utils/use-real-shape'
export type ExpandProps = {
isExpanded?: boolean
delay?: number
}
const defaultProps = {
isExpanded: false,
delay: 200,
}
const Expand: React.FC<React.PropsWithChildren<ExpandProps>> = ({
isExpanded,
delay,
children,
}: React.PropsWithChildren<ExpandProps> & typeof defaultProps) => {
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>
)
}
Expand.defaultProps = defaultProps
Expand.displayName = 'GeistExpand'
export default Expand