├── .npmignore ├── examples └── Example │ ├── .watchmanconfig │ ├── img │ ├── lunch.png │ ├── archery.png │ ├── soccer.png │ ├── badminton.png │ └── dumbbell.png │ ├── assets │ ├── icon.png │ └── splash.png │ ├── babel.config.js │ ├── .gitignore │ ├── .expo-shared │ └── assets.json │ ├── package.json │ ├── app.json │ ├── pages │ ├── basicExample.js │ ├── template.js │ ├── customExample.js │ ├── dotExample.js │ ├── iconExample.js │ ├── timelinePressExample.js │ ├── menu.js │ ├── twoColumnExample.js │ ├── refreshLoadMoreExample.js │ ├── overrideRenderExample.js │ ├── singleRightExample.js │ └── react-native-timeline-flatlist.js │ └── app.js ├── .gitignore ├── package.json ├── LICENSE ├── lib ├── index.d.ts └── index.js ├── CODE_OF_CONDUCT.md └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | examples 2 | -------------------------------------------------------------------------------- /examples/Example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /examples/Example/img/lunch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eugnis/react-native-timeline-flatlist/HEAD/examples/Example/img/lunch.png -------------------------------------------------------------------------------- /examples/Example/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eugnis/react-native-timeline-flatlist/HEAD/examples/Example/assets/icon.png -------------------------------------------------------------------------------- /examples/Example/img/archery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eugnis/react-native-timeline-flatlist/HEAD/examples/Example/img/archery.png -------------------------------------------------------------------------------- /examples/Example/img/soccer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eugnis/react-native-timeline-flatlist/HEAD/examples/Example/img/soccer.png -------------------------------------------------------------------------------- /examples/Example/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eugnis/react-native-timeline-flatlist/HEAD/examples/Example/assets/splash.png -------------------------------------------------------------------------------- /examples/Example/img/badminton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eugnis/react-native-timeline-flatlist/HEAD/examples/Example/img/badminton.png -------------------------------------------------------------------------------- /examples/Example/img/dumbbell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eugnis/react-native-timeline-flatlist/HEAD/examples/Example/img/dumbbell.png -------------------------------------------------------------------------------- /examples/Example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /examples/Example/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | npm-debug.* 4 | *.jks 5 | *.p8 6 | *.p12 7 | *.key 8 | *.mobileprovision 9 | *.orig.* 10 | web-build/ 11 | web-report/ 12 | -------------------------------------------------------------------------------- /examples/Example/.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "f9155ac790fd02fadcdeca367b02581c04a353aa6d5aa84409a59f6804c87acd": true, 3 | "89ed26367cdb9b771858e026f2eb95bfdb90e5ae943e716575327ec325f39c44": true, 4 | "f60988cd416e0156baeb414d2a4ebb046d9e7be5606c79ffccf57ba6071f117d": true, 5 | "60fc3ce7945cf71b15eddacf0f64c52c7ccc6eedc36bd4d5a0d8dd6a013bdc25": true, 6 | "eeee5b232ec2240a2c79f499476256a90965f8f3ee7a85955d3928b9bd063595": true, 7 | "46aeaf9b556733ef9f5ec9dbf38b2b462118a14632abf1096eeff36d9b68b739": true, 8 | "23700d9de35758dbf27f05913ba90d710194f7710278d3a93355cd050c69252d": true 9 | } 10 | -------------------------------------------------------------------------------- /examples/Example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "scripts": { 4 | "start": "expo start", 5 | "android": "expo start --android", 6 | "ios": "expo start --ios", 7 | "web": "expo start --web", 8 | "eject": "expo eject" 9 | }, 10 | "dependencies": { 11 | "expo": "^36.0.0", 12 | "react": "16.9.0", 13 | "react-dom": "16.9.0", 14 | "react-native": "https://github.com/expo/react-native/archive/sdk-36.0.1.tar.gz", 15 | "react-native-deprecated-custom-components": "^0.1.2", 16 | "react-native-timeline-flatlist": "^0.7.1", 17 | "react-native-web": "^0.11.7" 18 | }, 19 | "devDependencies": { 20 | "babel-preset-expo": "^8.0.0" 21 | }, 22 | "private": true 23 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | .DS_Store 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # nyc test coverage 19 | .nyc_output 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # node-waf configuration 25 | .lock-wscript 26 | 27 | # Compiled binary addons (http://nodejs.org/api/addons.html) 28 | build/Release 29 | 30 | # Dependency directories 31 | node_modules 32 | jspm_packages 33 | 34 | # Optional npm cache directory 35 | .npm 36 | 37 | # Optional REPL history 38 | .node_repl_history 39 | -------------------------------------------------------------------------------- /examples/Example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "Examples Timeline Flatlist", 4 | "slug": "react-native-timeline-flatlist-examples", 5 | "privacy": "public", 6 | "platforms": [ 7 | "ios", 8 | "android", 9 | "web" 10 | ], 11 | "version": "1.0.0", 12 | "orientation": "portrait", 13 | "icon": "./assets/icon.png", 14 | "splash": { 15 | "image": "./assets/splash.png", 16 | "resizeMode": "contain", 17 | "backgroundColor": "#ffffff" 18 | }, 19 | "updates": { 20 | "fallbackToCacheTimeout": 0 21 | }, 22 | "assetBundlePatterns": [ 23 | "**/*" 24 | ], 25 | "ios": { 26 | "supportsTablet": true 27 | }, 28 | "description": "Examples for React Native Timeline Flatlist", 29 | "githubUrl": "https://github.com/Eugnis/react-native-timeline-flatlist" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-timeline-flatlist", 3 | "version": "0.8.0", 4 | "description": "Timeline component for React Native", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git@github.com:eugnis/react-native-timeline-flatlist.git" 12 | }, 13 | "keywords": [ 14 | "react-native", 15 | "timeline", 16 | "flatlist", 17 | "schedule", 18 | "react native component", 19 | "ios", 20 | "android", 21 | "component" 22 | ], 23 | "peerDependencies": { 24 | "react-native": ">= 0.58.2" 25 | }, 26 | "author": "Eugnis (https://github.com/eugnis)", 27 | "contributors": [ 28 | "Watcharachai Kanjaikaew (https://github.com/thegamenicorus)", 29 | "Eugnis (https://github.com/eugnis)" 30 | ], 31 | "license": "MIT", 32 | "bugs": { 33 | "url": "https://github.com/eugnis/react-native-timeline-flatlist/issues" 34 | }, 35 | "homepage": "https://github.com/eugnis/react-native-timeline-flatlist" 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Watcharachai Kanjaikaew 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/Example/pages/basicExample.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * @flow 5 | */ 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | Text, 11 | View 12 | } from 'react-native'; 13 | import Timeline from 'react-native-timeline-flatlist' 14 | 15 | export default class Example extends Component { 16 | constructor(){ 17 | super() 18 | this.data = [ 19 | {time: '09:00', title: 'Event 1', description: 'Event 1 Description'}, 20 | {time: '10:45', title: 'Event 2', description: 'Event 2 Description'}, 21 | {time: '12:00', title: 'Event 3', description: 'Event 3 Description'}, 22 | {time: '14:00', title: 'Event 4', description: 'Event 4 Description'}, 23 | {time: '16:30', title: 'Event 5', description: 'Event 5 Description'} 24 | ] 25 | } 26 | 27 | render() { 28 | //'rgb(45,156,219)' 29 | return ( 30 | 31 | 35 | 36 | ); 37 | } 38 | } 39 | 40 | const styles = StyleSheet.create({ 41 | container: { 42 | flex: 1, 43 | padding: 20, 44 | paddingTop:65, 45 | backgroundColor:'white' 46 | }, 47 | list: { 48 | flex: 1, 49 | marginTop:20, 50 | }, 51 | }); 52 | -------------------------------------------------------------------------------- /lib/index.d.ts: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { StyleProp, ImageStyle, TextStyle, ViewStyle, FlatListProps } from "react-native"; 3 | 4 | declare module "react-native-timeline-flatlist" { 5 | type Data = { 6 | time?: string; 7 | title?: string; 8 | description?: any; 9 | lineWidth?: number; 10 | lineColor?: string; 11 | eventContainerStyle?: StyleProp; 12 | circleSize?: number; 13 | circleColor?: string; 14 | dotColor?: string; 15 | icon?: string | React.ReactNode; 16 | position?: 'left' | 'right'; 17 | }; 18 | 19 | interface TimelineProps { 20 | data: Data[] | any; 21 | innerCircle?: 'none' | 'icon' | 'dot' | 'element'; 22 | separator?: boolean; 23 | columnFormat?: 'single-column-left' | 'single-column-right' | 'two-column'; 24 | lineWidth?: number; 25 | lineColor?: string; 26 | circleSize?: number; 27 | circleColor?: string; 28 | dotColor?: string; 29 | dotSize?: number; 30 | iconDefault?: string | React.ReactNode; 31 | style?: StyleProp; 32 | circleStyle?: StyleProp 33 | listViewStyle?: StyleProp; 34 | listViewContainerStyle?: StyleProp; 35 | timeStyle?: StyleProp; 36 | titleStyle?: StyleProp; 37 | descriptionStyle?: StyleProp; 38 | iconStyle?: StyleProp; 39 | separatorStyle?: StyleProp; 40 | rowContainerStyle?: StyleProp; 41 | eventContainerStyle?: StyleProp; 42 | eventDetailStyle?: StyleProp; 43 | timeContainerStyle?: StyleProp; 44 | detailContainerStyle?: StyleProp; 45 | onEventPress?: (event: Event) => any; 46 | renderTime?: (rowData: Data | any, rowID: number) => any; 47 | renderDetail?: (rowData: Data | any, rowID: number) => any; 48 | renderCircle?: (rowData: Data | any, rowID: number) => any; 49 | renderFullLine?: boolean; 50 | options?: Partial>; 51 | showTime?: boolean; 52 | isUsingFlatlist?: boolean; 53 | } 54 | 55 | export default class Timeline extends React.Component {} 56 | } 57 | -------------------------------------------------------------------------------- /examples/Example/pages/template.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * @flow 5 | */ 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | Text, 11 | View 12 | } from 'react-native'; 13 | import Timeline from 'react-native-timeline-flatlist' 14 | 15 | export default class Example extends Component { 16 | constructor(){ 17 | super() 18 | this.data = [ 19 | {time: '09:00', title: 'Archery Training', description: 'Event 1 Description', icon: require('../img/archery.png')}, 20 | {time: '10:45', title: 'Event 2', description: 'Event 2 Description', icon: require('../img/badminton.png')}, 21 | {time: '12:00', title: 'Event 3', description: 'Event 3 Description', icon: require('../img/lunch.png')}, 22 | {time: '14:00', title: 'Event 4', description: 'Event 4 Description', icon: require('../img/soccer.png')}, 23 | {time: '16:30', title: 'Event 5', description: 'Event 5 Description', icon: require('../img/dumbbell.png')} 24 | ] 25 | } 26 | 27 | render() { 28 | //'rgb(45,156,219)' 29 | return ( 30 | 31 | 52 | 53 | ); 54 | } 55 | } 56 | 57 | const styles = StyleSheet.create({ 58 | container: { 59 | flex: 1, 60 | padding: 20, 61 | paddingTop:65 62 | }, 63 | list: { 64 | flex: 1, 65 | marginTop:20, 66 | }, 67 | }); 68 | -------------------------------------------------------------------------------- /examples/Example/pages/customExample.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * @flow 5 | */ 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | Text, 11 | View 12 | } from 'react-native'; 13 | import Timeline from 'react-native-timeline-flatlist' 14 | 15 | export default class Example extends Component { 16 | constructor(){ 17 | super() 18 | this.data = [ 19 | {time: '09:00', title: 'Archery Training', description: 'The Beginner Archery and Beginner Crossbow course does not require you to bring any equipment, since everything you need will be provided for the course. ', circleColor: '#009688',lineColor:'#009688'}, 20 | {time: '10:45', title: 'Play Badminton', description: 'Badminton is a racquet sport played using racquets to hit a shuttlecock across a net.'}, 21 | {time: '12:00', title: 'Lunch'}, 22 | {time: '14:00', title: 'Watch Soccer', description: 'Team sport played between two teams of eleven players with a spherical ball. ',lineColor:'#009688'}, 23 | {time: '16:30', title: 'Go to Fitness center', description: 'Look out for the Best Gym & Fitness Centers around me :)', circleColor: '#009688'} 24 | ] 25 | } 26 | 27 | render() { 28 | //'rgb(45,156,219)' 29 | return ( 30 | 31 | 45 | 46 | ); 47 | } 48 | } 49 | 50 | const styles = StyleSheet.create({ 51 | container: { 52 | flex: 1, 53 | padding: 20, 54 | paddingTop:65, 55 | backgroundColor:'white' 56 | }, 57 | list: { 58 | flex: 1, 59 | marginTop:20, 60 | }, 61 | }); 62 | -------------------------------------------------------------------------------- /examples/Example/pages/dotExample.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * @flow 5 | */ 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | Text, 11 | View 12 | } from 'react-native'; 13 | import Timeline from 'react-native-timeline-flatlist' 14 | 15 | export default class Example extends Component { 16 | constructor(){ 17 | super() 18 | this.data = [ 19 | {time: '09:00', title: 'Archery Training', description: 'The Beginner Archery and Beginner Crossbow course does not require you to bring any equipment, since everything you need will be provided for the course. ', circleColor: '#009688',lineColor:'#009688'}, 20 | {time: '10:45', title: 'Play Badminton', description: 'Badminton is a racquet sport played using racquets to hit a shuttlecock across a net.'}, 21 | {time: '12:00', title: 'Lunch', icon: require('../img/lunch.png')}, 22 | {time: '14:00', title: 'Watch Soccer', description: 'Team sport played between two teams of eleven players with a spherical ball. ',lineColor:'#009688'}, 23 | {time: '16:30', title: 'Go to Fitness center', description: 'Look out for the Best Gym & Fitness Centers around me :)', circleColor: '#009688'} 24 | ] 25 | } 26 | 27 | render() { 28 | //'rgb(45,156,219)' 29 | return ( 30 | 31 | 46 | 47 | ); 48 | } 49 | } 50 | 51 | const styles = StyleSheet.create({ 52 | container: { 53 | flex: 1, 54 | padding: 20, 55 | paddingTop:65, 56 | backgroundColor:'white' 57 | }, 58 | list: { 59 | flex: 1, 60 | marginTop:20, 61 | }, 62 | }); 63 | -------------------------------------------------------------------------------- /examples/Example/pages/iconExample.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * @flow 5 | */ 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | Text, 11 | View, 12 | Image 13 | } from 'react-native'; 14 | import Timeline from 'react-native-timeline-flatlist' 15 | 16 | export default class Example extends Component { 17 | constructor(){ 18 | super() 19 | this.data = [ 20 | {time: '09:00', title: 'Archery Training', description: 'The Beginner Archery and Beginner Crossbow course does not require you to bring any equipment, since everything you need will be provided for the course. ',lineColor:'#009688', icon: require('../img/archery.png')}, 21 | {time: '10:45', title: 'Play Badminton', description: 'Badminton is a racquet sport played using racquets to hit a shuttlecock across a net.', icon: require('../img/badminton.png')}, 22 | {time: '12:00', title: 'Custom rendered icon', icon: }, 26 | {time: '14:00', title: 'Watch Soccer', description: 'Team sport played between two teams of eleven players with a spherical ball. ',lineColor:'#009688', icon: require('../img/soccer.png')}, 27 | {time: '16:30', title: 'Go to Fitness center', description: 'Look out for the Best Gym & Fitness Centers around me :)', icon: require('../img/dumbbell.png')} 28 | ] 29 | } 30 | 31 | render() { 32 | //'rgb(45,156,219)' 33 | return ( 34 | 35 | 49 | 50 | ); 51 | } 52 | } 53 | 54 | const styles = StyleSheet.create({ 55 | container: { 56 | flex: 1, 57 | padding: 20, 58 | paddingTop:65, 59 | backgroundColor:'white' 60 | }, 61 | list: { 62 | flex: 1, 63 | marginTop:20, 64 | }, 65 | }); 66 | -------------------------------------------------------------------------------- /examples/Example/pages/timelinePressExample.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * @flow 5 | */ 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | Text, 11 | View 12 | } from 'react-native'; 13 | import Timeline from 'react-native-timeline-flatlist' 14 | 15 | export default class Example extends Component { 16 | constructor(){ 17 | super() 18 | this.onEventPress = this.onEventPress.bind(this) 19 | this.renderSelected = this.renderSelected.bind(this) 20 | this.data = [ 21 | {time: '09:00', title: 'Archery Training', description: 'The Beginner Archery and Beginner Crossbow course does not require you to bring any equipment, since everything you need will be provided for the course. ',lineColor:'#009688', icon: require('../img/archery.png')}, 22 | {time: '10:45', title: 'Play Badminton', description: 'Badminton is a racquet sport played using racquets to hit a shuttlecock across a net.', icon: require('../img/badminton.png')}, 23 | {time: '12:00', title: 'Lunch', icon: require('../img/lunch.png')}, 24 | {time: '14:00', title: 'Watch Soccer', description: 'Team sport played between two teams of eleven players with a spherical ball. ',lineColor:'#009688', icon: require('../img/soccer.png')}, 25 | {time: '16:30', title: 'Go to Fitness center', description: 'Look out for the Best Gym & Fitness Centers around me :)', icon: require('../img/dumbbell.png')} 26 | ] 27 | this.state = {selected: null} 28 | } 29 | 30 | onEventPress(data){ 31 | this.setState({selected: data}) 32 | } 33 | 34 | renderSelected(){ 35 | if(this.state.selected) 36 | return Selected event: {this.state.selected.title} at {this.state.selected.time} 37 | } 38 | 39 | render() { 40 | return ( 41 | 42 | {this.renderSelected()} 43 | 58 | 59 | ); 60 | } 61 | } 62 | 63 | const styles = StyleSheet.create({ 64 | container: { 65 | flex: 1, 66 | padding: 20, 67 | paddingTop:65, 68 | backgroundColor:'white' 69 | }, 70 | list: { 71 | flex: 1, 72 | marginTop:20, 73 | }, 74 | }); 75 | -------------------------------------------------------------------------------- /examples/Example/pages/menu.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * @flow 5 | */ 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | Text, 11 | View, 12 | Navigator, 13 | TouchableOpacity, 14 | StatusBar 15 | } from 'react-native'; 16 | 17 | export default class Example extends Component { 18 | constructor(){ 19 | super() 20 | 21 | this.goto = this.goto.bind(this) 22 | } 23 | 24 | goto(page){ 25 | this.props.navigator.push(page) 26 | } 27 | 28 | render() { 29 | return ( 30 | 31 | 32 | this.goto({name:'basic', title:'Basic Example'})}> 33 | Basic Example 34 | 35 | this.goto({name:'custom', title:'Custom Example'})}> 36 | Custom Example 37 | 38 | this.goto({name:'dot', title:'Circle Dot Example'})}> 39 | Circle Dot Example 40 | 41 | this.goto({name:'icon', title:'Icon Example'})}> 42 | Icon Example 43 | 44 | this.goto({name:'press', title:'Press Example'})}> 45 | Press Example 46 | 47 | this.goto({name:'override', title:'Override Render Example'})}> 48 | Override Render Example 49 | 50 | this.goto({name:'single-right', title:'Single Right Example'})}> 51 | Single Column Right Example 52 | 53 | this.goto({name:'two-column', title:'Two Column Example'})}> 54 | Two Column Example 55 | 56 | this.goto({name:'refresh-loadmore', title:'Refresh Load More'})}> 57 | Refresh and Loadmore 58 | 59 | 60 | ) 61 | } 62 | } 63 | 64 | const styles = StyleSheet.create({ 65 | container: { 66 | flex: 1, 67 | justifyContent: 'center', 68 | alignItems: 'center', 69 | backgroundColor:'white' 70 | }, 71 | button:{ 72 | padding:10, 73 | }, 74 | menu: { 75 | fontSize: 20 76 | } 77 | }); 78 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at eugenijb@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /examples/Example/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | * @flow 7 | */ 8 | 9 | import React, {Component} from 'react'; 10 | import { 11 | StyleSheet, 12 | Text, 13 | View, 14 | TouchableOpacity 15 | } from 'react-native'; 16 | import {Navigator} from 'react-native-deprecated-custom-components'; 17 | 18 | 19 | import Menu from './pages/menu' 20 | import BasicExample from './pages/basicExample' 21 | import CustomExample from './pages/customExample' 22 | import DotExample from './pages/dotExample' 23 | import IconExample from './pages/iconExample' 24 | import PressExample from './pages/timelinePressExample' 25 | import OverrideExample from './pages/overrideRenderExample' 26 | import SingleRightExample from './pages/singleRightExample' 27 | import TwoColumnExample from './pages/twoColumnExample' 28 | import RefreshLoadMore from './pages/refreshLoadMoreExample' 29 | 30 | export default class App extends Component { 31 | constructor(){ 32 | super() 33 | this.renderScene = this.renderScene.bind(this) 34 | } 35 | 36 | renderScene(route, nav){ 37 | switch (route.name) { 38 | case 'menu': 39 | return 40 | case 'basic': 41 | return 42 | case 'custom': 43 | return 44 | case 'dot': 45 | return 46 | case 'icon': 47 | return 48 | case 'press': 49 | return 50 | case 'override': 51 | return 52 | case 'single-right': 53 | return 54 | case 'two-column': 55 | return 56 | case 'refresh-loadmore': 57 | return 58 | } 59 | } 60 | 61 | render() { 62 | var NavigationBarRouteMapper = 65 | { 66 | if (route.index === 0) { 67 | return null; 68 | } else { 69 | return ( 70 | navigator.pop()}> 71 | Back 72 | 73 | ); 74 | } 75 | }, 76 | RightButton: (route, navigator, index, navState) => 77 | { return null }, 78 | Title: (route, navigator, index, navState) => 79 | { return ({route.title}); }, 80 | }} 81 | style={{backgroundColor: '#009688'}} 82 | /> 83 | return ( 84 | 89 | ) 90 | } 91 | } 92 | 93 | const styles = StyleSheet.create({ 94 | container: { 95 | flex: 1, 96 | justifyContent: 'center', 97 | alignItems: 'center', 98 | backgroundColor: '#F5FCFF', 99 | }, 100 | title: { 101 | fontSize: 20, 102 | textAlign: 'center', 103 | color: 'white' 104 | }, 105 | back: { 106 | paddingLeft:10, 107 | fontSize: 20, 108 | color: 'white', 109 | textAlign: 'center' 110 | }, 111 | instructions: { 112 | textAlign: 'center', 113 | color: '#333333', 114 | marginBottom: 5, 115 | }, 116 | }); 117 | -------------------------------------------------------------------------------- /examples/Example/pages/twoColumnExample.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * @flow 5 | */ 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | Text, 11 | View, 12 | Image 13 | } from 'react-native'; 14 | import Timeline from 'react-native-timeline-flatlist' 15 | 16 | export default class Example extends Component { 17 | constructor(){ 18 | super() 19 | this.onEventPress = this.onEventPress.bind(this) 20 | this.renderSelected = this.renderSelected.bind(this) 21 | 22 | this.data = [ 23 | { 24 | time: '09:00', 25 | title: 'Archery Training', 26 | description: 'The Beginner Archery and Beginner Crossbow course does not require you to bring any equipment, since everything you need will be provided for the course. ', 27 | lineColor:'#009688', 28 | icon: require('../img/archery.png'), 29 | imageUrl: 'https://cloud.githubusercontent.com/assets/21040043/24240340/c0f96b3a-0fe3-11e7-8964-fe66e4d9be7a.jpg' 30 | }, 31 | { 32 | time: '10:45', 33 | title: 'Play Badminton', 34 | description: 'Badminton is a racquet sport played using racquets to hit a shuttlecock across a net.', 35 | icon: require('../img/badminton.png'), 36 | imageUrl: 'https://cloud.githubusercontent.com/assets/21040043/24240405/0ba41234-0fe4-11e7-919b-c3f88ced349c.jpg' 37 | }, 38 | { 39 | time: '12:00', 40 | title: 'Lunch', 41 | icon: require('../img/lunch.png'), 42 | }, 43 | { 44 | time: '14:00', 45 | title: 'Watch Soccer', 46 | description: 'Team sport played between two teams of eleven players with a spherical ball. ', 47 | lineColor:'#009688', 48 | icon: require('../img/soccer.png'), 49 | imageUrl: 'https://cloud.githubusercontent.com/assets/21040043/24240419/1f553dee-0fe4-11e7-8638-6025682232b1.jpg' 50 | }, 51 | { 52 | time: '16:30', 53 | title: 'Go to Fitness center', 54 | description: 'Look out for the Best Gym & Fitness Centers around me :)', 55 | icon: require('../img/dumbbell.png'), 56 | imageUrl: 'https://cloud.githubusercontent.com/assets/21040043/24240422/20d84f6c-0fe4-11e7-8f1d-9dbc594d0cfa.jpg' 57 | } 58 | ] 59 | this.state = {selected: null} 60 | } 61 | 62 | onEventPress(data){ 63 | this.setState({selected: data}) 64 | } 65 | 66 | renderSelected(){ 67 | if(this.state.selected) 68 | return Selected event: {this.state.selected.title} at {this.state.selected.time} 69 | } 70 | 71 | render() { 72 | return ( 73 | 74 | {this.renderSelected()} 75 | 93 | 94 | ); 95 | } 96 | } 97 | 98 | const styles = StyleSheet.create({ 99 | container: { 100 | flex: 1, 101 | padding: 20, 102 | paddingTop:65, 103 | backgroundColor:'white' 104 | }, 105 | list: { 106 | flex: 1, 107 | marginTop:20, 108 | }, 109 | title:{ 110 | fontSize:16, 111 | fontWeight: 'bold' 112 | }, 113 | descriptionContainer:{ 114 | flexDirection: 'row', 115 | paddingRight: 50 116 | }, 117 | image:{ 118 | width: 50, 119 | height: 50, 120 | borderRadius: 25 121 | }, 122 | textDescription: { 123 | marginLeft: 10, 124 | color: 'gray' 125 | } 126 | }); -------------------------------------------------------------------------------- /examples/Example/pages/refreshLoadMoreExample.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * @flow 5 | */ 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | Text, 11 | View, 12 | RefreshControl, 13 | ActivityIndicator 14 | } from 'react-native'; 15 | import Timeline from 'react-native-timeline-flatlist' 16 | 17 | export default class Example extends Component { 18 | constructor(){ 19 | super() 20 | this.onEndReached = this.onEndReached.bind(this) 21 | this.renderFooter = this.renderFooter.bind(this) 22 | this.onRefresh = this.onRefresh.bind(this) 23 | 24 | this.data = [ 25 | {time: '09:00', title: 'Archery Training', description: 'The Beginner Archery and Beginner Crossbow course does not require you to bring any equipment, since everything you need will be provided for the course. '}, 26 | {time: '10:45', title: 'Play Badminton', description: 'Badminton is a racquet sport played using racquets to hit a shuttlecock across a net.'}, 27 | {time: '12:00', title: 'Lunch', icon: require('../img/lunch.png')}, 28 | {time: '14:00', title: 'Watch Soccer', description: 'Team sport played between two teams of eleven players with a spherical ball. '}, 29 | {time: '16:30', title: 'Go to Fitness center', description: 'Look out for the Best Gym & Fitness Centers around me :)'}, 30 | ] 31 | 32 | this.state = { 33 | isRefreshing: false, 34 | waiting: false, 35 | data: this.data 36 | } 37 | } 38 | 39 | onRefresh(){ 40 | this.setState({isRefreshing: true}); 41 | //refresh to initial data 42 | setTimeout(() => { 43 | //refresh to initial data 44 | this.setState({ 45 | data: this.data, 46 | isRefreshing: false 47 | }); 48 | }, 2000); 49 | } 50 | 51 | onEndReached() { 52 | if (!this.state.waiting) { 53 | this.setState({waiting: true}); 54 | 55 | //fetch and concat data 56 | setTimeout(() => { 57 | 58 | //refresh to initial data 59 | var data = this.state.data.concat( 60 | [ 61 | {time: '18:00', title: 'Load more data', description: 'append event at bottom of timeline'}, 62 | {time: '18:00', title: 'Load more data', description: 'append event at bottom of timeline'}, 63 | {time: '18:00', title: 'Load more data', description: 'append event at bottom of timeline'}, 64 | {time: '18:00', title: 'Load more data', description: 'append event at bottom of timeline'}, 65 | {time: '18:00', title: 'Load more data', description: 'append event at bottom of timeline'} 66 | ] 67 | ) 68 | 69 | this.setState({ 70 | waiting: false, 71 | data: data, 72 | }); 73 | }, 2000); 74 | } 75 | } 76 | 77 | renderFooter() { 78 | if (this.state.waiting) { 79 | return ; 80 | } else { 81 | return ~; 82 | } 83 | } 84 | 85 | render() { 86 | //'rgb(45,156,219)' 87 | return ( 88 | 89 | 105 | ), 106 | renderFooter: this.renderFooter, 107 | onEndReached: this.onEndReached 108 | }} 109 | innerCircle={'dot'} 110 | /> 111 | 112 | ); 113 | } 114 | } 115 | 116 | const styles = StyleSheet.create({ 117 | container: { 118 | flex: 1, 119 | padding: 20, 120 | paddingTop:65, 121 | backgroundColor:'white' 122 | }, 123 | list: { 124 | flex: 1, 125 | marginTop:20, 126 | }, 127 | }); -------------------------------------------------------------------------------- /examples/Example/pages/overrideRenderExample.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * @flow 5 | */ 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | Text, 11 | View, 12 | Image 13 | } from 'react-native'; 14 | import Timeline from 'react-native-timeline-flatlist' 15 | 16 | export default class Example extends Component { 17 | constructor(){ 18 | super() 19 | this.onEventPress = this.onEventPress.bind(this) 20 | this.renderSelected = this.renderSelected.bind(this) 21 | this.renderDetail = this.renderDetail.bind(this) 22 | 23 | this.data = [ 24 | { 25 | time: '09:00', 26 | title: 'Archery Training', 27 | description: 'The Beginner Archery and Beginner Crossbow course does not require you to bring any equipment, since everything you need will be provided for the course. ', 28 | lineColor:'#009688', 29 | icon: require('../img/archery.png'), 30 | imageUrl: 'https://cloud.githubusercontent.com/assets/21040043/24240340/c0f96b3a-0fe3-11e7-8964-fe66e4d9be7a.jpg' 31 | }, 32 | { 33 | time: '10:45', 34 | title: 'Play Badminton', 35 | description: 'Badminton is a racquet sport played using racquets to hit a shuttlecock across a net.', 36 | icon: require('../img/badminton.png'), 37 | imageUrl: 'https://cloud.githubusercontent.com/assets/21040043/24240405/0ba41234-0fe4-11e7-919b-c3f88ced349c.jpg' 38 | }, 39 | { 40 | time: '12:00', 41 | title: 'Lunch', 42 | icon: require('../img/lunch.png'), 43 | }, 44 | { 45 | time: '14:00', 46 | title: 'Watch Soccer', 47 | description: 'Team sport played between two teams of eleven players with a spherical ball. ', 48 | lineColor:'#009688', 49 | icon: require('../img/soccer.png'), 50 | imageUrl: 'https://cloud.githubusercontent.com/assets/21040043/24240419/1f553dee-0fe4-11e7-8638-6025682232b1.jpg' 51 | }, 52 | { 53 | time: '16:30', 54 | title: 'Go to Fitness center', 55 | description: 'Look out for the Best Gym & Fitness Centers around me :)', 56 | icon: require('../img/dumbbell.png'), 57 | imageUrl: 'https://cloud.githubusercontent.com/assets/21040043/24240422/20d84f6c-0fe4-11e7-8f1d-9dbc594d0cfa.jpg' 58 | } 59 | ] 60 | this.state = {selected: null} 61 | } 62 | 63 | onEventPress(data){ 64 | this.setState({selected: data}) 65 | } 66 | 67 | renderSelected(){ 68 | if(this.state.selected) 69 | return Selected event: {this.state.selected.title} at {this.state.selected.time} 70 | } 71 | 72 | renderDetail(rowData, sectionID, rowID) { 73 | let title = {rowData.title} 74 | var desc = null 75 | if(rowData.description && rowData.imageUrl) 76 | desc = ( 77 | 78 | 79 | {rowData.description} 80 | 81 | ) 82 | 83 | return ( 84 | 85 | {title} 86 | {desc} 87 | 88 | ) 89 | } 90 | 91 | render() { 92 | return ( 93 | 94 | {this.renderSelected()} 95 | 111 | 112 | ); 113 | } 114 | } 115 | 116 | const styles = StyleSheet.create({ 117 | container: { 118 | flex: 1, 119 | padding: 20, 120 | paddingTop:65, 121 | backgroundColor:'white' 122 | }, 123 | list: { 124 | flex: 1, 125 | marginTop:20, 126 | }, 127 | title:{ 128 | fontSize:16, 129 | fontWeight: 'bold' 130 | }, 131 | descriptionContainer:{ 132 | flexDirection: 'row', 133 | paddingRight: 50 134 | }, 135 | image:{ 136 | width: 50, 137 | height: 50, 138 | borderRadius: 25 139 | }, 140 | textDescription: { 141 | marginLeft: 10, 142 | color: 'gray' 143 | } 144 | }); -------------------------------------------------------------------------------- /examples/Example/pages/singleRightExample.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * @flow 5 | */ 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | StyleSheet, 10 | Text, 11 | View, 12 | Image 13 | } from 'react-native'; 14 | import Timeline from 'react-native-timeline-flatlist' 15 | 16 | export default class Example extends Component { 17 | constructor(){ 18 | super() 19 | this.onEventPress = this.onEventPress.bind(this) 20 | this.renderSelected = this.renderSelected.bind(this) 21 | this.renderDetail = this.renderDetail.bind(this) 22 | 23 | this.data = [ 24 | { 25 | time: '09:00', 26 | title: 'Archery Training', 27 | description: 'The Beginner Archery and Beginner Crossbow course does not require you to bring any equipment, since everything you need will be provided for the course. ', 28 | lineColor:'#009688', 29 | icon: require('../img/archery.png'), 30 | imageUrl: 'https://cloud.githubusercontent.com/assets/21040043/24240340/c0f96b3a-0fe3-11e7-8964-fe66e4d9be7a.jpg' 31 | }, 32 | { 33 | time: '10:45', 34 | title: 'Play Badminton', 35 | description: 'Badminton is a racquet sport played using racquets to hit a shuttlecock across a net.', 36 | icon: require('../img/badminton.png'), 37 | imageUrl: 'https://cloud.githubusercontent.com/assets/21040043/24240405/0ba41234-0fe4-11e7-919b-c3f88ced349c.jpg' 38 | }, 39 | { 40 | time: '12:00', 41 | title: 'Lunch', 42 | icon: require('../img/lunch.png'), 43 | }, 44 | { 45 | time: '14:00', 46 | title: 'Watch Soccer', 47 | description: 'Team sport played between two teams of eleven players with a spherical ball. ', 48 | lineColor:'#009688', 49 | icon: require('../img/soccer.png'), 50 | imageUrl: 'https://cloud.githubusercontent.com/assets/21040043/24240419/1f553dee-0fe4-11e7-8638-6025682232b1.jpg' 51 | }, 52 | { 53 | time: '16:30', 54 | title: 'Go to Fitness center', 55 | description: 'Look out for the Best Gym & Fitness Centers around me :)', 56 | icon: require('../img/dumbbell.png'), 57 | imageUrl: 'https://cloud.githubusercontent.com/assets/21040043/24240422/20d84f6c-0fe4-11e7-8f1d-9dbc594d0cfa.jpg' 58 | } 59 | ] 60 | this.state = {selected: null} 61 | } 62 | 63 | onEventPress(data){ 64 | this.setState({selected: data}) 65 | } 66 | 67 | renderSelected(){ 68 | if(this.state.selected) 69 | return Selected event: {this.state.selected.title} at {this.state.selected.time} 70 | } 71 | 72 | renderDetail(rowData, sectionID, rowID) { 73 | let title = {rowData.title} 74 | var desc = null 75 | if(rowData.description && rowData.imageUrl) 76 | desc = ( 77 | 78 | 79 | {rowData.description} 80 | 81 | ) 82 | 83 | return ( 84 | 85 | {title} 86 | {desc} 87 | 88 | ) 89 | } 90 | 91 | render() { 92 | return ( 93 | 94 | {this.renderSelected()} 95 | 114 | 115 | ); 116 | } 117 | } 118 | 119 | const styles = StyleSheet.create({ 120 | container: { 121 | flex: 1, 122 | padding: 20, 123 | paddingTop:65, 124 | backgroundColor:'white' 125 | }, 126 | list: { 127 | flex: 1, 128 | marginTop:20, 129 | }, 130 | title:{ 131 | fontSize:16, 132 | fontWeight: 'bold' 133 | }, 134 | descriptionContainer:{ 135 | flexDirection: 'row', 136 | paddingRight: 50 137 | }, 138 | image:{ 139 | width: 50, 140 | height: 50, 141 | borderRadius: 25 142 | }, 143 | textDescription: { 144 | marginLeft: 10, 145 | color: 'gray' 146 | } 147 | }); -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { 4 | FlatList, 5 | I18nManager, 6 | Image, 7 | StyleSheet, 8 | Text, 9 | TouchableOpacity, 10 | View, 11 | } from "react-native"; 12 | import React, { PureComponent } from "react"; 13 | 14 | const defaultCircleSize = 16; 15 | const defaultCircleColor = "#007AFF"; 16 | const defaultLineWidth = 2; 17 | const defaultLineColor = "#007AFF"; 18 | const defaultTimeTextColor = "black"; 19 | const defaultDotColor = "white"; 20 | const defaultInnerCircle = "none"; 21 | const isRtl = I18nManager.isRTL; 22 | 23 | export default class Timeline extends PureComponent { 24 | constructor(props, context) { 25 | super(props, context); 26 | 27 | this._renderItem = this._renderItem.bind(this); 28 | this.renderTime = ( 29 | this.props.renderTime ? this.props.renderTime : this._renderTime 30 | ).bind(this); 31 | this.renderDetail = ( 32 | this.props.renderDetail ? this.props.renderDetail : this._renderDetail 33 | ).bind(this); 34 | this.renderCircle = ( 35 | this.props.renderCircle ? this.props.renderCircle : this._renderCircle 36 | ).bind(this); 37 | this.renderEvent = this._renderEvent.bind(this); 38 | 39 | this.state = { 40 | data: this.props.data, 41 | x: 0, 42 | width: 0, 43 | }; 44 | } 45 | 46 | static getDerivedStateFromProps(nextProps, prevState) { 47 | if (prevState.data !== nextProps.data) { 48 | return { 49 | data: nextProps.data, 50 | }; 51 | } 52 | 53 | return null; 54 | } 55 | 56 | render() { 57 | return ( 58 | 59 | {this.props.isUsingFlatlist ? ( 60 | index + ""} 67 | {...this.props.options} 68 | /> 69 | ) : ( 70 | this.state.data.map((item, index) => ( 71 | {this._renderItem({ item, index })} 72 | )) 73 | )} 74 | 75 | ); 76 | } 77 | 78 | _renderItem({ item, index }) { 79 | let content = null; 80 | switch (this.props.columnFormat) { 81 | case "single-column-left": 82 | content = ( 83 | 84 | {this.renderTime(item, index)} 85 | {this.renderEvent(item, index)} 86 | {this.renderCircle(item, index)} 87 | 88 | ); 89 | break; 90 | case "single-column-right": 91 | content = ( 92 | 93 | {this.renderEvent(item, index)} 94 | {this.renderTime(item, index)} 95 | {this.renderCircle(item, index)} 96 | 97 | ); 98 | break; 99 | case "two-column": 100 | content = 101 | (item.position && item.position == "right") || 102 | (!item.position && index % 2 == 0) ? ( 103 | 104 | {this.renderTime(item, index)} 105 | {this.renderEvent(item, index)} 106 | {this.renderCircle(item, index)} 107 | 108 | ) : ( 109 | 110 | {this.renderEvent(item, index)} 111 | {this.renderTime(item, index)} 112 | {this.renderCircle(item, index)} 113 | 114 | ); 115 | break; 116 | } 117 | return {content}; 118 | } 119 | 120 | _renderTime(rowData, rowID) { 121 | if (!this.props.showTime) { 122 | return null; 123 | } 124 | var timeWrapper = null; 125 | switch (this.props.columnFormat) { 126 | case "single-column-left": 127 | timeWrapper = { 128 | alignItems: "flex-end", 129 | }; 130 | break; 131 | case "single-column-right": 132 | timeWrapper = { 133 | alignItems: "flex-start", 134 | }; 135 | break; 136 | case "two-column": 137 | timeWrapper = { 138 | flex: 1, 139 | alignItems: 140 | (rowData.position && rowData.position == "right") || 141 | (!rowData.position && rowID % 2 == 0) 142 | ? "flex-end" 143 | : "flex-start", 144 | }; 145 | break; 146 | } 147 | const { isAllowFontScaling } = this.props; 148 | return ( 149 | 150 | 151 | 155 | {rowData.time} 156 | 157 | 158 | 159 | ); 160 | } 161 | 162 | _renderEvent(rowData, rowID) { 163 | const lineWidth = rowData.lineWidth 164 | ? rowData.lineWidth 165 | : this.props.lineWidth; 166 | const isLast = this.props.renderFullLine 167 | ? !this.props.renderFullLine 168 | : this.state.data.slice(-1)[0] === rowData; 169 | const lineColor = isLast 170 | ? "rgba(0,0,0,0)" 171 | : rowData.lineColor 172 | ? rowData.lineColor 173 | : this.props.lineColor; 174 | let opStyle = null; 175 | 176 | switch (this.props.columnFormat) { 177 | case "single-column-left": 178 | opStyle = { 179 | borderColor: lineColor, 180 | borderLeftWidth: lineWidth, 181 | borderRightWidth: 0, 182 | marginLeft: 20, 183 | paddingLeft: 20, 184 | }; 185 | break; 186 | case "single-column-right": 187 | opStyle = { 188 | borderColor: lineColor, 189 | borderLeftWidth: 0, 190 | borderRightWidth: lineWidth, 191 | marginRight: 20, 192 | paddingRight: 20, 193 | }; 194 | break; 195 | case "two-column": 196 | opStyle = 197 | (rowData.position && rowData.position == "right") || 198 | (!rowData.position && rowID % 2 == 0) 199 | ? { 200 | borderColor: lineColor, 201 | borderLeftWidth: lineWidth, 202 | borderRightWidth: 0, 203 | marginLeft: 20, 204 | paddingLeft: 20, 205 | } 206 | : { 207 | borderColor: lineColor, 208 | borderLeftWidth: 0, 209 | borderRightWidth: lineWidth, 210 | marginRight: 20, 211 | paddingRight: 20, 212 | }; 213 | break; 214 | } 215 | 216 | return ( 217 | { 225 | if (!this.state.x && !this.state.width) { 226 | const { x, width } = evt.nativeEvent.layout; 227 | this.setState({ x, width }); 228 | } 229 | }} 230 | > 231 | 235 | this.props.onEventPress ? this.props.onEventPress(rowData) : null 236 | } 237 | > 238 | 239 | {this.renderDetail(rowData, rowID)} 240 | 241 | {this._renderSeparator()} 242 | 243 | 244 | ); 245 | } 246 | 247 | _renderDetail(rowData, rowID) { 248 | const { isAllowFontScaling } = this.props; 249 | let description; 250 | if (typeof rowData.description === "string") { 251 | description = ( 252 | 260 | {rowData.description} 261 | 262 | ); 263 | } else if (typeof rowData.description === "object") { 264 | description = rowData.description; 265 | } 266 | 267 | return ( 268 | 269 | 273 | {rowData.title} 274 | 275 | {description} 276 | 277 | ); 278 | } 279 | 280 | _renderCircle(rowData, rowID) { 281 | var circleSize = rowData.circleSize 282 | ? rowData.circleSize 283 | : this.props.circleSize 284 | ? this.props.circleSize 285 | : defaultCircleSize; 286 | var circleColor = rowData.circleColor 287 | ? rowData.circleColor 288 | : this.props.circleColor 289 | ? this.props.circleColor 290 | : defaultCircleColor; 291 | var lineWidth = rowData.lineWidth 292 | ? rowData.lineWidth 293 | : this.props.lineWidth 294 | ? this.props.lineWidth 295 | : defaultLineWidth; 296 | 297 | var circleStyle = null; 298 | 299 | switch (this.props.columnFormat) { 300 | case "single-column-left": 301 | circleStyle = isRtl 302 | ? { 303 | width: this.state.width ? circleSize : 0, 304 | height: this.state.width ? circleSize : 0, 305 | borderRadius: circleSize / 2, 306 | backgroundColor: circleColor, 307 | right: this.state.width - circleSize / 2 - (lineWidth - 1) / 2, 308 | } 309 | : { 310 | width: this.state.x ? circleSize : 0, 311 | height: this.state.x ? circleSize : 0, 312 | borderRadius: circleSize / 2, 313 | backgroundColor: circleColor, 314 | left: this.state.x - circleSize / 2 + (lineWidth - 1) / 2, 315 | }; 316 | break; 317 | case "single-column-right": 318 | circleStyle = { 319 | width: this.state.width ? circleSize : 0, 320 | height: this.state.width ? circleSize : 0, 321 | borderRadius: circleSize / 2, 322 | backgroundColor: circleColor, 323 | left: this.state.width - circleSize / 2 - (lineWidth - 1) / 2, 324 | }; 325 | break; 326 | case "two-column": 327 | circleStyle = { 328 | width: this.state.width ? circleSize : 0, 329 | height: this.state.width ? circleSize : 0, 330 | borderRadius: circleSize / 2, 331 | backgroundColor: circleColor, 332 | left: this.state.width - circleSize / 2 - (lineWidth - 1) / 2, 333 | }; 334 | break; 335 | } 336 | 337 | var innerCircle = null; 338 | switch (this.props.innerCircle) { 339 | case "icon": 340 | let iconDefault = rowData.iconDefault 341 | ? rowData.iconDefault 342 | : this.props.iconDefault; 343 | let iconSource = rowData.icon ? rowData.icon : iconDefault; 344 | if (React.isValidElement(iconSource)) { 345 | innerCircle = iconSource; 346 | break; 347 | } 348 | if (rowData.icon) 349 | iconSource = 350 | rowData.icon.constructor === String 351 | ? { uri: rowData.icon } 352 | : rowData.icon; 353 | let iconStyle = { 354 | height: circleSize, 355 | width: circleSize, 356 | }; 357 | innerCircle = ( 358 | 363 | ); 364 | break; 365 | case "dot": 366 | const dotSize = this.props.dotSize 367 | ? this.props.dotSize 368 | : circleSize / 2; 369 | let dotStyle = { 370 | height: dotSize, 371 | width: dotSize, 372 | borderRadius: circleSize / 4, 373 | backgroundColor: rowData.dotColor 374 | ? rowData.dotColor 375 | : this.props.dotColor 376 | ? this.props.dotColor 377 | : defaultDotColor, 378 | }; 379 | innerCircle = ; 380 | break; 381 | case "element": 382 | innerCircle = rowData.icon; 383 | break; 384 | } 385 | return ( 386 | 387 | {innerCircle} 388 | 389 | ); 390 | } 391 | 392 | _renderSeparator() { 393 | if (!this.props.separator) { 394 | return null; 395 | } 396 | return ; 397 | } 398 | } 399 | 400 | Timeline.defaultProps = { 401 | circleSize: defaultCircleSize, 402 | circleColor: defaultCircleColor, 403 | lineWidth: defaultLineWidth, 404 | lineColor: defaultLineColor, 405 | innerCircle: defaultInnerCircle, 406 | columnFormat: "single-column-left", 407 | separator: false, 408 | showTime: true, 409 | isAllowFontScaling: true, 410 | isUsingFlatlist: true, 411 | }; 412 | 413 | const styles = StyleSheet.create({ 414 | container: { 415 | flex: 1, 416 | }, 417 | listview: { 418 | flex: 1, 419 | }, 420 | sectionHeader: { 421 | marginBottom: 15, 422 | backgroundColor: "#007AFF", 423 | height: 30, 424 | justifyContent: "center", 425 | }, 426 | sectionHeaderText: { 427 | color: "#FFF", 428 | fontSize: 18, 429 | alignSelf: "center", 430 | }, 431 | rowContainer: { 432 | flexDirection: "row", 433 | flex: 1, 434 | justifyContent: "center", 435 | }, 436 | timeContainer: { 437 | minWidth: 45, 438 | }, 439 | time: { 440 | textAlign: "right", 441 | color: defaultTimeTextColor, 442 | overflow: "hidden", 443 | }, 444 | circle: { 445 | width: 16, 446 | height: 16, 447 | borderRadius: 10, 448 | zIndex: 1, 449 | position: "absolute", 450 | alignItems: "center", 451 | justifyContent: "center", 452 | }, 453 | dot: { 454 | width: 8, 455 | height: 8, 456 | borderRadius: 4, 457 | backgroundColor: defaultDotColor, 458 | }, 459 | title: { 460 | fontSize: 16, 461 | fontWeight: "bold", 462 | }, 463 | details: { 464 | borderLeftWidth: defaultLineWidth, 465 | flexDirection: "column", 466 | flex: 1, 467 | }, 468 | detail: { paddingTop: 10, paddingBottom: 10 }, 469 | description: { 470 | marginTop: 10, 471 | }, 472 | separator: { 473 | height: 1, 474 | backgroundColor: "#aaa", 475 | marginTop: 10, 476 | marginBottom: 10, 477 | }, 478 | }); 479 | -------------------------------------------------------------------------------- /examples/Example/pages/react-native-timeline-flatlist.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import React, { PureComponent } from "react"; 4 | import { 5 | StyleSheet, 6 | FlatList, 7 | Image, 8 | View, 9 | Text, 10 | TouchableOpacity 11 | } from "react-native"; 12 | 13 | const defaultCircleSize = 16; 14 | const defaultCircleColor = "#007AFF"; 15 | const defaultLineWidth = 2; 16 | const defaultLineColor = "#007AFF"; 17 | const defaultTimeTextColor = "black"; 18 | const defaultDotColor = "white"; 19 | const defaultInnerCircle = "none"; 20 | 21 | export default class Timeline extends PureComponent { 22 | constructor(props, context) { 23 | super(props, context); 24 | 25 | this._renderItem = this._renderItem.bind(this); 26 | this.renderTime = (this.props.renderTime 27 | ? this.props.renderTime 28 | : this._renderTime 29 | ).bind(this); 30 | this.renderDetail = (this.props.renderDetail 31 | ? this.props.renderDetail 32 | : this._renderDetail 33 | ).bind(this); 34 | this.renderCircle = (this.props.renderCircle 35 | ? this.props.renderCircle 36 | : this._renderCircle 37 | ).bind(this); 38 | this.renderEvent = this._renderEvent.bind(this); 39 | 40 | this.state = { 41 | data: this.props.data, 42 | //dataSource: ds.cloneWithRows(this.props.data), 43 | x: 0, 44 | width: 0 45 | }; 46 | } 47 | 48 | static getDerivedStateFromProps(nextProps, prevState) { 49 | if (prevState.data !== nextProps.data) { 50 | return { 51 | data: nextProps.data 52 | }; 53 | 54 | } 55 | 56 | return null; 57 | } 58 | 59 | render() { 60 | return ( 61 | 62 | index + ""} 68 | {...this.props.options} 69 | /> 70 | 71 | ); 72 | } 73 | 74 | _renderItem({ item, index }) { 75 | let content = null; 76 | switch (this.props.columnFormat) { 77 | case "single-column-left": 78 | content = ( 79 | 80 | {this.renderTime(item, index)} 81 | {this.renderEvent(item, index)} 82 | {this.renderCircle(item, index)} 83 | 84 | ); 85 | break; 86 | case "single-column-right": 87 | content = ( 88 | 89 | {this.renderEvent(item, index)} 90 | {this.renderTime(item, index)} 91 | {this.renderCircle(item, index)} 92 | 93 | ); 94 | break; 95 | case "two-column": 96 | content = 97 | (item.position && item.position == "right") || (!item.position && index % 2 == 0) ? ( 98 | 99 | {this.renderTime(item, index)} 100 | {this.renderEvent(item, index)} 101 | {this.renderCircle(item, index)} 102 | 103 | ) : ( 104 | 105 | {this.renderEvent(item, index)} 106 | {this.renderTime(item, index)} 107 | {this.renderCircle(item, index)} 108 | 109 | ); 110 | break; 111 | } 112 | return {content}; 113 | } 114 | 115 | _renderTime(rowData, rowID) { 116 | if (!this.props.showTime) { 117 | return null; 118 | } 119 | var timeWrapper = null; 120 | switch (this.props.columnFormat) { 121 | case "single-column-left": 122 | timeWrapper = { 123 | alignItems: "flex-end" 124 | }; 125 | break; 126 | case "single-column-right": 127 | timeWrapper = { 128 | alignItems: "flex-start" 129 | }; 130 | break; 131 | case "two-column": 132 | timeWrapper = { 133 | flex: 1, 134 | alignItems: (rowData.position && rowData.position == "right") || (!rowData.position && rowID % 2 == 0) ? "flex-end" : "flex-start" 135 | }; 136 | break; 137 | } 138 | return ( 139 | 140 | 141 | 142 | {rowData.time} 143 | 144 | 145 | 146 | ); 147 | } 148 | 149 | _renderEvent(rowData, rowID) { 150 | const lineWidth = rowData.lineWidth 151 | ? rowData.lineWidth 152 | : this.props.lineWidth; 153 | const isLast = this.props.renderFullLine 154 | ? !this.props.renderFullLine 155 | : this.state.data.slice(-1)[0] === rowData; 156 | const lineColor = isLast 157 | ? "rgba(0,0,0,0)" 158 | : rowData.lineColor ? rowData.lineColor : this.props.lineColor; 159 | let opStyle = null; 160 | 161 | switch (this.props.columnFormat) { 162 | case "single-column-left": 163 | opStyle = { 164 | borderColor: lineColor, 165 | borderLeftWidth: lineWidth, 166 | borderRightWidth: 10, 167 | marginLeft: 20, 168 | paddingLeft: 20 169 | }; 170 | break; 171 | case "single-column-right": 172 | opStyle = { 173 | borderColor: lineColor, 174 | borderLeftWidth: 0, 175 | borderRightWidth: lineWidth, 176 | marginRight: 20, 177 | paddingRight: 20 178 | }; 179 | break; 180 | case "two-column": 181 | opStyle = 182 | (rowData.position && rowData.position == "right") || (!rowData.position && rowID % 2 == 0) 183 | ? { 184 | borderColor: lineColor, 185 | borderLeftWidth: lineWidth, 186 | borderRightWidth: 0, 187 | marginLeft: 20, 188 | paddingLeft: 20 189 | } 190 | : { 191 | borderColor: lineColor, 192 | borderLeftWidth: 0, 193 | borderRightWidth: lineWidth, 194 | marginRight: 20, 195 | paddingRight: 20 196 | }; 197 | break; 198 | } 199 | 200 | return ( 201 | { 204 | if (!this.state.x && !this.state.width) { 205 | const { x, width } = evt.nativeEvent.layout; 206 | this.setState({ x, width }); 207 | } 208 | }} 209 | > 210 | 214 | this.props.onEventPress ? this.props.onEventPress(rowData) : null 215 | } 216 | > 217 | 218 | {this.renderDetail(rowData, rowID)} 219 | 220 | {this._renderSeparator()} 221 | 222 | 223 | ); 224 | } 225 | 226 | _renderDetail(rowData, rowID) { 227 | let title = rowData.description ? ( 228 | 229 | 230 | {rowData.title} 231 | 232 | 233 | {rowData.description} 234 | 235 | 236 | ) : ( 237 | {rowData.title} 238 | ); 239 | return {title}; 240 | } 241 | 242 | _renderCircle(rowData, rowID) { 243 | var circleSize = rowData.circleSize 244 | ? rowData.circleSize 245 | : this.props.circleSize ? this.props.circleSize : defaultCircleSize; 246 | var circleColor = rowData.circleColor 247 | ? rowData.circleColor 248 | : this.props.circleColor ? this.props.circleColor : defaultCircleColor; 249 | var lineWidth = rowData.lineWidth 250 | ? rowData.lineWidth 251 | : this.props.lineWidth ? this.props.lineWidth : defaultLineWidth; 252 | 253 | var circleStyle = null; 254 | 255 | switch (this.props.columnFormat) { 256 | case "single-column-left": 257 | circleStyle = { 258 | width: this.state.x ? circleSize : 0, 259 | height: this.state.x ? circleSize : 0, 260 | borderRadius: circleSize / 2, 261 | backgroundColor: circleColor, 262 | left: this.state.x - circleSize / 2 + (lineWidth - 1) / 2 263 | }; 264 | break; 265 | case "single-column-right": 266 | circleStyle = { 267 | width: this.state.width ? circleSize : 0, 268 | height: this.state.width ? circleSize : 0, 269 | borderRadius: circleSize / 2, 270 | backgroundColor: circleColor, 271 | left: this.state.width - circleSize / 2 - (lineWidth - 1) / 2 272 | }; 273 | break; 274 | case "two-column": 275 | circleStyle = { 276 | width: this.state.width ? circleSize : 0, 277 | height: this.state.width ? circleSize : 0, 278 | borderRadius: circleSize / 2, 279 | backgroundColor: circleColor, 280 | left: this.state.width - circleSize / 2 - (lineWidth - 1) / 2 281 | }; 282 | break; 283 | } 284 | 285 | var innerCircle = null; 286 | switch (this.props.innerCircle) { 287 | case "icon": 288 | let iconDefault = rowData.iconDefault ? rowData.iconDefault : this.props.iconDefault; 289 | let iconSource = rowData.icon ? rowData.icon : iconDefault; 290 | if (rowData.icon) iconSource = rowData.icon.constructor === String ? { uri: rowData.icon } : rowData.icon 291 | let iconStyle = { 292 | height: circleSize, 293 | width: circleSize 294 | }; 295 | innerCircle = ( 296 | 301 | ); 302 | break; 303 | case "dot": 304 | let dotStyle = { 305 | height: circleSize / 2, 306 | width: circleSize / 2, 307 | borderRadius: circleSize / 4, 308 | backgroundColor: rowData.dotColor 309 | ? rowData.dotColor 310 | : this.props.dotColor ? this.props.dotColor : defaultDotColor 311 | }; 312 | innerCircle = ; 313 | break; 314 | } 315 | return ( 316 | 317 | {innerCircle} 318 | 319 | ); 320 | } 321 | 322 | _renderSeparator() { 323 | if (!this.props.separator) { 324 | return null; 325 | } 326 | return ; 327 | } 328 | } 329 | 330 | Timeline.defaultProps = { 331 | circleSize: defaultCircleSize, 332 | circleColor: defaultCircleColor, 333 | lineWidth: defaultLineWidth, 334 | lineColor: defaultLineColor, 335 | innerCircle: defaultInnerCircle, 336 | columnFormat: "single-column-left", 337 | separator: false, 338 | showTime: true 339 | }; 340 | 341 | const styles = StyleSheet.create({ 342 | container: { 343 | flex: 1 344 | }, 345 | listview: { 346 | flex: 0 347 | }, 348 | sectionHeader: { 349 | marginBottom: 15, 350 | backgroundColor: "#007AFF", 351 | height: 30, 352 | justifyContent: "center" 353 | }, 354 | sectionHeaderText: { 355 | color: "#FFF", 356 | fontSize: 18, 357 | alignSelf: "center" 358 | }, 359 | rowContainer: { 360 | flexDirection: "row", 361 | flex: 1, 362 | //alignItems: 'stretch', 363 | justifyContent: "center" 364 | }, 365 | timeContainer: { 366 | minWidth: 45 367 | }, 368 | time: { 369 | textAlign: "right", 370 | color: defaultTimeTextColor, 371 | overflow: "hidden" 372 | }, 373 | circle: { 374 | width: 16, 375 | height: 16, 376 | borderRadius: 10, 377 | zIndex: 1, 378 | position: "absolute", 379 | // left: 0, 380 | alignItems: "center", 381 | justifyContent: "center" 382 | }, 383 | dot: { 384 | width: 8, 385 | height: 8, 386 | borderRadius: 4, 387 | backgroundColor: defaultDotColor 388 | }, 389 | title: { 390 | fontSize: 16, 391 | fontWeight: "bold" 392 | }, 393 | details: { 394 | borderLeftWidth: defaultLineWidth, 395 | flexDirection: "column", 396 | flex: 1 397 | }, 398 | detail: { paddingTop: 10, paddingBottom: 10 }, 399 | description: { 400 | marginTop: 10 401 | }, 402 | separator: { 403 | height: 1, 404 | backgroundColor: "#aaa", 405 | marginTop: 10, 406 | marginBottom: 10 407 | } 408 | }); 409 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Native Timeline Flatlist 2 | 3 | [![npm version](https://badge.fury.io/js/react-native-timeline-flatlist.svg)](https://badge.fury.io/js/react-native-timeline-flatlist) 4 | [![Platform](https://img.shields.io/badge/react--native-0.61-blue.svg)](http://facebook.github.io/react-native/) 5 | 6 | Timeline component for React Native App work for Android and iOS 7 | 8 | It's a fork of [react-native-timeline-listview](https://github.com/thegamenicorus/react-native-timeline-listview) with some updates including FlatList, because old ListView is deprecated. 9 | 10 | Examples in examples folder and on Expo https://expo.io/@eugnis/react-native-timeline-flatlist-examples 11 | 12 | [DEMO HERE](https://snack.expo.io/@eugnis/eaae28) 13 | 14 | ![untitled-1](https://cloud.githubusercontent.com/assets/21040043/24750025/8c8d044e-1aef-11e7-8fd7-7d64431af7e4.png) 15 | 16 | **Table of Contents** 17 | 18 | - [Installation](#installation) 19 | - Usage 20 | - [Basic usage](#basic-usage) 21 | - [Custom example](#custom) 22 | - [Circle dot example](#circle-dot) 23 | - [Icon example](#icon) 24 | - [Override render example](#override-render) 25 | - [Pull to refresh and load more example](#pull-to-refresh-and-load-more) 26 | - Column Format (in v.0.2.0) 27 | - [Single column right](#single-column-right) 28 | - [Two column](#two-column) 29 | - [Time container hiding](#hide-time) 30 | - Configuration 31 | - [Data Object](#data-object) 32 | - [Timeline](#timeline) 33 | - [Shift problem](#shift-problem) 34 | 35 | ## Installation 36 | 37 | ``` 38 | npm i react-native-timeline-flatlist --save 39 | ``` 40 | 41 | or 42 | 43 | ``` 44 | yarn add react-native-timeline-flatlist 45 | ``` 46 | 47 | ## Basic Usage 48 | 49 | ![image2](https://cloud.githubusercontent.com/assets/21040043/24320617/6a7494ea-116b-11e7-9cf5-12244f5eec58.png) 50 | 51 | ```jsx 52 | import Timeline from 'react-native-timeline-flatlist' 53 | 54 | constructor(){ 55 | super() 56 | this.data = [ 57 | {time: '09:00', title: 'Event 1', description: 'Event 1 Description'}, 58 | {time: '10:45', title: 'Event 2', description: 'Event 2 Description'}, 59 | {time: '12:00', title: 'Event 3', description: 'Event 3 Description'}, 60 | {time: '14:00', title: 'Event 4', description: 'Event 4 Description'}, 61 | {time: '16:30', title: 'Event 5', description: 'Event 5 Description'} 62 | ] 63 | } 64 | 65 | render(){ 66 | return( 67 | 70 | ) 71 | } 72 | ``` 73 | 74 | [see full basic example](https://github.com/Eugnis/react-native-timeline-flatlist/blob/master/examples/Example/pages/basicExample.js) 75 | 76 | ## Custom 77 | 78 | ![image3](https://cloud.githubusercontent.com/assets/21040043/24320631/9df21a86-116b-11e7-8865-2631d35bc640.png) 79 | 80 | ```jsx 81 | render(){ 82 | return( 83 | 96 | ) 97 | } 98 | ``` 99 | 100 | [see full custom example](https://github.com/Eugnis/react-native-timeline-flatlist/blob/master/examples/Example/pages/customExample.js) 101 | 102 | ## Circle Dot 103 | 104 | ![image4](https://cloud.githubusercontent.com/assets/21040043/24320644/f5bc5b0a-116b-11e7-9252-2c9fc2361dc9.png) 105 | 106 | ```jsx 107 | render(){ 108 | return( 109 | 113 | ) 114 | } 115 | ``` 116 | 117 | [see full circle dot example](https://github.com/Eugnis/react-native-timeline-flatlist/blob/master/examples/Example/pages/dotExample.js) 118 | 119 | ## Icon 120 | 121 | ![image5](https://cloud.githubusercontent.com/assets/21040043/24320654/1c5de27e-116c-11e7-95cc-750d55e001b8.png) 122 | 123 | ```jsx 124 | constructor(){ 125 | super() 126 | this.data = [ 127 | {time: '09:00', title: 'Archery Training', description: 'The Beginner Archery and Beginner Crossbow course does not require you to bring any equipment, since everything you need will be provided for the course. ',lineColor:'#009688', icon: require('../img/archery.png')}, 128 | {time: '10:45', title: 'Play Badminton', description: 'Badminton is a racquet sport played using racquets to hit a shuttlecock across a net.', icon: require('../img/badminton.png')}, 129 | {time: '12:00', title: 'Lunch', icon: require('../img/lunch.png')}, 130 | {time: '14:00', title: 'Watch Soccer', description: 'Team sport played between two teams of eleven players with a spherical ball. ',lineColor:'#009688', icon: require('../img/soccer.png')}, 131 | {time: '16:30', title: 'Go to Fitness center', description: 'Look out for the Best Gym & Fitness Centers around me :)', icon: require('../img/dumbbell.png')} 132 | ] 133 | } 134 | render(){ 135 | return( 136 | 140 | ) 141 | } 142 | ``` 143 | 144 | Also you can pass any React element as icon or iconDefault: 145 | 146 | ``` 147 | this.data = [ 148 | ... 149 | {time: '12:00', title: 'Custom rendered icon', icon: }, 153 | ... 154 | ] 155 | } 156 | ``` 157 | 158 | [see full icon example](https://github.com/Eugnis/react-native-timeline-flatlist/blob/master/examples/Example/pages/iconExample.js) 159 | 160 | ## Override Render 161 | 162 | ![image6](https://cloud.githubusercontent.com/assets/21040043/24320661/36fe76e8-116c-11e7-950f-2968aef312bb.png) 163 | 164 | ```jsx 165 | constructor(){ 166 | super() 167 | this.renderDetail = this.renderDetail.bind(this) 168 | 169 | this.data = [ 170 | { 171 | time: '09:00', 172 | title: 'Archery Training', 173 | description: 'The Beginner Archery and Beginner Crossbow course does not require you to bring any equipment, since everything you need will be provided for the course. ', 174 | lineColor:'#009688', 175 | icon: require('../img/archery.png'), 176 | imageUrl: 'https://cloud.githubusercontent.com/assets/21040043/24240340/c0f96b3a-0fe3-11e7-8964-fe66e4d9be7a.jpg' 177 | }, 178 | { 179 | time: '10:45', 180 | title: 'Play Badminton', 181 | description: 'Badminton is a racquet sport played using racquets to hit a shuttlecock across a net.', 182 | icon: require('../img/badminton.png'), 183 | imageUrl: 'https://cloud.githubusercontent.com/assets/21040043/24240405/0ba41234-0fe4-11e7-919b-c3f88ced349c.jpg' 184 | }, 185 | { 186 | time: '12:00', 187 | title: 'Lunch', 188 | icon: require('../img/lunch.png'), 189 | }, 190 | { 191 | time: '14:00', 192 | title: 'Watch Soccer', 193 | description: 'Team sport played between two teams of eleven players with a spherical ball. ', 194 | lineColor:'#009688', 195 | icon: require('../img/soccer.png'), 196 | imageUrl: 'https://cloud.githubusercontent.com/assets/21040043/24240419/1f553dee-0fe4-11e7-8638-6025682232b1.jpg' 197 | }, 198 | { 199 | time: '16:30', 200 | title: 'Go to Fitness center', 201 | description: 'Look out for the Best Gym & Fitness Centers around me :)', 202 | icon: require('../img/dumbbell.png'), 203 | imageUrl: 'https://cloud.githubusercontent.com/assets/21040043/24240422/20d84f6c-0fe4-11e7-8f1d-9dbc594d0cfa.jpg' 204 | } 205 | ] 206 | } 207 | 208 | renderDetail(rowData, sectionID, rowID) { 209 | let title = {rowData.title} 210 | var desc = null 211 | if(rowData.description && rowData.imageUrl) 212 | desc = ( 213 | 214 | 215 | {rowData.description} 216 | 217 | ) 218 | 219 | return ( 220 | 221 | {title} 222 | {desc} 223 | 224 | ) 225 | } 226 | 227 | render(){ 228 | return( 229 | 233 | ) 234 | } 235 | ``` 236 | 237 | [see full override render example](https://github.com/Eugnis/react-native-timeline-flatlist/blob/master/examples/Example/pages/overrideRenderExample.js) 238 | 239 | ## Pull to refresh and load more 240 | 241 | ![rflm](https://cloud.githubusercontent.com/assets/21040043/26756369/304d2e7a-48cb-11e7-816d-66e8d40a97ee.png) 242 | 243 | ```jsx 244 | 245 | onRefresh(){ 246 | //set initial data 247 | } 248 | 249 | onEndReached() { 250 | //fetch next data 251 | } 252 | 253 | renderFooter() { 254 | //show loading indicator 255 | if (this.state.waiting) { 256 | return ; 257 | } else { 258 | return ~; 259 | } 260 | } 261 | 262 | render(){ 263 | return( 264 | 272 | ), 273 | renderFooter: this.renderFooter, 274 | onEndReached: this.onEndReached 275 | }} 276 | /> 277 | ) 278 | } 279 | ``` 280 | 281 | [see full refresh and load more example](https://github.com/Eugnis/react-native-timeline-flatlist/blob/master/examples/Example/pages/refreshLoadMoreExample.js) 282 | 283 | ## Column Format 284 | 285 | ### Single Column Right 286 | 287 | ![simulator screen shot apr 6 2560 be 5 19 51 pm](https://cloud.githubusercontent.com/assets/21040043/24749469/60a7869e-1aed-11e7-9c41-f87f866b2d8d.png) 288 | 289 | ```jsx 290 | render(){ 291 | return( 292 | 296 | ) 297 | } 298 | ``` 299 | 300 | [see full single column right example](https://github.com/Eugnis/react-native-timeline-flatlist/blob/master/examples/Example/pages/singleRightExample.js) 301 | 302 | ### Two Column 303 | 304 | ![simulator screen shot apr 6 2560 be 5 05 32 pm](https://cloud.githubusercontent.com/assets/21040043/24749638/0515f210-1aee-11e7-82af-082d93efb618.png) 305 | 306 | ```jsx 307 | render(){ 308 | return( 309 | 313 | ) 314 | } 315 | ``` 316 | 317 | [see full two column example](https://github.com/Eugnis/react-native-timeline-flatlist/blob/master/examples/Example/pages/twoColumnExample.js) 318 | 319 | ### Time container hiding 320 | 321 | ![showTime](https://user-images.githubusercontent.com/6987730/35145888-fae0f1e2-fd3b-11e7-9571-2143342512c8.png) 322 | 323 | ```jsx 324 | render(){ 325 | return( 326 | 330 | ) 331 | } 332 | ``` 333 | 334 | ## Configuration 335 | 336 | #### Data Object: 337 | 338 | | Property | Type | Default | Description | 339 | | ------------------- | ---------------------------------- | --------------------------------- | --------------------------------------------------- | 340 | | time | string | null | event time | 341 | | title | string | null | event title | 342 | | description | string or object | null | event description | 343 | | lineWidth | int | same as lineWidth of 'Timeline' | event line width | 344 | | lineColor | string | same as lineColor of 'Timeline' | event line color | 345 | | eventContainerStyle | object | null | custom styles of line | 346 | | circleSize | int | same as circleSize of 'Timeline' | event circle size | 347 | | circleColor | string | same as circleColor of 'Timeline' | event circle color | 348 | | dotColor | string | same as dotColor of 'Timeline' | event dot color (innerCircle = 'dot') | 349 | | icon | obj(image source) or React.Element | same as icon of 'Timeline' | event icon (innerCircle = 'icon' or 'element') | 350 | | position | string | null | event side in 'two-column' layout : 'left', 'right' | 351 | 352 | #### Timeline: 353 | 354 | | Property | Type | Default | Description | 355 | | ---------------------- | ----------------------------------- | -------------------------- | ---------------------------------------------------------------- | 356 | | data | data object | null | timeline data | 357 | | innerCircle | string | null | timeline mode : 'none', 'dot', 'icon', 'element' | 358 | | separator | bool | true | render separator line of events | 359 | | columnFormat | string | 'single-left' | can be 'single-column-left', 'single-column-right', 'two-column' | 360 | | lineWidth | int | 2 | timeline line width | 361 | | lineColor | string | '#007AFF' | timeline line color | 362 | | circleSize | int | 16 | timeline circle size | 363 | | circleColor | string | '#007AFF' | timeline circle color | 364 | | dotColor | string | 'white' | timeline dot color (innerCircle = 'dot') | 365 | | dotSize | int | circleSize / 2 | timeline dot size (innerCircle = 'dot') | 366 | | iconDefault (or icon) | obj(image source) or React.Element | same as icon of 'Timeline' | default event icon | 367 | | style | object | null | custom styles of Timeline container | 368 | | listViewStyle | object | null | custom styles of inner ListView | 369 | | listViewContainerStyle | object | null | custom styles of inner ListView container | 370 | | timeStyle | object | null | custom styles of event time | 371 | | titleStyle | object | null | custom styles of event title | 372 | | descriptionStyle | object | null | custom styles of event description | 373 | | iconStyle | object | null | custom styles of event icon | 374 | | separatorStyle | object | null | custom styles of separator | 375 | | rowContainerStyle | object | null | custom styles of event container | 376 | | eventContainerStyle | object | null | custom styles of the event part of the row (line) | 377 | | eventDetailStyle | object | null | custom styles of the event detail part of the row (line) | 378 | | timeContainerStyle | object | null | custom styles of container of event time | 379 | | detailContainerStyle | object | null | custom styles of container of event title and event description | 380 | | onEventPress | function(event) | null | function to be invoked when event was pressed | 381 | | renderTime | function(rowData, sectionID, rowID) | null | custom render event time | 382 | | renderDetail | function(rowData, sectionID, rowID) | null | custom render event title and event description | 383 | | renderCircle | function(rowData, sectionID, rowID) | null | custom render circle | 384 | | renderFullLine | bool | false | render event border on last timeline item | 385 | | options | object | null | ListView properties | 386 | | showTime | boolean | true | Time container options | 387 | | isUsingFlatlist | boolean | false | Render inner components in Flatlist (if false - render in View) | 388 | 389 | ## Shift problem 390 | 391 | Text width of event time may not be the same. 392 | 393 | ![untitled-1](https://cloud.githubusercontent.com/assets/21040043/24321589/78d0c77c-1182-11e7-9c0f-69ebe591cb14.png) 394 | 395 | fix by add 'minWidth' in 'timeContainerStyle' to appropriate value 396 | 397 | ```jsx 398 | render(){ 399 | return( 400 | 404 | ) 405 | } 406 | ``` 407 | 408 | ## Timeline is rendered, but not displayed until scroll 409 | 410 | fix by add removeClippedSubviews: false into options 411 | 412 | ```jsx 413 | render(){ 414 | return( 415 | 421 | ) 422 | } 423 | ``` 424 | --------------------------------------------------------------------------------