@gssfed/vital-ui-kit-react
Version:
Vital UI Kit for React!
198 lines (178 loc) • 4.68 kB
JSX
/**
* @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;