Created
October 1, 2022 02:51
-
-
Save tzynwang/d0edecb49bf283a6e4a0b8fe468f0882 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { memo, useState, useCallback, useEffect, useMemo } from 'react'; | |
import { css } from '@emotion/css'; | |
import cn from 'classnames'; | |
import type { AccordionProps } from './types'; | |
const defaultWrapperStyle = css({ width: '100%', border: '1px solid #333' }); | |
const defaultBodyStyle = css({ | |
padding: '24px', | |
borderTop: '1px solid #333', | |
backgroundColor: '#fffffe', | |
}); | |
function Accordion(props: AccordionProps): React.ReactElement { | |
/* States */ | |
const { | |
children, | |
classes = { wrapper: '', title: '', body: '' }, | |
open, | |
...rest | |
} = props; | |
delete rest.className; | |
const [localOpen, setLocalOpen] = useState<boolean>(false); | |
const [titleElement, setTitleElement] = useState<JSX.Element>( | |
<React.Fragment /> | |
); | |
const [bodyElement, setBodyElement] = useState<JSX.Element>( | |
<React.Fragment /> | |
); | |
const hasOpenFromProps = useMemo( | |
() => Object.keys(props).includes('open'), | |
[props] | |
); | |
const openToUse = useMemo( | |
() => (hasOpenFromProps ? open : localOpen), | |
[hasOpenFromProps, open, localOpen] | |
); | |
/* Functions */ | |
const toggleAccordion = useCallback((): void => { | |
setLocalOpen((prev) => !prev); | |
}, []); | |
/* Hooks */ | |
useEffect(() => { | |
React.Children.forEach(children, (child, index) => { | |
const childElement = child as JSX.Element; | |
if (index === 0) { | |
setTitleElement( | |
React.cloneElement(childElement, { | |
open: openToUse, | |
onClick: hasOpenFromProps ? undefined : toggleAccordion, | |
accordionTitleClass: classes.title, | |
}) | |
); | |
} | |
if (index === 1) { | |
setBodyElement( | |
React.cloneElement(childElement, { | |
open: openToUse, | |
accordionBodyClass: cn(openToUse && defaultBodyStyle, classes.body), | |
}) | |
); | |
} | |
}); | |
}, [ | |
children, | |
openToUse, | |
hasOpenFromProps, | |
classes.title, | |
classes.body, | |
toggleAccordion, | |
]); | |
/* Main */ | |
return ( | |
<div className={cn(defaultWrapperStyle, classes.wrapper)} {...rest}> | |
{titleElement} | |
{bodyElement} | |
</div> | |
); | |
} | |
export default memo(Accordion); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
export interface AccordionProps extends React.HTMLAttributes<HTMLDivElement> { | |
children: React.ReactNode; | |
open?: boolean; | |
classes?: Partial<AccordionClass>; | |
} | |
interface AccordionClass { | |
wrapper: string; | |
title: string; | |
body: string; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { memo, useMemo } from 'react'; | |
import { css } from '@emotion/css'; | |
import cn from 'classnames'; | |
import type { AccordionBodyProps } from './types'; | |
function AccordionBody(props: AccordionBodyProps): React.ReactElement { | |
/* States */ | |
const { open, children, accordionBodyClass = '', ...rest } = props; | |
delete rest.className; | |
const bodyStyle = useMemo( | |
() => | |
css({ | |
height: open ? 'auto' : '0px', | |
visibility: open ? 'unset' : 'hidden', | |
overflow: open ? 'visible' : 'hidden', | |
transform: `scaleY(${open ? '100%' : '0%'})`, | |
transformOrigin: 'top left', | |
transitionDuration: '.2s', | |
transitionProperty: 'transform, height' | |
}), | |
[open] | |
); | |
/* Main */ | |
return ( | |
<div className={cn(bodyStyle, accordionBodyClass)} {...rest}> | |
{children} | |
</div> | |
); | |
} | |
AccordionBody.displayName = 'AccordionBody'; | |
export default memo(AccordionBody); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
export interface AccordionBodyProps | |
extends React.HTMLAttributes<HTMLDivElement> { | |
open?: boolean; | |
children: React.ReactNode; | |
accordionBodyClass?: string; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { memo } from 'react'; | |
import { css } from '@emotion/css'; | |
import cn from 'classnames'; | |
import { ExpendLessIcon } from '@Assets/icons'; | |
import ButtonBase from '@Components/Base/ButtonBase'; | |
import type { AccordionTitleProps } from './types'; | |
const title = css({ | |
width: '100%', | |
minHeight: '48px', | |
display: 'flex', | |
justifyContent: 'space-between', | |
alignItems: 'center', | |
padding: '16px', | |
border: 'none', | |
backgroundColor: 'transparent', | |
fontSize: 'inherit', | |
}); | |
const iconStyle = css({ | |
transition: 'transform .2s ease', | |
}); | |
const transformIcon = css({ | |
transform: 'rotate(180deg)', | |
}); | |
function AccordionTitle(props: AccordionTitleProps): React.ReactElement { | |
/* States */ | |
const { children, accordionTitleClass = '', open, onClick, ...rest } = props; | |
delete rest.className; | |
/* Main */ | |
return ( | |
<ButtonBase className={cn(title, accordionTitleClass)} onClick={onClick}> | |
{children} | |
<ExpendLessIcon className={cn(iconStyle, open && transformIcon)} /> | |
</ButtonBase> | |
); | |
} | |
AccordionTitle.displayName = 'AccordionTitle'; | |
export default memo(AccordionTitle); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { ButtonBaseProps } from '@Components/Base/ButtonBase/types'; | |
export interface AccordionTitleProps extends ButtonBaseProps { | |
open?: boolean; | |
accordionTitleClass?: string; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment