├── V2.md ├── index.js ├── .npmignore ├── 2017_03_06_13_09_14.gif ├── demo ├── public │ ├── favicon.ico │ ├── manifest.json │ └── index.html ├── src │ ├── index.css │ ├── index.js │ ├── registerServiceWorker.js │ └── App.js ├── config │ ├── jest │ │ ├── fileTransform.js │ │ └── cssTransform.js │ ├── polyfills.js │ ├── paths.js │ ├── env.js │ ├── webpackDevServer.config.js │ ├── webpack.config.dev.js │ └── webpack.config.prod.js ├── .gitignore ├── scripts │ ├── test.js │ ├── start.js │ └── build.js └── package.json ├── .gitignore ├── .editorconfig ├── .babelrc ├── .eslintrc ├── package.json ├── src ├── spring.js ├── component │ ├── index.js │ └── index.native.js └── index.js ├── README.md └── yarn.lock /V2.md: -------------------------------------------------------------------------------- 1 | # V2 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib') 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /test/ 2 | /demo/ 3 | *.gif 4 | *.tgz 5 | .babelrc 6 | /src/ 7 | *.md 8 | -------------------------------------------------------------------------------- /2017_03_06_13_09_14.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yusukeshib/react-pullrefresh/HEAD/2017_03_06_13_09_14.gif -------------------------------------------------------------------------------- /demo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yusukeshib/react-pullrefresh/HEAD/demo/public/favicon.ico -------------------------------------------------------------------------------- /demo/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | overflow: hidden; 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # misc 7 | .DS_Store 8 | npm-debug.log 9 | 10 | # build files 11 | *.tgz 12 | *.log 13 | /lib/ 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ "env" , { 4 | "targets": { 5 | "browsers": ["last 2 versions", "safari >= 7"] 6 | } 7 | }], 8 | "react" 9 | ], 10 | "plugins": [ 11 | "transform-object-rest-spread", 12 | "transform-function-bind" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /demo/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import registerServiceWorker from './registerServiceWorker' 5 | import whyDidYouUpdate from 'why-did-you-update' 6 | 7 | whyDidYouUpdate(React) 8 | ReactDOM.render(, document.getElementById('root')) 9 | registerServiceWorker() 10 | -------------------------------------------------------------------------------- /demo/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /demo/config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | // This is a custom Jest transformer turning file imports into filenames. 6 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 7 | 8 | module.exports = { 9 | process(src, filename) { 10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /demo/config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | 23 | /src/react-pullrefresh/ 24 | -------------------------------------------------------------------------------- /demo/config/polyfills.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (typeof Promise === 'undefined') { 4 | // Rejection tracking prevents a common issue where React gets into an 5 | // inconsistent state due to an error, but it gets swallowed by a Promise, 6 | // and the user has no idea what causes React's erratic future behavior. 7 | require('promise/lib/rejection-tracking').enable(); 8 | window.Promise = require('promise/lib/es6-extensions.js'); 9 | } 10 | 11 | // fetch() polyfill for making API calls. 12 | require('whatwg-fetch'); 13 | 14 | // Object.assign() is commonly used with React. 15 | // It will use the native implementation if it's present and isn't buggy. 16 | Object.assign = require('object-assign'); 17 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "plugins": [ 4 | ], 5 | "parserOptions": { 6 | "ecmaVersion": 6, 7 | "sourceType": "module", 8 | "ecmaFeatures": { 9 | "jsx": true 10 | } 11 | }, 12 | "env": { 13 | "browser": true, 14 | "amd": true, 15 | "es6": true, 16 | "node": true, 17 | "mocha": true 18 | }, 19 | "rules": { 20 | "space-before-function-paren": [1, {"anonymous": "never", "named": "never"}], 21 | "comma-spacing": [1, { "before": false, "after": true }], 22 | "space-infix-ops": 1, 23 | "semi": [1, "never" ], 24 | "comma-dangle": 1, 25 | "quotes": [ 1, "single" ], 26 | "no-undef": 1, 27 | "no-extra-semi": 1, 28 | "no-trailing-spaces": [1, { "skipBlankLines": true }], 29 | "no-unreachable": 1 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /demo/scripts/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'test'; 5 | process.env.NODE_ENV = 'test'; 6 | process.env.PUBLIC_URL = ''; 7 | 8 | // Makes the script crash on unhandled rejections instead of silently 9 | // ignoring them. In the future, promise rejections that are not handled will 10 | // terminate the Node.js process with a non-zero exit code. 11 | process.on('unhandledRejection', err => { 12 | throw err; 13 | }); 14 | 15 | // Ensure environment variables are read. 16 | require('../config/env'); 17 | 18 | const jest = require('jest'); 19 | const argv = process.argv.slice(2); 20 | 21 | // Watch unless on CI or in coverage mode 22 | if (!process.env.CI && argv.indexOf('--coverage') < 0) { 23 | argv.push('--watch'); 24 | } 25 | 26 | 27 | jest.run(argv); 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-pullrefresh", 3 | "version": "2.0.0-alpha.1", 4 | "description": "react component pull to refresh navigation", 5 | "main": "index.js", 6 | "scripts": { 7 | "prepublishOnly": "npm run build", 8 | "build": "babel src --out-dir lib", 9 | "lint:fix": "eslint src --fix" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/yusukeshibata/react-pullrefresh.git" 14 | }, 15 | "keywords": [ 16 | "pull", 17 | "refresh", 18 | "react", 19 | "component" 20 | ], 21 | "author": "Yusuke Shibata (https://github.com/yusukeshibata)", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/yusukeshibata/react-pullrefresh/issues" 25 | }, 26 | "homepage": "https://github.com/yusukeshibata/react-pullrefresh#readme", 27 | "peerDependencies": { 28 | "react": "^16.1.1", 29 | "react-native": "^0.42.3", 30 | "react-native-svg": "^5.1.6" 31 | }, 32 | "devDependencies": { 33 | "babel-cli": "^6.26.0", 34 | "babel-core": "^6.26.0", 35 | "babel-eslint": "^8.0.2", 36 | "babel-plugin-transform-function-bind": "^6.22.0", 37 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 38 | "babel-polyfill": "^6.26.0", 39 | "babel-preset-env": "^1.6.1", 40 | "babel-preset-react": "^6.24.1", 41 | "eslint": "^4.11.0", 42 | "react": "^16.1.1", 43 | "react-dom": "^16.1.1" 44 | }, 45 | "dependencies": { 46 | "event-emitter": "^0.3.5", 47 | "styled-components": "^2.2.3" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /demo/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 28 | 29 | 30 | 33 |
34 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/spring.js: -------------------------------------------------------------------------------- 1 | import EventEmitter from 'event-emitter' 2 | 3 | const sleep = msec => new Promise(resolve => setTimeout(resolve, msec)) 4 | const loop = async promise => { 5 | const proc = async () => await promise() && await proc() 6 | await proc() 7 | } 8 | 9 | export default class Spring { 10 | constructor(tension, friction) { 11 | this._emitter = new EventEmitter() 12 | this._tension = tension 13 | this._friction = friction 14 | this._value = 0 15 | this._endValue = 0 16 | this._loop = false 17 | this._paused = false 18 | } 19 | pause() { 20 | this._paused = true 21 | } 22 | resume() { 23 | this._paused = false 24 | } 25 | set onUpdate(onUpdate) { 26 | this._onUpdate = onUpdate 27 | } 28 | set endValue(value) { 29 | this._endValue = value 30 | this.loop() 31 | } 32 | async to(value) { 33 | this._endValue = value 34 | this.loop() 35 | await this._wait('end') 36 | } 37 | get currentValue() { 38 | return this._value 39 | } 40 | setValue(value) { 41 | if(this._value !== value) { 42 | this._value = value 43 | this._onUpdate(this) 44 | } 45 | } 46 | _emit(type) { 47 | this._emitter.emit(type) 48 | } 49 | async _wait(type) { 50 | await new Promise(resolve => this._emitter.once(type, resolve)) 51 | } 52 | async loop() { 53 | if(this._loop) return 54 | 55 | this._emit('start') 56 | this._loop = true 57 | 58 | await loop(async () => { 59 | await sleep(1000 / 60) 60 | if(this._paused) return true 61 | // TODO: dummy -> use tention,friction 62 | const dv = (this._endValue - this._value) / 5 63 | this.setValue(this._value + dv) 64 | return Math.abs(dv) > 0.2 65 | }) 66 | this.setValue(this._endValue) 67 | 68 | this._loop = false 69 | this._emit('end') 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-pullrefresh 2 | 3 | Pull to reflesh material design component. 4 | react-native is supported. 5 | 6 | ![](/2017_03_06_13_09_14.gif?raw=true) 7 | 8 | #### Demo 9 | 10 | [https://yusukeshibata.github.io/react-pullrefresh/](https://yusukeshibata.github.io/react-pullrefresh/) 11 | 12 | 13 | #### Install 14 | 15 | ```sh 16 | npm install react-pullrefresh 17 | ``` 18 | 19 | #### Usage 20 | 21 | ```javascript 22 | import PullRefresh from 'react-pullrefresh' 23 | 24 | class App extends Component { 25 | // onRefresh function canbe async/sync 26 | async onRefresh() { 27 | await someAsyncFunction() 28 | } 29 | // Without children PullRefresh element observe document.body's scroll 30 | render() { 31 | return ( 32 | 35 | {range(100).map(i => { 36 | return ( 37 |
{i}
38 | ) 39 | })} 40 |
41 | ) 42 | } 43 | } 44 | 45 | export default App 46 | ``` 47 | #### Behaviour difference between v1/v2 48 | 49 | TODO: 50 | 51 | #### Props 52 | 53 | ##### render 54 | 55 | TODO: 56 | 57 | 58 | ##### color 59 | 60 | default: `#787878` 61 | 62 | ##### bgColor 63 | 64 | default: `#ffffff` 65 | 66 | ##### disabled 67 | 68 | disable component 69 | 70 | default: `false` 71 | 72 | ##### zIndex 73 | 74 | specify css z-index. 75 | 76 | default: `undefined` 77 | 78 | ##### onRefresh 79 | 80 | ```javascript 81 | async function onRefresh() { 82 | //...some async function 83 | } 84 | ``` 85 | 86 | ##### style 87 | 88 | container style. 89 | 90 | default: `undefined` 91 | 92 | #### Removed props 93 | 94 | * size 95 | * offset 96 | * max 97 | * waitingComponent 98 | * pullingComponent 99 | * pulledComponent 100 | * supportDesktop 101 | 102 | #### License 103 | 104 | MIT 105 | -------------------------------------------------------------------------------- /demo/config/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const url = require('url'); 6 | 7 | // Make sure any symlinks in the project folder are resolved: 8 | // https://github.com/facebookincubator/create-react-app/issues/637 9 | const appDirectory = fs.realpathSync(process.cwd()); 10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 11 | 12 | const envPublicUrl = process.env.PUBLIC_URL; 13 | 14 | function ensureSlash(path, needsSlash) { 15 | const hasSlash = path.endsWith('/'); 16 | if (hasSlash && !needsSlash) { 17 | return path.substr(path, path.length - 1); 18 | } else if (!hasSlash && needsSlash) { 19 | return `${path}/`; 20 | } else { 21 | return path; 22 | } 23 | } 24 | 25 | const getPublicUrl = appPackageJson => 26 | envPublicUrl || require(appPackageJson).homepage; 27 | 28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 29 | // "public path" at which the app is served. 30 | // Webpack needs to know it to put the right