├── .babelrc
├── .gitignore
├── .npmignore
├── README.md
├── demo.gif
├── example
├── app.css
├── index.template.html
├── main.js
└── routes
│ ├── ExampleTableView1.js
│ ├── ExampleTableView2.js
│ └── Home.js
├── lib
├── ReactRefreshInfiniteTableView.js
├── rri.js
└── spinner.css
├── package.json
├── server.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "es2015",
4 | "react",
5 | "stage-0"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | *.log
5 | *.mov
6 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | *.gif
2 | example
3 | node_modules
4 | .DS_Store
5 | dist
6 | *.log
7 | webpack.config.js
8 | server.js
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Thank you guys so much for all your support. The project has been discontinued. Instead, I will make another one with better performance on both desktop and mobile
2 |
3 | # React-Refresh-Infinite-TableView
4 |
5 | [](https://www.npmjs.com/package/react-refresh-infinite-tableview)
6 | [](https://www.npmjs.com/package/react-refresh-infinite-tableview)
7 | ___
8 |
9 | 
10 |
11 | ### Features 👀
12 |
13 | - Pull to Refresh
14 | - Pull to Load More
15 | - Fully Customizable Loading Indicator
16 | - Subclass-able React Component
17 |
18 | ### How to Install 😍?
19 |
20 | 0. via npm install
21 | ```
22 | npm install --save react-refresh-infinite-tableview
23 | ```
24 | 1. or manually
25 | - extract the ```rri.js``` and ```spinner.css``` from ```lib/```, and use them in your projects.
26 |
27 | ### How to Use 🤔?
28 | - You can use it with default spinners or your custom spinners
29 | - Below are some setups, if you are looking for detail, please take a look at /examples
30 |
31 | ```
32 | import ReactRefreshInfiniteTableView from 'react-refresh-infinite-tableview'
33 | ```
34 |
35 | - Use Default Spinners
36 |
37 | - subclass the ```ReactRefreshInfiniteTableView```
38 | ```es6
39 | class ExampleTableView extends ReactRefreshInfiniteTableView {
40 | //...
41 | }
42 | ```
43 |
44 | - attach an scroll event listener to your scrollview
45 | ```es6
46 |
47 | ```
48 | - set props to your tableview component
49 | ```es6
50 |
52 | ```
53 | - handle scroll events
54 | ```es6
55 | // handle onScrollToTop
56 | handleScrollToTop(completed) {
57 | // refresh data
58 | // ...
59 |
60 | // once received data
61 | completed()
62 | this.setState({data: newData})
63 | }
64 |
65 | // handle onScrollToBottom
66 | handleScrollToBottom(completed) {
67 | // load more data
68 | // ...
69 |
70 | // once received data
71 | completed()
72 | this.setState({data1: newData})
73 | }
74 | ```
75 | - see ExampleTableView1 for details
76 |
77 | - Use your own loading indicators
78 | - first, you need to follow the basic set up as the above(use default spinner)
79 | - set useDefaultIndicator to false for your component
80 | ```es6
81 | useDefaultIndicator={false}
82 | ```
83 | - construct your own indicators with jsx
84 | ```es6
85 | // customize your Refresh Indicator here
86 | refreshIndicator() {
87 | if (this.state.isRefreshing) {
88 | return (
89 |
🏃...
90 | )
91 | }
92 | return
93 | }
94 | // customize your Load-more Indicator here
95 | loadMoreIndicator() {
96 | if (this.state.isLoadingMore) {
97 | return (
98 |
...🏃
99 | )
100 | }
101 | return
102 | }
103 | ```
104 | - render your indicators with your tableview
105 | ```es6
106 |
107 | {this.refreshIndicator()}
108 | {cells}
109 | {this.loadMoreIndicator()}
110 |
111 | ```
112 | - see ExampleTableView2 for details
113 |
114 | - You can also disable the scrollToTop or scrollToBottom by just by just not setting the props.
115 | ```es6
116 |
117 | ```
118 |
119 | ### TODO:
120 |
121 | - Customizable default spinner
122 | - Trigger scroll-to-top event when pull down if the tableview is already at the top
123 |
124 | ### Demo 😮
125 |
126 | - Run the demo with
127 | ```
128 | npm install
129 | npm start
130 | ```
131 | then go to http://localhost:3000/
132 |
133 | - P.S. In the demo, you may notice that the page will auto-refresh after you change the code because the demo is based on my another repo [React-SPA-Starter](https://github.com/calvinchankf/React-SPA-Starter), which is a very handy starter-kit for react dev.
134 |
--------------------------------------------------------------------------------
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calvinchankf/ReactRefreshinFiniteTableView/59fbb5f5afbbd4645bac9e2f8b7b933d49061cbe/demo.gif
--------------------------------------------------------------------------------
/example/app.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #EEEEEE;
3 | }
4 |
5 | .title {
6 | font-size: large;
7 | height: 50px;
8 | line-height: 50px;
9 | }
10 |
11 | .tableView {
12 | height: calc( 100vh - 100px );
13 | overflow-y: scroll;
14 | }
15 |
16 | .tableView .list-group-item:first-child,
17 | .tableView .list-group-item:last-child {
18 | border-radius: 0px;
19 | }
20 |
--------------------------------------------------------------------------------
/example/index.template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
React Refresh Infinite TableView
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/example/main.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { render } from 'react-dom'
3 | import { Router, Route, IndexRoute, Link, IndexLink, browserHistory } from 'react-router'
4 |
5 | import Home from './routes/Home.js'
6 |
7 | render((
8 |
9 |
10 |
11 | ), document.getElementById('root'))
12 |
--------------------------------------------------------------------------------
/example/routes/ExampleTableView1.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 |
4 | // use default loading spinners
5 | import ReactRefreshInfiniteTableView from '../../lib/ReactRefreshInfiniteTableView.js'
6 |
7 | export default class ExampleTableView1 extends ReactRefreshInfiniteTableView {
8 |
9 | constructor(props) {
10 | super(props)
11 | }
12 |
13 | render() {
14 | var cells = this.props.dataSource.map(function(item, index) {
15 | return
16 | {item}
17 |
18 | })
19 |
20 | return (
21 | // remember to invoke viewDidScroll from superclass(InfinitScrollView)
22 |
23 | {cells}
24 |
25 | )
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/example/routes/ExampleTableView2.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 |
4 | // customize loading Indicators
5 | import ReactRefreshInfiniteTableView from '../../lib/ReactRefreshInfiniteTableView.js'
6 |
7 | export default class ExampleTableView2 extends ReactRefreshInfiniteTableView {
8 |
9 | constructor(props) {
10 | super(props)
11 | }
12 |
13 | render() {
14 |
15 | var cells = this.props.dataSource.map(function(item, index) {
16 | return
17 | {item}
18 |
19 | })
20 |
21 | return (
22 | // remember to invoke viewDidScroll from superclass(InfinitScrollView)
23 |
24 | {this.refreshIndicator()}
25 | {cells}
26 | {this.loadMoreIndicator()}
27 |
28 | )
29 | }
30 |
31 | // customize your Refresh Indicator here
32 | refreshIndicator() {
33 | if (this.state.isRefreshing) {
34 | return (
35 |
🏃...
36 | )
37 | }
38 | return
39 | }
40 |
41 | // customize your Load-more Indicator here
42 | loadMoreIndicator() {
43 | if (this.state.isLoadingMore) {
44 | return (
45 |
...🏃
46 | )
47 | }
48 | return
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/example/routes/Home.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import '../../node_modules/bootstrap/dist/css/bootstrap.css'
3 | import '../app.css'
4 |
5 | import ExampleTableView1 from './ExampleTableView1.js'
6 | import ExampleTableView2 from './ExampleTableView2.js'
7 |
8 | export default class Home extends React.Component {
9 |
10 | constructor(props) {
11 | super(props)
12 |
13 | // initial data for tableviews
14 | var data1 = this.initData()
15 | var data2 = this.initData()
16 | this.state = {data1: data1, data2: data2}
17 |
18 | // recommend that you bind your event handlers in the constructor so they are only bound once for every instance
19 | // https://facebook.github.io/react/docs/reusable-components.html#no-autobinding
20 | this.handleScrollToTop1 = this.handleScrollToTop1.bind(this)
21 | this.handleScrollToBottom1 = this.handleScrollToBottom1.bind(this)
22 |
23 | this.handleScrollToTop2 = this.handleScrollToTop2.bind(this)
24 | this.handleScrollToBottom2 = this.handleScrollToBottom2.bind(this)
25 | }
26 |
27 | render() {
28 | return (
29 |
30 |
31 |
Default
32 |
Custom
33 |
34 |
35 |
40 |
46 |
47 |
48 | )
49 | }
50 |
51 | initData() {
52 | var data = []
53 | for (var i=0; i<20; i++) {
54 | data.push(i)
55 | }
56 | return data
57 | }
58 |
59 | moreData(oldData) {
60 | var newData = Object.assign([], oldData)
61 | var base = newData[newData.length-1]
62 | for (var i=base+1; i<=base+20; i++) {
63 | newData.push(i)
64 | }
65 | return newData
66 | }
67 |
68 | // example 1
69 | handleScrollToTop1(completed) {
70 | // refresh
71 | setTimeout(function() {
72 | var data = this.initData()
73 | console.log(data)
74 |
75 | // completed is a callback to tell infinite table to hide loading indicator
76 | // must invcke completed before setState
77 | completed()
78 | this.setState({data1: data})
79 |
80 | }.bind(this), 1000)
81 | }
82 |
83 | handleScrollToBottom1(completed) {
84 | // load more
85 | setTimeout(function() {
86 | var newData = this.moreData(this.state.data1)
87 | console.log(newData)
88 |
89 | completed()
90 | this.setState({data1: newData})
91 |
92 | }.bind(this), 1000)
93 | }
94 |
95 | // example 2
96 | handleScrollToTop2(completed) {
97 | // refresh
98 | setTimeout(function() {
99 | var data = this.initData()
100 | console.log(data)
101 |
102 | completed()
103 | this.setState({data2: data})
104 |
105 | }.bind(this), 1000)
106 | }
107 |
108 | handleScrollToBottom2(completed) {
109 | // load more
110 | setTimeout(function() {
111 | var newData = this.moreData(this.state.data2)
112 | console.log(newData)
113 |
114 | completed()
115 | this.setState({data2: newData})
116 |
117 | }.bind(this), 1000)
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/lib/ReactRefreshInfiniteTableView.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import './spinner.css'
4 |
5 | export default class ReactRefreshInfiniteTableView extends React.Component {
6 |
7 | constructor(props) {
8 | super(props)
9 | this.viewDidScroll = this.viewDidScroll.bind(this)
10 | this.state = {
11 | isRefreshing : false,
12 | isLoadingMore : false
13 | }
14 | }
15 |
16 | render() {
17 | // override this to render your own components
18 | return (
19 |
20 | )
21 | }
22 |
23 | findNodeIndex(dom) {
24 | var targetNodeIndex = 0
25 | var nodes = document.getElementsByClassName(dom.className)
26 | for (var i=0; i< nodes.length; i++) {
27 | if (nodes[i]==dom) {
28 | targetNodeIndex = i
29 | break
30 | }
31 | }
32 | return targetNodeIndex
33 | }
34 |
35 | viewDidScroll(event) {
36 |
37 | var dom = ReactDOM.findDOMNode(this)
38 |
39 | // vars for UI
40 | var tableViewIdName = dom.id
41 | var tableViewClassName = dom.className
42 | var targetNodeIndex = this.findNodeIndex(dom) // the index of target node within the nodes with same className
43 | var isFindNodeById = tableViewIdName ? true : false // prefer use id becox less calculation
44 | var indicatorClassName = "infinit-table-spinner"
45 |
46 | // vars for calculation
47 | var scrollviewOffsetY = dom.scrollTop
48 | var scrollviewFrameHeight = dom.clientHeight
49 | var scrollviewContentHeight = dom.scrollHeight
50 | var sum = scrollviewOffsetY+scrollviewFrameHeight
51 |
52 | if (sum <= scrollviewFrameHeight) {
53 |
54 | // disable scroll to top if onScrollToTop isn't set
55 | if (!this.props.onScrollToTop) { return }
56 |
57 | // console.log('ReactRefreshInfiniteTableView onScrollToTop')
58 |
59 | if (this.state.isRefreshing) { return }
60 | this.setState({isRefreshing: true})
61 |
62 | // use default refresh indicator
63 | if (this.props.useDefaultIndicator) {
64 | // spinner for refreshing
65 | var refreshIndicator = document.createElement("div")
66 | refreshIndicator.className = indicatorClassName
67 |
68 | var tableView = isFindNodeById ? document.getElementById(tableViewIdName) : document.getElementsByClassName(tableViewClassName)[targetNodeIndex]
69 | tableView.insertBefore(refreshIndicator, tableView.firstChild)
70 | }
71 |
72 | // event
73 | this.props.onScrollToTop(function() {
74 |
75 | this.setState({isRefreshing: false})
76 |
77 | if (this.props.useDefaultIndicator) {
78 | var tableView = isFindNodeById ? document.getElementById(tableViewIdName) : document.getElementsByClassName(tableViewClassName)[targetNodeIndex]
79 | var firstChild = tableView.firstChild
80 | if (firstChild.className.indexOf(indicatorClassName) > -1) {
81 | tableView.removeChild(firstChild)
82 | }
83 | }
84 |
85 | }.bind(this))
86 |
87 | } else if (sum >= scrollviewContentHeight) {
88 |
89 | // disable scroll to top if onScrollToTop isn't set
90 | if (!this.props.onScrollToBottom) { return }
91 |
92 | // console.log('ReactRefreshInfiniteTableView onScrollToBottom');
93 |
94 | if (this.state.isLoadingMore) { return }
95 | this.setState({isLoadingMore: true})
96 |
97 | // use default load more indicator
98 | if (this.props.useDefaultIndicator) {
99 | // spinner for loading more
100 | var loadMoreIndicator = document.createElement("div")
101 | loadMoreIndicator.className = indicatorClassName
102 |
103 | var tableView = isFindNodeById ? document.getElementById(tableViewIdName) : document.getElementsByClassName(tableViewClassName)[targetNodeIndex]
104 | tableView.insertBefore(loadMoreIndicator, tableView.lastChild.nextSibling)
105 | }
106 |
107 | // event
108 | this.props.onScrollToBottom(function() {
109 |
110 | this.setState({isLoadingMore: false})
111 |
112 | if (this.props.useDefaultIndicator) {
113 | var tableView = isFindNodeById ? document.getElementById(tableViewIdName) : document.getElementsByClassName(tableViewClassName)[targetNodeIndex]
114 | var lastChild = tableView.lastChild
115 | if (lastChild.className.indexOf(indicatorClassName) > -1) {
116 | tableView.removeChild(lastChild)
117 | }
118 | }
119 |
120 | }.bind(this))
121 | }
122 |
123 | }
124 | }
125 |
126 | ReactRefreshInfiniteTableView.defaultProps = {
127 | useDefaultIndicator: true
128 | }
129 |
--------------------------------------------------------------------------------
/lib/rri.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | 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; }; }();
8 |
9 | var _react = require('react');
10 |
11 | var _react2 = _interopRequireDefault(_react);
12 |
13 | var _reactDom = require('react-dom');
14 |
15 | var _reactDom2 = _interopRequireDefault(_reactDom);
16 |
17 | require('./spinner.css');
18 |
19 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20 |
21 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
22 |
23 | 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; }
24 |
25 | 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; }
26 |
27 | var ReactRefreshInfiniteTableView = function (_React$Component) {
28 | _inherits(ReactRefreshInfiniteTableView, _React$Component);
29 |
30 | function ReactRefreshInfiniteTableView(props) {
31 | _classCallCheck(this, ReactRefreshInfiniteTableView);
32 |
33 | var _this = _possibleConstructorReturn(this, (ReactRefreshInfiniteTableView.__proto__ || Object.getPrototypeOf(ReactRefreshInfiniteTableView)).call(this, props));
34 |
35 | _this.viewDidScroll = _this.viewDidScroll.bind(_this);
36 | _this.state = {
37 | isRefreshing: false,
38 | isLoadingMore: false
39 | };
40 | return _this;
41 | }
42 |
43 | _createClass(ReactRefreshInfiniteTableView, [{
44 | key: 'render',
45 | value: function render() {
46 | // override this to render your own components
47 | return _react2.default.createElement('div', null);
48 | }
49 | }, {
50 | key: 'findNodeIndex',
51 | value: function findNodeIndex(dom) {
52 | var targetNodeIndex = 0;
53 | var nodes = document.getElementsByClassName(dom.className);
54 | for (var i = 0; i < nodes.length; i++) {
55 | if (nodes[i] == dom) {
56 | targetNodeIndex = i;
57 | break;
58 | }
59 | }
60 | return targetNodeIndex;
61 | }
62 | }, {
63 | key: 'viewDidScroll',
64 | value: function viewDidScroll(event) {
65 |
66 | var dom = _reactDom2.default.findDOMNode(this);
67 |
68 | // vars for UI
69 | var tableViewIdName = dom.id;
70 | var tableViewClassName = dom.className;
71 | var targetNodeIndex = this.findNodeIndex(dom); // the index of target node within the nodes with same className
72 | var isFindNodeById = tableViewIdName ? true : false; // prefer use id becox less calculation
73 | var indicatorClassName = "infinit-table-spinner";
74 |
75 | // vars for calculation
76 | var scrollviewOffsetY = dom.scrollTop;
77 | var scrollviewFrameHeight = dom.clientHeight;
78 | var scrollviewContentHeight = dom.scrollHeight;
79 | var sum = scrollviewOffsetY + scrollviewFrameHeight;
80 |
81 | if (sum <= scrollviewFrameHeight) {
82 |
83 | // disable scroll to top if onScrollToTop isn't set
84 | if (!this.props.onScrollToTop) {
85 | return;
86 | }
87 |
88 | // console.log('ReactRefreshInfiniteTableView onScrollToTop')
89 |
90 | if (this.state.isRefreshing) {
91 | return;
92 | }
93 | this.setState({ isRefreshing: true });
94 |
95 | // use default refresh indicator
96 | if (this.props.useDefaultIndicator) {
97 | // spinner for refreshing
98 | var refreshIndicator = document.createElement("div");
99 | refreshIndicator.className = indicatorClassName;
100 |
101 | var tableView = isFindNodeById ? document.getElementById(tableViewIdName) : document.getElementsByClassName(tableViewClassName)[targetNodeIndex];
102 | tableView.insertBefore(refreshIndicator, tableView.firstChild);
103 | }
104 |
105 | // event
106 | this.props.onScrollToTop(function () {
107 |
108 | this.setState({ isRefreshing: false });
109 |
110 | if (this.props.useDefaultIndicator) {
111 | var tableView = isFindNodeById ? document.getElementById(tableViewIdName) : document.getElementsByClassName(tableViewClassName)[targetNodeIndex];
112 | var firstChild = tableView.firstChild;
113 | if (firstChild.className.indexOf(indicatorClassName) > -1) {
114 | tableView.removeChild(firstChild);
115 | }
116 | }
117 | }.bind(this));
118 | } else if (sum >= scrollviewContentHeight) {
119 |
120 | // disable scroll to top if onScrollToTop isn't set
121 | if (!this.props.onScrollToBottom) {
122 | return;
123 | }
124 |
125 | // console.log('ReactRefreshInfiniteTableView onScrollToBottom');
126 |
127 | if (this.state.isLoadingMore) {
128 | return;
129 | }
130 | this.setState({ isLoadingMore: true });
131 |
132 | // use default load more indicator
133 | if (this.props.useDefaultIndicator) {
134 | // spinner for loading more
135 | var loadMoreIndicator = document.createElement("div");
136 | loadMoreIndicator.className = indicatorClassName;
137 |
138 | var tableView = isFindNodeById ? document.getElementById(tableViewIdName) : document.getElementsByClassName(tableViewClassName)[targetNodeIndex];
139 | tableView.insertBefore(loadMoreIndicator, tableView.lastChild.nextSibling);
140 | }
141 |
142 | // event
143 | this.props.onScrollToBottom(function () {
144 |
145 | this.setState({ isLoadingMore: false });
146 |
147 | if (this.props.useDefaultIndicator) {
148 | var tableView = isFindNodeById ? document.getElementById(tableViewIdName) : document.getElementsByClassName(tableViewClassName)[targetNodeIndex];
149 | var lastChild = tableView.lastChild;
150 | if (lastChild.className.indexOf(indicatorClassName) > -1) {
151 | tableView.removeChild(lastChild);
152 | }
153 | }
154 | }.bind(this));
155 | }
156 | }
157 | }]);
158 |
159 | return ReactRefreshInfiniteTableView;
160 | }(_react2.default.Component);
161 |
162 | exports.default = ReactRefreshInfiniteTableView;
163 |
164 |
165 | ReactRefreshInfiniteTableView.defaultProps = {
166 | useDefaultIndicator: true
167 | };
168 |
--------------------------------------------------------------------------------
/lib/spinner.css:
--------------------------------------------------------------------------------
1 | .infinit-table-spinner {
2 | margin: 16px auto 16px auto;
3 | height: 32px;
4 | width: 32px;
5 | animation: rotate-spinner 1s infinite linear;
6 | border: 2px solid #3498db;
7 | border-right-color: transparent;
8 | border-radius: 50%;
9 | }
10 |
11 | @keyframes rotate-spinner {
12 | 0% { transform: rotate(0deg); }
13 | 100% { transform: rotate(360deg); }
14 | }
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-refresh-infinite-tableview",
3 | "version": "1.0.3",
4 | "description": "A Subclass-able React Component to make a simple Pull-To-Refresh and Infinite TableView",
5 | "main": "./lib/rri.js",
6 | "author": {
7 | "name": "calvinchankf",
8 | "email": "chan9118kin@gmail.com"
9 | },
10 | "license": "MIT",
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/calvinchankf/ReactRefreshinFiniteTableView.git"
14 | },
15 | "bugs": {
16 | "url": "https://github.com/calvinchankf/ReactRefreshinFiniteTableView/issues"
17 | },
18 | "scripts": {
19 | "start": "node server"
20 | },
21 | "keywords": [
22 | "react",
23 | "react-component",
24 | "scrolling",
25 | "tableview",
26 | "pull to refresh",
27 | "refresh",
28 | "load more",
29 | "infinite"
30 | ],
31 | "dependencies": {
32 | },
33 | "devDependencies": {
34 | "babel-preset-react-hmre": "^1.1.0",
35 | "react-addons-test-utils": "^0.14.3",
36 | "react-transform-hmr": "^1.0.0",
37 | "webpack-dev-middleware": "^1.2.0",
38 | "webpack-hot-middleware": "^2.2.0",
39 | "babel-cli": "^6.4.0",
40 | "babel-loader": "^6.2.4",
41 | "babel-preset-es2015": "^6.9.0",
42 | "babel-preset-react": "^6.5.0",
43 | "babel-preset-stage-0": "^6.5.0",
44 | "bootstrap": "^3.3.6",
45 | "css-loader": "^0.23.1",
46 | "express": "^4.13.4",
47 | "file-loader": "^0.8.5",
48 | "html-webpack-plugin": "^1.7.0",
49 | "json-loader": "^0.5.4",
50 | "react": "^0.14.3",
51 | "react-dom": "^0.14.3",
52 | "react-router": "^2.4.1",
53 | "style-loader": "^0.13.1",
54 | "url-loader": "^0.5.7",
55 | "webpack": "^1.13.1"
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var webpackConfig = require('./webpack.config.js');
5 |
6 | var port = process.env.PORT || 3000 ;
7 | var app = express();
8 |
9 | var webpackMiddleware = require('webpack-dev-middleware');
10 | var webpackHotMiddleware = require('webpack-hot-middleware');
11 |
12 | var compiler = webpack(webpackConfig);
13 | var middleware = webpackMiddleware(compiler, {
14 | publicPath: webpackConfig.output.publicPath,
15 | contentBase: 'src',
16 | stats: {
17 | colors: true,
18 | hash: false,
19 | timings: true,
20 | chunks: false,
21 | chunkModules: false,
22 | modules: false
23 | }
24 | });
25 |
26 | app.use(middleware);
27 | app.use(webpackHotMiddleware(compiler));
28 | app.use(express.static(__dirname + '/dist'));
29 | app.get('*', function response(req, res) {
30 | res.write(middleware.fileSystem.readFileSync(path.join(__dirname, 'dist/index.html')));
31 | });
32 |
33 | app.listen(port, '0.0.0.0', function onStart(err) {
34 | if (err) {
35 | console.log(err);
36 | }
37 | console.info('==> 🌎 Listening on port %s. Open up http://0.0.0.0:%s/ in your browser.', port, port);
38 | });
39 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 | var HtmlWebpackPlugin = require('html-webpack-plugin');
4 |
5 | module.exports = {
6 | devtool: 'eval-source-map',
7 | entry: [
8 | 'webpack-hot-middleware/client?reload=true',
9 | path.join(__dirname, 'example/main.js')
10 | ],
11 | output: {
12 | path: path.join(__dirname, '/dist/'),
13 | filename: '[name].js',
14 | publicPath: '/'
15 | },
16 | plugins: [
17 | new HtmlWebpackPlugin({
18 | template: 'example/index.template.html',
19 | inject: 'body',
20 | filename: 'index.html'
21 | }),
22 | new webpack.HotModuleReplacementPlugin(),
23 | new webpack.NoErrorsPlugin(),
24 | new webpack.DefinePlugin({
25 | 'process.env.NODE_ENV': JSON.stringify('development')
26 | })
27 | ],
28 | module: {
29 | loaders: [{
30 | test: /\.js?$/,
31 | exclude: /node_modules/,
32 | loader: 'babel',
33 | query: {
34 | "presets": ["react", "es2015", "stage-0", "react-hmre"]
35 | }
36 | }, {
37 | test: /\.json?$/,
38 | loader: 'json'
39 | }, {
40 | test: /\.css$/,
41 | loader: 'style!css'
42 | }, {
43 | // for bootstrap
44 | test: /\.woff($|\?)|\.woff2($|\?)|\.ttf($|\?)|\.eot($|\?)|\.svg($|\?)/,
45 | loader: 'url-loader',
46 | }]
47 | }
48 | };
49 |
--------------------------------------------------------------------------------