UNPKG

@gssfed/vital-ui-kit-react

Version:
198 lines (178 loc) 4.68 kB
/** * @flow * Copyright © 2018 Galaxy Software Services https://github.com/GSS-FED/vital-ui-kit-react * MIT license */ import * as React from 'react'; import styled from 'styled-components'; import { tween, styler, easing } from 'popmotion'; import { TitleWrapper, List, Title } from './styled'; import Badge from '../Badge'; import IconBase from '../Icon'; import SubListItem from './SubListItem'; const ICON_SIZE = 10; const Icon = styled(IconBase)` position: absolute; right: 15px; top: calc(50% - ${ICON_SIZE / 2}px); pointer-events: none; color: ${({ open, theme }) => (open ? `${theme.info}` : 'inherit')}; transform: ${props => props.open ? `rotateZ(-180deg)` : `rotateZ(0deg)`}; transition: all 0.05s ease-in; transform-origin: center center; `; const InnerWrapper = styled.ul` will-change: height; background-color: ${({ theme }) => theme.list.item.bg}; overflow: hidden; `; const BadgeWrapper = styled(Title)` text-align: right; padding-right: ${props => (props.hasIconRight ? '20px' : '0')}; `; type Props = { /** Children node inside ListItem, nested ListItem */ children?: React.Node, /** Expand state, will toggle open on click by default */ open?: boolean, /** The title of the current ListItem */ title: Node, /** Whether it has link ref style */ hasLink: boolean, /** Badge on right, show if exist */ badge?: Node, /** `onClick`, **it will not override the default expand event** */ onClick?: () => mixed, /** @private Light or Dark theme */ themed: 'light' | 'dark', /** @private Check if it is a children */ level?: number, /** @private Pass down from ListGroup */ collapse: boolean, /** @private */ dispatchClose: (level: number) => mixed, /** @private */ border: boolean, }; type State = { open: boolean, }; class ListItem extends React.Component<Props, State> { static defaultProps = { children: null, level: 0, isChildren: false, open: false, badge: null, onClick: null, }; state = { open: this.props.open || false, }; componentDidMount() { if (this.child) { this.child.style.display = this.state.open ? '' : 'none'; } } onItemClick = () => { if (this.child) { if (!!this.props.collapse && !this.state.open) { this.props.dispatchClose(this.props.level || 0); } this.startAnimation(); if (this.props.onClick) { this.props.onClick(); } } }; startAnimation = () => { const stylerBall = styler(this.child); // Animation after state change if (!this.state.open) { this.child.style.display = ''; } tween({ from: { height: this.state.open ? this.child.clientHeight : 0 }, to: { height: this.state.open ? 0 : this.child.clientHeight }, duration: 200, ease: this.state.open ? easing.easeIn : easing.easeOut, }).start({ update: stylerBall.set, complete: () => { this.setState( prevState => ({ open: !prevState.open, }), () => { setTimeout(() => { if (!this.state.open) { this.child.style.display = 'none'; } this.child.style.height = ''; }, 50); }, ); }, }); }; iconHandler = () => this.props.children ? 'chevron-down' : 'chevron-right'; child: ?HTMLElement; renderBadge = () => ( <BadgeWrapper hasIconRight={this.props.children || this.props.hasLink} > <Badge label={this.props.badge} /> </BadgeWrapper> ); render() { const { title, children, hasLink, level, themed, badge, border, } = this.props; return ( <List> <TitleWrapper hasChildren={!!children} hasLink={hasLink} onClick={this.onItemClick} level={level} border={border} themed={themed} > <Title>{title}</Title> {badge && this.renderBadge()} {(children || hasLink) && ( <Icon open={this.state.open} name={this.iconHandler()} size={ICON_SIZE} /> )} </TitleWrapper> {children && ( <InnerWrapper innerRef={s => { this.child = s; }} > <SubListItem isChildren={!!children} level={level + 1} themed={themed} > {children} </SubListItem> </InnerWrapper> )} </List> ); } } export default ListItem;