├── .gitignore
├── doc
├── screenshot.png
└── screencapture.gif
├── .babelrc
├── src
├── index.js
├── config.js
├── entry.js
├── timeline.js
└── content.js
├── .vscode
└── settings.json
├── .travis.yml
├── example
├── index.html
└── index.js
├── dist
├── index.js
├── config.js
├── entry.js
├── timeline.js
└── content.js
├── webpack.config.js
├── test
└── timeline.test.js
├── .eslintrc.yml
├── webpack.prod.config.js
├── package.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /example/bundle.js
3 |
--------------------------------------------------------------------------------
/doc/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ftes/react-dual-timeline/HEAD/doc/screenshot.png
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "react"],
3 | "plugins": ["transform-object-rest-spread"]
4 | }
--------------------------------------------------------------------------------
/doc/screencapture.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ftes/react-dual-timeline/HEAD/doc/screencapture.gif
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import Timeline from './timeline'
2 | export { default as Timeline } from './timeline.js'
3 | export default Timeline
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | "editor.tabSize": 2
4 | }
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - node
4 | cache:
5 | yarn: true
6 | directories:
7 | - node_modules
8 | before_script:
9 | - yarn install
10 | script:
11 | - yarn test
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/dist/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.Timeline = undefined;
7 |
8 | var _timeline = require('./timeline.js');
9 |
10 | Object.defineProperty(exports, 'Timeline', {
11 | enumerable: true,
12 | get: function get() {
13 | return _interopRequireDefault(_timeline).default;
14 | }
15 | });
16 |
17 | var _timeline2 = require('./timeline');
18 |
19 | var _timeline3 = _interopRequireDefault(_timeline2);
20 |
21 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
22 |
23 | exports.default = _timeline3.default;
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var webpack = require('webpack')
4 |
5 | module.exports = {
6 |
7 | entry: ['./example/index.js'],
8 |
9 | devtool: 'inline-source-map',
10 |
11 | output: {
12 | filename: './example/bundle.js',
13 | pathinfo: true
14 | },
15 |
16 | module: {
17 | loaders: [{
18 | loader: 'babel-loader',
19 | test: /\.js$/,
20 | exclude: /node_modules/,
21 | query: {
22 | cacheDirectory: true,
23 | presets: ['react', 'es2015']
24 | }
25 | }]
26 | },
27 |
28 | plugins: [
29 | new webpack.LoaderOptionsPlugin({
30 | debug: false
31 | })
32 | ]
33 |
34 | }
--------------------------------------------------------------------------------
/test/timeline.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { shallow, configure } from 'enzyme'
3 | import Adapter from 'enzyme-adapter-react-16'
4 |
5 | configure({ adapter: new Adapter() })
6 |
7 | import Timeline from '../src'
8 |
9 | describe('Timeline', () => {
10 | it('passes smoke test', () => {
11 | shallow()
12 | })
13 |
14 | it('inserts children', () => {
15 | const tl =
16 | shallow(1
2
3
)
17 | expect(tl.contains(1
)).toEqual(true)
18 | expect(tl.contains(2
)).toEqual(true)
19 | expect(tl.contains(3
)).toEqual(true)
20 | })
21 | })
--------------------------------------------------------------------------------
/.eslintrc.yml:
--------------------------------------------------------------------------------
1 | root: true
2 | env:
3 | es6: true
4 | browser: true
5 | jasmine: true
6 | parserOptions:
7 | ecmaFeatures:
8 | experimentalObjectRestSpread: true
9 | jsx: true
10 | sourceType: module
11 | plugins:
12 | - react
13 | - jasmine
14 | extends:
15 | - eslint:recommended
16 | - plugin:react/recommended
17 | - plugin:jasmine/recommended
18 | rules:
19 | indent:
20 | - error
21 | - 2
22 | linebreak-style:
23 | - error
24 | - unix
25 | quotes:
26 | - error
27 | - single
28 | semi:
29 | - error
30 | - never
31 | no-console: off
32 | max-len:
33 | - error
34 | - 80
35 | object-curly-spacing:
36 | - error
37 | - always
38 | jsx-quotes:
39 | - error
40 | - prefer-single
41 | react/jsx-uses-react:
42 | - error
43 |
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | // global
3 | paddingTop: 50,
4 | mediaWidthMed: 900,
5 | mediaWidthSmall: 700,
6 | activeColor: '#F45B69',
7 | color: 'black',
8 | twoSidedOverlap: 80, // negative overlap between items if two-sided
9 | animations: true,
10 | addEvenPropToChildren: false,
11 |
12 | // line
13 | lineColor: '#FFF',
14 | circleWidth: 30,
15 | paddingToItem: 30,
16 | paddingToItemSmall: 20,
17 | lineWidth: 5,
18 |
19 | // triangle
20 | triangleWidth: 16,
21 | triangleHeight: 8,
22 |
23 | // list item content
24 | itemWidth: 350,
25 | itemWidthMed: 250,
26 | offsetHidden: 200,
27 | triangleOffset: 7,
28 | smallItemWidthPadding: 50,
29 | itemPadding: 16,
30 | evenItemOffset: 0, // important when using bootstrap.css
31 | }
32 |
33 | export default config
--------------------------------------------------------------------------------
/dist/config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | var config = {
7 | // global
8 | paddingTop: 50,
9 | mediaWidthMed: 900,
10 | mediaWidthSmall: 700,
11 | activeColor: '#F45B69',
12 | color: 'black',
13 | twoSidedOverlap: 80, // negative overlap between items if two-sided
14 | animations: true,
15 | addEvenPropToChildren: false,
16 |
17 | // line
18 | lineColor: '#FFF',
19 | circleWidth: 30,
20 | paddingToItem: 30,
21 | paddingToItemSmall: 20,
22 | lineWidth: 5,
23 |
24 | // triangle
25 | triangleWidth: 16,
26 | triangleHeight: 8,
27 |
28 | // list item content
29 | itemWidth: 350,
30 | itemWidthMed: 250,
31 | offsetHidden: 200,
32 | triangleOffset: 7,
33 | smallItemWidthPadding: 50,
34 | itemPadding: 16,
35 | evenItemOffset: 0 // important when using bootstrap.css
36 | };
37 |
38 | exports.default = config;
--------------------------------------------------------------------------------
/webpack.prod.config.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var webpack = require('webpack')
4 |
5 | module.exports = {
6 |
7 | entry: ['./example/index.js'],
8 |
9 | output: {
10 | filename: './example/bundle.js',
11 | pathinfo: true
12 | },
13 |
14 | module: {
15 | loaders: [{
16 | loader: 'babel-loader',
17 | test: /\.js$/,
18 | exclude: /node_modules/,
19 | query: {
20 | cacheDirectory: true,
21 | presets: ['react', 'es2015']
22 | }
23 | }]
24 | },
25 |
26 | plugins: [
27 | new webpack.DefinePlugin({
28 | 'process.env': {
29 | 'NODE_ENV': JSON.stringify('production')
30 | }
31 | }),
32 | new webpack.optimize.UglifyJsPlugin({
33 | minimize: true,
34 | compress: { // hide warnings (unused var, always true etc.)
35 | warnings: false
36 | }
37 | }),
38 | new webpack.LoaderOptionsPlugin({
39 | debug: false
40 | })
41 | ]
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/example/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render } from 'react-dom'
3 | import { StyleRoot } from 'radium'
4 |
5 | import Timeline from '../src/timeline'
6 |
7 |
8 | render(
9 |
10 |
11 | Entry 1
12 |
13 |
Entry 2
14 |
15 | - arbitrary content in entries
16 |
17 |
18 | {[0,1,2,3,4,5,6,7,8,9,10].map(i =>
19 |
20 |
{i}
21 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
22 | eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
23 | voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
24 | clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
25 | amet.
26 |
27 | )}
28 |
29 | ,
30 | document.getElementById('root')
31 | )
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-dual-timeline",
3 | "version": "0.3.0",
4 | "description": "Timeline Component for React",
5 | "keywords": [
6 | "react",
7 | "timeline",
8 | "component",
9 | "dual",
10 | "two-sided"
11 | ],
12 | "repository": "https://github.com/ftes/react-dual-timeline.git",
13 | "author": "Fredrik Teschke",
14 | "license": "MIT",
15 | "bugs": {
16 | "url": "https://github.com/ftes/react-dual-timeline/issues"
17 | },
18 | "homepage": "https://github.com/ftes/react-dual-timeline#readme",
19 | "main": "dist",
20 | "scripts": {
21 | "build": "babel src --out-dir dist",
22 | "patch": "npm run build && git add dist && git commit -m 'build new dist' && npm version patch && npm publish && git push",
23 | "dev": "babel src --out-dir dist --watch",
24 | "build-example": "webpack -p --config=webpack.prod.config.js",
25 | "dev-example": "webpack-dev-server --host 0.0.0.0 --hot --inline",
26 | "test": "jest",
27 | "start": "npm run dev-example"
28 | },
29 | "devDependencies": {
30 | "babel-cli": "^6.26.0",
31 | "babel-jest": "^22.1.0",
32 | "babel-loader": "^6.2.4",
33 | "babel-plugin-transform-object-rest-spread": "^6.26.0",
34 | "babel-preset-es2015": "^6.24.1",
35 | "babel-preset-react": "^6.24.1",
36 | "enzyme": "^3.3.0",
37 | "enzyme-adapter-react-16": "^1.1.1",
38 | "eslint": "^4.17.0",
39 | "eslint-plugin-jasmine": "^2.9.1",
40 | "eslint-plugin-react": "^7.6.1",
41 | "jest": "^22.1.4",
42 | "jest-cli": "^22.1.4",
43 | "radium": "^0.21.2",
44 | "react": "^16.2.0",
45 | "react-addons-test-utils": "^15.6.2",
46 | "react-dom": "^16.2.0",
47 | "webpack": "^3.10.0",
48 | "webpack-dev-server": "^2.11.1"
49 | },
50 | "dependencies": {
51 | "prop-types": "^15.6.0"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **Deprecated. Consider switching to [react-vertical-timeline-component](https://www.npmjs.com/package/react-vertical-timeline-component) or using [vanilla CSS](https://codepen.io/savalazic/pen/QKwERN/).**
2 |
3 | [](https://travis-ci.org/ftes/react-dual-timeline)
4 | [](https://www.npmjs.com/package/react-dual-timeline)
5 |
6 | # React Timeline
7 | A react component for animated timelines.
8 |
9 | 
10 |
11 | Based on the [CSS and Javascript template](https://webdesign.tutsplus.com/tutorials/building-a-vertical-timeline-with-css-and-a-touch-of-javascript--cms-26528)
12 | by George Martsoukos ([CodePen](http://codepen.io/tutsplus/full/QNeJgR/)).
13 |
14 | ## Usage
15 | For a full example see [example/index.js](./example/index.js).
16 |
17 | Requirements: `radium`
18 |
19 | ```
20 | import Timeline from 'timeline'
21 |
22 | render(
23 |
24 |
25 | Arbitrary entry
26 | Arbitrary entry
27 |
28 |
29 | )
30 | ```
31 |
32 | A custom icon can (optionally) be provided for each entry.
33 |
34 | ## Configuration
35 | [src/config.js](./src/config.js) holds the default configuration.
36 |
37 | Alternative values can be passed to the `` component,
38 | e.g. `
71 |
72 |
75 | {children}
76 |
77 |
78 |
82 | {icon}
83 |
84 |
85 | )
86 | }
87 | }
88 |
89 | Entry.propTypes = {
90 | children: PropTypes.node.isRequired,
91 | even: PropTypes.bool.isRequired,
92 | config: PropTypes.object.isRequired,
93 | icon: PropTypes.node,
94 | }
95 |
96 | export default Radium(Entry)
--------------------------------------------------------------------------------
/src/timeline.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import Radium from 'radium'
4 |
5 | import Entry from './entry'
6 | import defaultConfig from './config'
7 |
8 | export class Timeline extends React.Component {
9 | constructor(props) {
10 | super(props)
11 | this.state = { twoSided: true }
12 | this.onTwoSidedChange = this.onTwoSidedChange.bind(this)
13 | this.componentWillReceiveProps(props)
14 | }
15 |
16 | /**
17 | * Merge config with default only once (optimize)
18 | */
19 | componentWillReceiveProps(newProps) {
20 | //eslint-disable-next-line no-unused-vars
21 | const { children, ...config } = newProps // children are not config
22 | this.mergedConfig = {
23 | ...defaultConfig,
24 | ...config,
25 | }
26 | }
27 |
28 | componentWillMount() {
29 | const { mediaWidthSmall } = this.mergedConfig
30 | if (window && window.matchMedia) {
31 | this.mqTwoSided = window.matchMedia(`(min-width: ${mediaWidthSmall}px)`)
32 | this.mqTwoSided.addListener(this.onTwoSidedChange)
33 | this.onTwoSidedChange(this.mqTwoSided)
34 | }
35 | }
36 |
37 | componentWillUnmount() {
38 | if (this.mqTwoSided) {
39 | this.mqTwoSided.removeListener(this.onTwoSidedChange)
40 | }
41 | }
42 |
43 | onTwoSidedChange(mq) {
44 | this.setState({ twoSided: mq.matches })
45 | }
46 |
47 | render() {
48 | const { children } = this.props
49 | const { color, twoSidedOverlap } = this.mergedConfig
50 | const twoSided = this.state.twoSided
51 | let i = 0
52 |
53 | const styles = {
54 | base: {
55 | textAlign: 'center',
56 | paddingBottom: twoSided && twoSidedOverlap + 'px',
57 | color: color,
58 | overflow: 'hidden',
59 | [this.mqTwoSidedString]: {
60 | marginBottom: twoSidedOverlap + 'px',
61 | }
62 | }
63 | }
64 |
65 | return (
66 |
67 | {React.Children.map(children, c =>
68 |
70 | {c}
71 |
72 | )}
73 |
74 | )
75 | }
76 | }
77 |
78 | Timeline.propTypes = {
79 | children: PropTypes.node.isRequired,
80 |
81 | // global
82 | paddingTop: PropTypes.number,
83 | mediaWidthMed: PropTypes.number,
84 | mediaWidthSmall: PropTypes.number,
85 | activeColor: PropTypes.string,
86 | color: PropTypes.string,
87 | twoSidedOverlap: PropTypes.number,
88 | animations: PropTypes.bool,
89 | addEvenPropToChildren: PropTypes.bool,
90 |
91 | // line
92 | lineColor: PropTypes.string,
93 | circleWidth: PropTypes.number,
94 | paddingToItem: PropTypes.number,
95 | paddingToItemSmall: PropTypes.number,
96 | lineWidth: PropTypes.number,
97 |
98 | // triangle
99 | triangleWidth: PropTypes.number,
100 | triangleHeight: PropTypes.number,
101 |
102 | // list item content
103 | itemWidth: PropTypes.number,
104 | itemWidthMed: PropTypes.number,
105 | offsetHidden: PropTypes.number,
106 | triangleOffset: PropTypes.number,
107 | smallItemWidthPadding: PropTypes.number,
108 | itemPadding: PropTypes.number,
109 | evenItemOffset: PropTypes.number,
110 | }
111 |
112 | export default Radium(Timeline)
113 |
--------------------------------------------------------------------------------
/src/content.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import Radium from 'radium'
4 |
5 | export class Content extends React.Component {
6 | constructor(props) {
7 | super(props)
8 | this.onScroll = this.onScroll.bind(this)
9 | }
10 |
11 | areChildrenInView() {
12 | const rect = this.children.getBoundingClientRect()
13 | const vwHeight = window.innerHeight || document.documentElement.clientHeight
14 | return (
15 | ( rect.bottom >= 0 && rect.bottom <= vwHeight ) ||
16 | ( rect.top >= 0 && rect.top <= vwHeight )
17 | )
18 | }
19 |
20 | componentDidMount() {
21 | if (this.props.config.animations) {
22 | window.addEventListener('scroll', this.onScroll)
23 | window.addEventListener('resize', this.onScroll)
24 | this.onScroll()
25 | }
26 | }
27 |
28 | componentWillUnmount() {
29 | window.removeEventListener('scroll', this.onScroll)
30 | window.removeEventListener('resize', this.onScroll)
31 | }
32 |
33 | onScroll() {
34 | const inView = this.areChildrenInView()
35 | this.props.onInView(inView)
36 | }
37 |
38 | render() {
39 | const { children, even, inView, config } = this.props
40 |
41 | const { circleWidth, mediaWidthMed, paddingToItem, paddingToItemSmall,
42 | itemWidth, itemWidthMed, animations, lineWidth, evenItemOffset,
43 | offsetHidden, triangleWidth, triangleOffset, triangleHeight,
44 | activeColor, mediaWidthSmall, smallItemWidthPadding, itemPadding }
45 | = config
46 |
47 | const offsetEven = circleWidth + paddingToItem + triangleWidth - lineWidth
48 | + evenItemOffset
49 | const offsetEvenNml = offsetEven + itemWidth
50 | const offsetEvenMed = offsetEven + itemWidthMed
51 |
52 | const triangleLeft = {
53 | right: `-${triangleWidth - 1}px`,
54 | borderWidth:
55 | `${triangleHeight}px 0 ${triangleHeight}px ${triangleWidth}px`,
56 | borderColor: `transparent transparent transparent ${activeColor}`,
57 | }
58 | const triangleRight = {
59 | left: `-${triangleWidth - 1}px`,
60 | borderWidth:
61 | `${triangleHeight}px ${triangleWidth}px ${triangleHeight}px 0`,
62 | borderColor: `transparent ${activeColor} transparent transparent`,
63 | }
64 |
65 | const mediaMed = `@media screen and (min-width: ${mediaWidthSmall}px)
66 | and (max-width: ${mediaWidthMed}px)`
67 | const mediaSmall = `@media screen and (max-width: ${mediaWidthSmall}px)`
68 | const mediaPrint = '@media print'
69 |
70 | const styles = {
71 | base: {
72 | position: 'relative',
73 | bottom: '0',
74 | width: itemWidth + 'px',
75 | padding: itemPadding + 'px',
76 | background: activeColor,
77 | visibility: animations ? 'hidden' : null,
78 | opacity: animations ? 0 : 1,
79 | transition: animations ? 'all .5s ease-in-out' : null,
80 | [mediaMed]: {
81 | width: itemWidthMed + 'px',
82 | },
83 | [mediaSmall]: {
84 | width: `calc(100vw - ${triangleWidth + circleWidth +
85 | smallItemWidthPadding}px)`,
86 | },
87 | [mediaPrint]: {
88 | width: '100%',
89 | left: 0,
90 | transform: null,
91 | }
92 | },
93 | inView: {
94 | transform: 'none',
95 | visibility: 'visible',
96 | opacity: '1',
97 | },
98 | even: {
99 | left: `-${offsetEvenNml}px`,
100 | transform: animations ? `translate3d(-${offsetHidden}px,0,0)` : null,
101 | [mediaMed]: {
102 | left: `-${offsetEvenMed}px`,
103 | },
104 | [mediaSmall]: {
105 | left: paddingToItemSmall + triangleWidth + 'px',
106 | },
107 | },
108 | odd: {
109 | left: paddingToItem + triangleWidth + 'px',
110 | transform: animations ? `translate3d(${offsetHidden}px,0,0)` : null,
111 | [mediaSmall]: {
112 | left: paddingToItemSmall + triangleWidth + 'px',
113 | },
114 | },
115 | triangle: {
116 | base: {
117 | position: 'absolute',
118 | bottom: triangleOffset + 'px',
119 | width: '0',
120 | height: '0',
121 | borderStyle: 'solid',
122 | },
123 | even: {
124 | ...triangleLeft,
125 | [mediaSmall]: triangleRight
126 | },
127 | odd: triangleRight,
128 | }
129 | }
130 |
131 | let propsToAdd = {}
132 | if (config.addEvenPropToChildren) {
133 | propsToAdd = {
134 | ...propsToAdd,
135 | even,
136 | }
137 | }
138 |
139 | return (
140 |
145 |
150 |
this.children = c}>
151 | {React.cloneElement(children, propsToAdd)}
152 |
153 |
154 | )
155 | }
156 | }
157 |
158 | Content.propTypes = {
159 | children: PropTypes.node.isRequired,
160 | even: PropTypes.bool.isRequired,
161 | inView: PropTypes.bool.isRequired,
162 | onInView: PropTypes.func,
163 | config: PropTypes.object.isRequired,
164 | }
165 |
166 | export default Radium(Content)
167 |
--------------------------------------------------------------------------------
/dist/entry.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.Entry = undefined;
7 |
8 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
9 |
10 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
11 |
12 | var _react = require('react');
13 |
14 | var _react2 = _interopRequireDefault(_react);
15 |
16 | var _propTypes = require('prop-types');
17 |
18 | var _propTypes2 = _interopRequireDefault(_propTypes);
19 |
20 | var _radium = require('radium');
21 |
22 | var _radium2 = _interopRequireDefault(_radium);
23 |
24 | var _content = require('./content');
25 |
26 | var _content2 = _interopRequireDefault(_content);
27 |
28 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
29 |
30 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
31 |
32 | function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
33 |
34 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
35 |
36 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
37 |
38 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
39 |
40 | var Entry = exports.Entry = function (_React$Component) {
41 | _inherits(Entry, _React$Component);
42 |
43 | function Entry(props) {
44 | _classCallCheck(this, Entry);
45 |
46 | var _this = _possibleConstructorReturn(this, (Entry.__proto__ || Object.getPrototypeOf(Entry)).call(this, props));
47 |
48 | _this.onInView = _this.onInView.bind(_this);
49 | _this.state = { inView: false };
50 | return _this;
51 | }
52 |
53 | _createClass(Entry, [{
54 | key: 'onInView',
55 | value: function onInView(inView) {
56 | this.setState({ inView: inView });
57 | }
58 | }, {
59 | key: 'render',
60 | value: function render() {
61 | var _base;
62 |
63 | var _props = this.props,
64 | children = _props.children,
65 | icon = _props.icon,
66 | props = _objectWithoutProperties(_props, ['children', 'icon']);
67 |
68 | var inView = this.state.inView;
69 | var _props$config = this.props.config,
70 | lineWidth = _props$config.lineWidth,
71 | circleWidth = _props$config.circleWidth,
72 | paddingTop = _props$config.paddingTop,
73 | lineColor = _props$config.lineColor,
74 | activeColor = _props$config.activeColor,
75 | mediaWidthSmall = _props$config.mediaWidthSmall,
76 | twoSidedOverlap = _props$config.twoSidedOverlap,
77 | animations = _props$config.animations;
78 |
79 |
80 | var styles = {
81 | base: (_base = {
82 | listStyleType: 'none',
83 | position: 'relative', // base for map position
84 | width: lineWidth + 'px',
85 | margin: '0 auto -' + twoSidedOverlap + 'px auto',
86 | paddingTop: paddingTop + 'px',
87 | background: lineColor
88 | }, _defineProperty(_base, '@media screen and (max-width: ' + mediaWidthSmall + 'px)', {
89 | margin: '0 auto 0 20px'
90 | }), _defineProperty(_base, '@media print', {
91 | margin: 0,
92 | width: '100%',
93 | paddingTop: 0
94 | }), _base),
95 | circle: {
96 | base: {
97 | position: 'absolute',
98 | bottom: '0',
99 | transform: 'translateX(-50%)',
100 | width: circleWidth + 'px',
101 | height: circleWidth + 'px',
102 | borderRadius: '50%',
103 | background: lineColor,
104 | transition: animations ? 'background .5s ease-in-out' : null,
105 | zIndex: 1
106 | },
107 | inner: {
108 | base: {
109 | display: 'flex',
110 | justifyContent: 'center',
111 | alignItems: 'center',
112 | width: '100%',
113 | height: '100%'
114 | }
115 | },
116 | inView: {
117 | background: activeColor
118 | }
119 | }
120 | };
121 |
122 | return _react2.default.createElement(
123 | 'div',
124 | { style: [styles.base] },
125 | _react2.default.createElement(
126 | 'div',
127 | null,
128 | _react2.default.createElement(
129 | _content2.default,
130 | _extends({}, props, {
131 | inView: inView, onInView: this.onInView
132 | }),
133 | children
134 | )
135 | ),
136 | _react2.default.createElement(
137 | 'span',
138 | { className: 'no-print', style: [styles.circle.base, inView && styles.circle.inView] },
139 | _react2.default.createElement(
140 | 'span',
141 | { style: [styles.circle.inner.base] },
142 | icon
143 | )
144 | )
145 | );
146 | }
147 | }]);
148 |
149 | return Entry;
150 | }(_react2.default.Component);
151 |
152 | Entry.propTypes = {
153 | children: _propTypes2.default.node.isRequired,
154 | even: _propTypes2.default.bool.isRequired,
155 | config: _propTypes2.default.object.isRequired,
156 | icon: _propTypes2.default.node
157 | };
158 |
159 | exports.default = (0, _radium2.default)(Entry);
--------------------------------------------------------------------------------
/dist/timeline.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.Timeline = undefined;
7 |
8 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
9 |
10 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
11 |
12 | var _react = require('react');
13 |
14 | var _react2 = _interopRequireDefault(_react);
15 |
16 | var _propTypes = require('prop-types');
17 |
18 | var _propTypes2 = _interopRequireDefault(_propTypes);
19 |
20 | var _radium = require('radium');
21 |
22 | var _radium2 = _interopRequireDefault(_radium);
23 |
24 | var _entry = require('./entry');
25 |
26 | var _entry2 = _interopRequireDefault(_entry);
27 |
28 | var _config = require('./config');
29 |
30 | var _config2 = _interopRequireDefault(_config);
31 |
32 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
33 |
34 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
35 |
36 | function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
37 |
38 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
39 |
40 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
41 |
42 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
43 |
44 | var Timeline = exports.Timeline = function (_React$Component) {
45 | _inherits(Timeline, _React$Component);
46 |
47 | function Timeline(props) {
48 | _classCallCheck(this, Timeline);
49 |
50 | var _this = _possibleConstructorReturn(this, (Timeline.__proto__ || Object.getPrototypeOf(Timeline)).call(this, props));
51 |
52 | _this.state = { twoSided: true };
53 | _this.onTwoSidedChange = _this.onTwoSidedChange.bind(_this);
54 | _this.componentWillReceiveProps(props);
55 | return _this;
56 | }
57 |
58 | /**
59 | * Merge config with default only once (optimize)
60 | */
61 |
62 |
63 | _createClass(Timeline, [{
64 | key: 'componentWillReceiveProps',
65 | value: function componentWillReceiveProps(newProps) {
66 | //eslint-disable-next-line no-unused-vars
67 | var children = newProps.children,
68 | config = _objectWithoutProperties(newProps, ['children']); // children are not config
69 |
70 |
71 | this.mergedConfig = _extends({}, _config2.default, config);
72 | }
73 | }, {
74 | key: 'componentWillMount',
75 | value: function componentWillMount() {
76 | var mediaWidthSmall = this.mergedConfig.mediaWidthSmall;
77 |
78 | if (window && window.matchMedia) {
79 | this.mqTwoSided = window.matchMedia('(min-width: ' + mediaWidthSmall + 'px)');
80 | this.mqTwoSided.addListener(this.onTwoSidedChange);
81 | this.onTwoSidedChange(this.mqTwoSided);
82 | }
83 | }
84 | }, {
85 | key: 'componentWillUnmount',
86 | value: function componentWillUnmount() {
87 | if (this.mqTwoSided) {
88 | this.mqTwoSided.removeListener(this.onTwoSidedChange);
89 | }
90 | }
91 | }, {
92 | key: 'onTwoSidedChange',
93 | value: function onTwoSidedChange(mq) {
94 | this.setState({ twoSided: mq.matches });
95 | }
96 | }, {
97 | key: 'render',
98 | value: function render() {
99 | var _this2 = this;
100 |
101 | var children = this.props.children;
102 | var _mergedConfig = this.mergedConfig,
103 | color = _mergedConfig.color,
104 | twoSidedOverlap = _mergedConfig.twoSidedOverlap;
105 |
106 | var twoSided = this.state.twoSided;
107 | var i = 0;
108 |
109 | var styles = {
110 | base: _defineProperty({
111 | textAlign: 'center',
112 | paddingBottom: twoSided && twoSidedOverlap + 'px',
113 | color: color,
114 | overflow: 'hidden'
115 | }, this.mqTwoSidedString, {
116 | marginBottom: twoSidedOverlap + 'px'
117 | })
118 | };
119 |
120 | return _react2.default.createElement(
121 | 'div',
122 | { style: [styles.base] },
123 | _react2.default.Children.map(children, function (c) {
124 | return _react2.default.createElement(
125 | _entry2.default,
126 | { even: i++ % 2 === 0 && twoSided, config: _this2.mergedConfig,
127 | icon: c.props.icon },
128 | c
129 | );
130 | })
131 | );
132 | }
133 | }]);
134 |
135 | return Timeline;
136 | }(_react2.default.Component);
137 |
138 | Timeline.propTypes = {
139 | children: _propTypes2.default.node.isRequired,
140 |
141 | // global
142 | paddingTop: _propTypes2.default.number,
143 | mediaWidthMed: _propTypes2.default.number,
144 | mediaWidthSmall: _propTypes2.default.number,
145 | activeColor: _propTypes2.default.string,
146 | color: _propTypes2.default.string,
147 | twoSidedOverlap: _propTypes2.default.number,
148 | animations: _propTypes2.default.bool,
149 | addEvenPropToChildren: _propTypes2.default.bool,
150 |
151 | // line
152 | lineColor: _propTypes2.default.string,
153 | circleWidth: _propTypes2.default.number,
154 | paddingToItem: _propTypes2.default.number,
155 | paddingToItemSmall: _propTypes2.default.number,
156 | lineWidth: _propTypes2.default.number,
157 |
158 | // triangle
159 | triangleWidth: _propTypes2.default.number,
160 | triangleHeight: _propTypes2.default.number,
161 |
162 | // list item content
163 | itemWidth: _propTypes2.default.number,
164 | itemWidthMed: _propTypes2.default.number,
165 | offsetHidden: _propTypes2.default.number,
166 | triangleOffset: _propTypes2.default.number,
167 | smallItemWidthPadding: _propTypes2.default.number,
168 | itemPadding: _propTypes2.default.number,
169 | evenItemOffset: _propTypes2.default.number
170 | };
171 |
172 | exports.default = (0, _radium2.default)(Timeline);
--------------------------------------------------------------------------------
/dist/content.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.Content = undefined;
7 |
8 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
9 |
10 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
11 |
12 | var _react = require('react');
13 |
14 | var _react2 = _interopRequireDefault(_react);
15 |
16 | var _propTypes = require('prop-types');
17 |
18 | var _propTypes2 = _interopRequireDefault(_propTypes);
19 |
20 | var _radium = require('radium');
21 |
22 | var _radium2 = _interopRequireDefault(_radium);
23 |
24 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
25 |
26 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
27 |
28 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
29 |
30 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
31 |
32 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
33 |
34 | var Content = exports.Content = function (_React$Component) {
35 | _inherits(Content, _React$Component);
36 |
37 | function Content(props) {
38 | _classCallCheck(this, Content);
39 |
40 | var _this = _possibleConstructorReturn(this, (Content.__proto__ || Object.getPrototypeOf(Content)).call(this, props));
41 |
42 | _this.onScroll = _this.onScroll.bind(_this);
43 | return _this;
44 | }
45 |
46 | _createClass(Content, [{
47 | key: 'areChildrenInView',
48 | value: function areChildrenInView() {
49 | var rect = this.children.getBoundingClientRect();
50 | var vwHeight = window.innerHeight || document.documentElement.clientHeight;
51 | return rect.bottom >= 0 && rect.bottom <= vwHeight || rect.top >= 0 && rect.top <= vwHeight;
52 | }
53 | }, {
54 | key: 'componentDidMount',
55 | value: function componentDidMount() {
56 | if (this.props.config.animations) {
57 | window.addEventListener('scroll', this.onScroll);
58 | window.addEventListener('resize', this.onScroll);
59 | this.onScroll();
60 | }
61 | }
62 | }, {
63 | key: 'componentWillUnmount',
64 | value: function componentWillUnmount() {
65 | window.removeEventListener('scroll', this.onScroll);
66 | window.removeEventListener('resize', this.onScroll);
67 | }
68 | }, {
69 | key: 'onScroll',
70 | value: function onScroll() {
71 | var inView = this.areChildrenInView();
72 | this.props.onInView(inView);
73 | }
74 | }, {
75 | key: 'render',
76 | value: function render() {
77 | var _base,
78 | _even,
79 | _this2 = this;
80 |
81 | var _props = this.props,
82 | children = _props.children,
83 | even = _props.even,
84 | inView = _props.inView,
85 | config = _props.config;
86 | var circleWidth = config.circleWidth,
87 | mediaWidthMed = config.mediaWidthMed,
88 | paddingToItem = config.paddingToItem,
89 | paddingToItemSmall = config.paddingToItemSmall,
90 | itemWidth = config.itemWidth,
91 | itemWidthMed = config.itemWidthMed,
92 | animations = config.animations,
93 | lineWidth = config.lineWidth,
94 | evenItemOffset = config.evenItemOffset,
95 | offsetHidden = config.offsetHidden,
96 | triangleWidth = config.triangleWidth,
97 | triangleOffset = config.triangleOffset,
98 | triangleHeight = config.triangleHeight,
99 | activeColor = config.activeColor,
100 | mediaWidthSmall = config.mediaWidthSmall,
101 | smallItemWidthPadding = config.smallItemWidthPadding,
102 | itemPadding = config.itemPadding;
103 |
104 |
105 | var offsetEven = circleWidth + paddingToItem + triangleWidth - lineWidth + evenItemOffset;
106 | var offsetEvenNml = offsetEven + itemWidth;
107 | var offsetEvenMed = offsetEven + itemWidthMed;
108 |
109 | var triangleLeft = {
110 | right: '-' + (triangleWidth - 1) + 'px',
111 | borderWidth: triangleHeight + 'px 0 ' + triangleHeight + 'px ' + triangleWidth + 'px',
112 | borderColor: 'transparent transparent transparent ' + activeColor
113 | };
114 | var triangleRight = {
115 | left: '-' + (triangleWidth - 1) + 'px',
116 | borderWidth: triangleHeight + 'px ' + triangleWidth + 'px ' + triangleHeight + 'px 0',
117 | borderColor: 'transparent ' + activeColor + ' transparent transparent'
118 | };
119 |
120 | var mediaMed = '@media screen and (min-width: ' + mediaWidthSmall + 'px)\n and (max-width: ' + mediaWidthMed + 'px)';
121 | var mediaSmall = '@media screen and (max-width: ' + mediaWidthSmall + 'px)';
122 | var mediaPrint = '@media print';
123 |
124 | var styles = {
125 | base: (_base = {
126 | position: 'relative',
127 | bottom: '0',
128 | width: itemWidth + 'px',
129 | padding: itemPadding + 'px',
130 | background: activeColor,
131 | visibility: animations ? 'hidden' : null,
132 | opacity: animations ? 0 : 1,
133 | transition: animations ? 'all .5s ease-in-out' : null
134 | }, _defineProperty(_base, mediaMed, {
135 | width: itemWidthMed + 'px'
136 | }), _defineProperty(_base, mediaSmall, {
137 | width: 'calc(100vw - ' + (triangleWidth + circleWidth + smallItemWidthPadding) + 'px)'
138 | }), _defineProperty(_base, mediaPrint, {
139 | width: '100%',
140 | left: 0,
141 | transform: null
142 | }), _base),
143 | inView: {
144 | transform: 'none',
145 | visibility: 'visible',
146 | opacity: '1'
147 | },
148 | even: (_even = {
149 | left: '-' + offsetEvenNml + 'px',
150 | transform: animations ? 'translate3d(-' + offsetHidden + 'px,0,0)' : null
151 | }, _defineProperty(_even, mediaMed, {
152 | left: '-' + offsetEvenMed + 'px'
153 | }), _defineProperty(_even, mediaSmall, {
154 | left: paddingToItemSmall + triangleWidth + 'px'
155 | }), _even),
156 | odd: _defineProperty({
157 | left: paddingToItem + triangleWidth + 'px',
158 | transform: animations ? 'translate3d(' + offsetHidden + 'px,0,0)' : null
159 | }, mediaSmall, {
160 | left: paddingToItemSmall + triangleWidth + 'px'
161 | }),
162 | triangle: {
163 | base: {
164 | position: 'absolute',
165 | bottom: triangleOffset + 'px',
166 | width: '0',
167 | height: '0',
168 | borderStyle: 'solid'
169 | },
170 | even: _extends({}, triangleLeft, _defineProperty({}, mediaSmall, triangleRight)),
171 | odd: triangleRight
172 | }
173 | };
174 |
175 | var propsToAdd = {};
176 | if (config.addEvenPropToChildren) {
177 | propsToAdd = _extends({}, propsToAdd, {
178 | even: even
179 | });
180 | }
181 |
182 | return _react2.default.createElement(
183 | 'div',
184 | { style: [styles.base, even ? styles.even : styles.odd, inView && styles.inView] },
185 | _react2.default.createElement('span', { style: [styles.triangle.base, even ? styles.triangle.even : styles.triangle.odd, inView && styles.triangle.inView] }),
186 | _react2.default.createElement(
187 | 'div',
188 | { ref: function ref(c) {
189 | return _this2.children = c;
190 | } },
191 | _react2.default.cloneElement(children, propsToAdd)
192 | )
193 | );
194 | }
195 | }]);
196 |
197 | return Content;
198 | }(_react2.default.Component);
199 |
200 | Content.propTypes = {
201 | children: _propTypes2.default.node.isRequired,
202 | even: _propTypes2.default.bool.isRequired,
203 | inView: _propTypes2.default.bool.isRequired,
204 | onInView: _propTypes2.default.func,
205 | config: _propTypes2.default.object.isRequired
206 | };
207 |
208 | exports.default = (0, _radium2.default)(Content);
--------------------------------------------------------------------------------