Skip to content

Instantly share code, notes, and snippets.

@juliobetta
Created August 6, 2016 18:19
Show Gist options
  • Save juliobetta/950a549e4e74abefb3f65c16d01db44d to your computer and use it in GitHub Desktop.
Save juliobetta/950a549e4e74abefb3f65c16d01db44d to your computer and use it in GitHub Desktop.
Timeline element for React toolbox
$timeline-item-marging: 8px;
$timeline-top-spacer: 20px + $timeline-item-marging;
$center-padding: 4rem;
$row-spacing: 1rem;
$line-height: 56px;
/*----------------------------------------------------------------*/
/* Animations
/*----------------------------------------------------------------*/
@mixin transition() {
-webkit-backface-visibility: hidden;
-webkit-perspective: 1000px;
}
@mixin easingOutQuadTimingTransition($duration) {
@include transition();
transition-property: none;
transition-duration: $duration;
transition-timing-function: cubic-bezier(0.250, 0.460, 0.450, 0.940);
}
/*----------------------------------------------------------------*/
/* @ Custom Animation Keyframes
/*----------------------------------------------------------------*/
@keyframes slide-in-left {
0% {
opacity: 0;
transform: translateX(-500px);
}
65% {
opacity: 1;
transform: translateX(0);
}
100% {
transform: translateX(0);
}
}
@keyframes slide-in-right {
0% {
opacity: 0;
transform: translateX(500px);
}
65% {
opacity: 1;
transform: translateX(0);
}
100% {
transform: translateX(0);
}
}
@keyframes slide-in-bottom {
0% {
opacity: 0;
transform: translateY(200px);
}
65% {
opacity: 1;
transform: translateY(0);
}
100% {
transform: translateY(0);
}
}
@keyframes slide-in-top {
0% {
opacity: 0;
transform: translateY(-200px);
}
65% {
opacity: 1;
transform: translateY(0);
}
100%{
transform: translateY(0);
}
}
.animate-rotate {
animation: rotate 1.75s linear infinite;
}
@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg)
}
}
import Timeline from './Timeline';
import TimelineItem from './TimelineItem';
export {Timeline, TimelineItem};
@import "../../scss/shared/animations.scss";
@import "../../scss/shared/variables.scss";
@import "./config.scss";
.timeline {
flex-grow: 1;
width: 100%;
display: flex;
flex-direction: column;
position: relative;
padding: 0 4rem;
}
.timeline:before {
display: block;
position: absolute;
z-index: 10;
content: "";
width: 2px;
background: rgba(0,0,0,.08);
top: 0;
left: 50%;
bottom: 0;
margin-left: -1px;
}
.timelineItem {
overflow: hidden;
position: relative;
z-index: 20;
padding: $row-spacing 0;
width: 100%;
margin: 0 auto;
display: flex;
flex-direction: row;
align-content: flex-start;
justify-content: center;
&.inverse {
.timelineCard {
order: 3;
animation: slide-in-right 0.4s;
flex-direction: row;
padding-left: $center-padding;
}
.timelineDetail {
order: 1;
flex-direction: row-reverse;
display: flex;
animation: slide-in-left 0.4s;
> div {
padding-left: 0;
padding-right: $center-padding;
}
}
}
.timelinePoint {
position: absolute;
width: $line-height;
height: $line-height;
line-height: $line-height;
text-align: center;
border-radius: 50%;
left: 50%;
animation: slide-in-top 0.4s;
margin-top: $timeline-item-marging;
margin-right: 0;
margin-bottom: -$timeline-item-marging;
margin-left: -$timeline-top-spacer;
order: 2;
background-color: rgb(3,155,229);
color: rgba(255,255,255,1);
:global {
.material-icons {
font-size: 3.5rem;
margin-top: 1.1rem;
}
}
}
.timelineCard {
order: 1;
animation: slide-in-left 0.4s;
box-sizing: border-box;
flex: 1 1 50%;
display: flex;
flex-direction: row-reverse;
padding-right: $center-padding;
> div {
width: auto;
}
}
.timelineDetail {
animation: slide-in-right 0.4s;
order: 3;
flex: 50% 0 0;
padding-top: $timeline-top-spacer;
> div {
padding-left: $center-padding;
}
.time {}
.detailText {
color: $ugly-text-dark-grey;
}
}
}
import React from 'react';
import compose from 'recompose/compose';
import classNames from 'classnames';
import styles from './styles.scss';
import pure from 'recompose/pure';
const Timeline = ({className, children}) => {
const classes = classNames(className, styles.timeline);
return (<div className={classes}>{children}</div>);
};
Timeline.propTypes = {
className: React.PropTypes.string,
children: React.PropTypes.oneOfType([React.PropTypes.element, React.PropTypes.array])
};
export {Timeline};
export default pure(Timeline);
import React from 'react';
import compose from 'recompose/compose';
import classNames from 'classnames';
import styles from './styles.scss';
import pure from 'recompose/pure';
import {convertToLocal} from '../../helpers/dates';
const TimelineItem = ({
className,
inverse = false,
children,
date,
icon = 'note',
iconClassName = '',
details = '',
detailsClassName = '',
cardClassName = ''
}) => {
const classes = classNames(className, {
[styles.inverse]: inverse
}, styles.timelineItem);
const iconClasses = classNames(iconClassName, styles.timelinePoint);
const dateStr = typeof(date) === 'function' ? date() : convertToLocal(date);
const detailClasses = classNames(detailsClassName, styles.timelineDetail);
const cardClasses = classNames(cardClassName, styles.timelineCard);
return (
<div className={classes}>
<div className={iconClasses}>
<i className="material-icons">{icon}</i>
</div>
<div className={cardClasses}>{children}</div>
<div className={detailClasses}>
<div>
<div className={styles.time}>{dateStr}</div>
<div className={styles.detailText}>{details}</div>
</div>
</div>
</div>
);
};
TimelineItem.propTypes = {
date: React.PropTypes.oneOfType([
React.PropTypes.object,
React.PropTypes.string,
React.PropTypes.func
]).isRequired,
children: React.PropTypes.element.isRequired,
icon: React.PropTypes.string,
details: React.PropTypes.string,
iconClassName: React.PropTypes.string,
detailsClassName: React.PropTypes.string,
cardClassName: React.PropTypes.string,
className: React.PropTypes.string,
inverse: React.PropTypes.bool
};
export {TimelineItem};
export default pure(TimelineItem);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment