diff --git a/.gitignore b/.gitignore
index 6d2037a..0b3302f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,6 @@ node_modules
dist
build
lib
+coverage
+ios
+android
diff --git a/HISTORY.md b/HISTORY.md
index 2c663d4..7b1c3c3 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,5 +1,9 @@
# History
+## 1.1.0
+
+- [`new`] react-native support
+
## 1.0.2
- [`fix`] error if this.refs.left/right is []
diff --git a/README.md b/README.md
index 4ed6ecf..cbb8dce 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# rc-swipeout
---
-iOS-style swipeout buttons that appear from behind a component
+iOS-style swipeout buttons that appear from behind a component (web & react-native support)
[![NPM version][npm-image]][npm-url]
[![build status][travis-image]][travis-url]
@@ -25,8 +25,12 @@ iOS-style swipeout buttons that appear from behind a component
## Development
```
+web:
npm install
npm start
+
+rn:
+tnpm run rn-start
```
## Example
diff --git a/examples/reactNative.js b/examples/reactNative.js
new file mode 100644
index 0000000..8b8b95d
--- /dev/null
+++ b/examples/reactNative.js
@@ -0,0 +1,49 @@
+import { View, Text, AppRegistry } from 'react-native';
+import Swipeout from './src';
+import React from 'react';
+
+class SwipeoutExample extends React.Component {
+ render() {
+ return (
+
+ console.log('more'),
+ style: { backgroundColor: 'orange', color: 'white' },
+ },
+ { text: 'delete',
+ onPress: () => console.log('delete'),
+ style: { backgroundColor: 'red', color: 'white' },
+ },
+ ]}
+ left={[
+ {
+ text: 'read',
+ onPress: () => console.log('read'),
+ style: { backgroundColor: 'blue', color: 'white' },
+ },
+ {
+ text: 'reply',
+ onPress: () => console.log('reply'),
+ style: { backgroundColor: 'green', color: 'white' },
+ },
+ ]}
+ onOpen={() => console.log('open')}
+ onClose={() => console.log('close')}
+ >
+ this is Demo
+
+
+ );
+ }
+}
+
+AppRegistry.registerComponent('swipeout', () => SwipeoutExample);
diff --git a/index.js b/index.js
index 60cc4e5..86dbf69 100644
--- a/index.js
+++ b/index.js
@@ -1 +1,2 @@
-module.exports = require('./src/');
\ No newline at end of file
+import Swipeout from './src/';
+export default Swipeout;
\ No newline at end of file
diff --git a/package.json b/package.json
index 4a97a7a..9f76d8c 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "rc-swipeout",
- "version": "1.0.2",
- "description": "swipe out ui component for react",
+ "version": "1.1.0",
+ "description": "swipe out ui component for react(web and react-native)",
"keywords": [
"react",
"react-component",
@@ -23,7 +23,7 @@
"assets/*.css"
],
"licenses": "MIT",
- "main": "./lib/index",
+ "main": "./lib/index.web",
"config": {
"port": 8000
},
@@ -34,22 +34,25 @@
"pub": "rc-tools run pub",
"lint": "rc-tools run lint",
"karma": "rc-tools run karma",
- "saucelabs": "rc-tools run saucelabs",
"test": "rc-tools run test",
"chrome-test": "rc-tools run chrome-test",
- "coverage": "rc-tools run coverage"
+ "coverage": "rc-tools run coverage",
+ "rn-start": "node node_modules/react-native/local-cli/cli.js start"
},
"dependencies": {
- "react-hammerjs": "^0.4.6"
+ "object.omit": "~2.0.0",
+ "react-hammerjs": "^0.5.0",
+ "react-native-swipeout": "https://github.com/dancormier/react-native-swipeout.git#master"
},
"devDependencies": {
"expect.js": "0.3.x",
"hammer-simulator": "0.0.1",
"pre-commit": "1.x",
"rc-tools": "5.x",
- "react": "0.14.x",
- "react-addons-test-utils": "0.14.x",
- "react-dom": "0.14.x"
+ "react": "~15.2.1",
+ "react-addons-test-utils": "~15.2.1",
+ "react-dom": "~15.2.1",
+ "react-native": "0.28.0"
},
"pre-commit": [
"lint"
diff --git a/src/Swipeout.js b/src/Swipeout.js
index 0a85fa2..bb8d447 100644
--- a/src/Swipeout.js
+++ b/src/Swipeout.js
@@ -1,5 +1,7 @@
import React, { PropTypes } from 'react';
-import Hammer from 'react-hammerjs';
+import { View, Text } from 'react-native';
+import Swipe from 'react-native-swipeout';
+import splitObject from './util/splitObject';
class Swipeout extends React.Component {
static propTypes = {
@@ -9,216 +11,77 @@ class Swipeout extends React.Component {
left: PropTypes.arrayOf(PropTypes.object),
right: PropTypes.arrayOf(PropTypes.object),
onOpen: PropTypes.func,
- onClose: PropTypes.func,
children: PropTypes.any,
- }
+ };
static defaultProps = {
- prefixCls: 'rc-swipeout',
autoClose: false,
disabled: false,
left: [],
right: [],
onOpen() {},
- onClose() {},
- }
+ };
constructor(props) {
super(props);
-
- this.onPanStart = this.onPanStart.bind(this);
- this.onPan = this.onPan.bind(this);
- this.onPanEnd = this.onPanEnd.bind(this);
- this.onTap = this.onTap.bind(this);
-
- this.openedLeft = false;
- this.openedRight = false;
- this.disabledPan = false;
- }
-
- componentDidMount() {
- const { left, right } = this.props;
- const width = this.refs.content.offsetWidth;
-
- this.contentWidth = width;
- this.btnsLeftWidth = left ? (width / 5) * left.length : 0;
- this.btnsRightWidth = right ? (width / 5) * right.length : 0;
- }
-
- onPanStart(e) {
- // cannot set direction by react-harmmerjs, fix left & right direction temporarily
- // wait react-harmmerjs pr #46 to merge
- const { left, right } = this.props;
- const aev = e.additionalEvent;
- if (aev === 'panright' && !left.length) {
- this.disabledPan = true;
- } else if (aev === 'panleft' && !right.length) {
- this.disabledPan = true;
- } else {
- this.disabledPan = false;
- }
-
- if (this.props.disabled || this.disabledPan) {
- return;
- }
- this.panStartX = e.deltaX;
- }
-
- onPan(e) {
- if (this.props.disabled || this.disabledPan) {
- return;
- }
-
- // get pan distance
- let posX = e.deltaX - this.panStartX;
- if (this.openedRight) {
- posX = posX - this.btnsRightWidth;
- } else if (this.openedLeft) {
- posX = posX + this.btnsLeftWidth;
- }
-
- if (posX < 0 && this.props.right) {
- this._setStyle(Math.min(posX, 0));
- } else if (posX > 0 && this.props.left) {
- this._setStyle(Math.max(posX, 0));
- }
- }
-
- onPanEnd(e) {
- if (this.props.disabled || this.disabledPan) {
- return;
- }
-
- const posX = e.deltaX - this.panStartX;
- const contentWidth = this.contentWidth;
- const btnsLeftWidth = this.btnsLeftWidth;
- const btnsRightWidth = this.btnsRightWidth;
- const openX = contentWidth * 0.33;
- let openLeft = posX > openX || posX > btnsLeftWidth / 2;
- let openRight = posX < -openX || posX < -btnsRightWidth / 2;
-
- if (this.openedRight) {
- openRight = posX - openX < -openX;
- }
- if (this.openedLeft) {
- openLeft = posX + openX > openX;
- }
-
- if (openRight && posX < 0) {
- this.open(-btnsRightWidth, false, true);
- } else if (openLeft && posX > 0) {
- this.open(btnsLeftWidth, true, false);
- } else {
- this.close();
- }
- }
-
- onTap() {
- if (this.openedLeft || this.openedRight) {
- this.close();
- }
- }
-
- // left & right button click
- onBtnClick(btn) {
- const onPress = btn.onPress;
- if (onPress) {
- onPress();
- }
- if (this.props.autoClose) {
- this.close();
- }
- }
-
- _getContentEasing(value, limit) {
- // limit content style left when value > actions width
- if (value < 0 && value < limit) {
- return limit - Math.pow(limit - value, 0.85);
- } else if (value > 0 && value > limit) {
- return limit + Math.pow(value - limit, 0.85);
- }
- return value;
- }
-
- // set content & actions style
- _setStyle(value) {
- const { left, right } = this.props;
- const limit = value > 0 ? this.btnsLeftWidth : -this.btnsRightWidth;
- const contentLeft = this._getContentEasing(value, limit);
- this.refs.content.style.left = `${contentLeft}px`;
- if (left.length) {
- const leftWidth = Math.max(Math.min(value, Math.abs(limit)), 0);
- this.refs.left.style.width = `${leftWidth}px`;
- }
- if (right.length) {
- const rightWidth = Math.max(Math.min(-value, Math.abs(limit)), 0);
- this.refs.right.style.width = `${rightWidth}px`;
- }
+ this.state = {
+ show: false,
+ paddingTop: 0,
+ };
}
- open(value, openedLeft, openedRight) {
- const onOpen = this.props.onOpen;
- if (onOpen && !this.openedLeft && !this.openedRight) {
- onOpen();
- }
-
- this.openedLeft = openedLeft;
- this.openedRight = openedRight;
- this._setStyle(value);
- }
-
- close() {
- const onClose = this.props.onClose;
- if (onClose && (this.openedLeft || this.openedRight)) {
- onClose();
- }
- this.openedLeft = false;
- this.openedRight = false;
- this._setStyle(0);
- }
-
- renderButtons(buttons, ref) {
- const prefixCls = this.props.prefixCls;
-
- return (buttons && buttons.length) ? (
-
- {buttons.map((btn, i) => {
- return (
-
this.onBtnClick(btn)}
- >
-
{btn.text || 'Click'}
-
- );
- })}
-
- ) : null;
+ renderCustomButton(button) {
+ const buttonStyle = button.style || {};
+ const bgColor = buttonStyle.backgroundColor || 'transparent';
+ const Component = (
+
+
+ {button.text}
+
+
+ );
+ return {
+ text: button.text || 'Click',
+ onPress: button.onPress,
+ type: 'default',
+ component: Component,
+ backgroundColor: 'transparent',
+ color: '#999',
+ disabled: false,
+ };
}
render() {
- const { prefixCls, left, right, children, ...others } = this.props;
-
- return (left.length || right.length) ? (
-
-
-
- {children}
-
-
+ const [{ disabled, autoClose, style, left, right, onOpen, children }, restProps] = splitObject(
+ this.props,
+ ['disabled', 'autoClose', 'style', 'left', 'right', 'onOpen', 'children']
+ );
- { this.renderButtons(left, 'left') }
- { this.renderButtons(right, 'right') }
-
+ const cutsomLeft = left.map(btn => {
+ return this.renderCustomButton(btn);
+ });
+ const cutsomRight = right.map(btn => {
+ return this.renderCustomButton(btn);
+ });
+
+ return (left.length || right.length) && !disabled ? (
+
+ {children}
+
) : (
- {children}
+
+ {children}
+
);
}
}
diff --git a/src/Swipeout.web.js b/src/Swipeout.web.js
new file mode 100644
index 0000000..498b6fc
--- /dev/null
+++ b/src/Swipeout.web.js
@@ -0,0 +1,244 @@
+import React, { PropTypes } from 'react';
+import Hammer from 'react-hammerjs';
+import omit from 'object.omit';
+import splitObject from './util/splitObject';
+
+class Swipeout extends React.Component {
+ static propTypes = {
+ prefixCls: PropTypes.string,
+ autoClose: PropTypes.bool,
+ disabled: PropTypes.bool,
+ left: PropTypes.arrayOf(PropTypes.object),
+ right: PropTypes.arrayOf(PropTypes.object),
+ onOpen: PropTypes.func,
+ onClose: PropTypes.func,
+ children: PropTypes.any,
+ };
+
+ static defaultProps = {
+ prefixCls: 'rc-swipeout',
+ autoClose: false,
+ disabled: false,
+ left: [],
+ right: [],
+ onOpen() {},
+ onClose() {},
+ };
+
+ constructor(props) {
+ super(props);
+
+ this.onPanStart = this.onPanStart.bind(this);
+ this.onPan = this.onPan.bind(this);
+ this.onPanEnd = this.onPanEnd.bind(this);
+ this.onTap = this.onTap.bind(this);
+
+ this.openedLeft = false;
+ this.openedRight = false;
+ this.disabledPan = false;
+ }
+
+ componentDidMount() {
+ const { left, right } = this.props;
+ const width = this.refs.content.offsetWidth;
+
+ this.contentWidth = width;
+ this.btnsLeftWidth = left ? (width / 5) * left.length : 0;
+ this.btnsRightWidth = right ? (width / 5) * right.length : 0;
+ }
+
+ onPanStart(e) {
+ // cannot set direction by react-harmmerjs, fix left & right direction temporarily
+ // wait react-harmmerjs pr #46 to merge
+ const { left, right } = this.props;
+ const aev = e.additionalEvent;
+ if (aev === 'panright' && !left.length) {
+ this.disabledPan = true;
+ } else if (aev === 'panleft' && !right.length) {
+ this.disabledPan = true;
+ } else {
+ this.disabledPan = false;
+ }
+
+ if (this.props.disabled || this.disabledPan) {
+ return;
+ }
+ this.panStartX = e.deltaX;
+ }
+
+ onPan(e) {
+ if (this.props.disabled || this.disabledPan) {
+ return;
+ }
+
+ // get pan distance
+ let posX = e.deltaX - this.panStartX;
+ if (this.openedRight) {
+ posX = posX - this.btnsRightWidth;
+ } else if (this.openedLeft) {
+ posX = posX + this.btnsLeftWidth;
+ }
+
+ if (posX < 0 && this.props.right) {
+ this._setStyle(Math.min(posX, 0));
+ } else if (posX > 0 && this.props.left) {
+ this._setStyle(Math.max(posX, 0));
+ }
+ }
+
+ onPanEnd(e) {
+ if (this.props.disabled || this.disabledPan) {
+ return;
+ }
+
+ const posX = e.deltaX - this.panStartX;
+ const contentWidth = this.contentWidth;
+ const btnsLeftWidth = this.btnsLeftWidth;
+ const btnsRightWidth = this.btnsRightWidth;
+ const openX = contentWidth * 0.33;
+ let openLeft = posX > openX || posX > btnsLeftWidth / 2;
+ let openRight = posX < -openX || posX < -btnsRightWidth / 2;
+
+ if (this.openedRight) {
+ openRight = posX - openX < -openX;
+ }
+ if (this.openedLeft) {
+ openLeft = posX + openX > openX;
+ }
+
+ if (openRight && posX < 0) {
+ this.open(-btnsRightWidth, false, true);
+ } else if (openLeft && posX > 0) {
+ this.open(btnsLeftWidth, true, false);
+ } else {
+ this.close();
+ }
+ }
+
+ onTap() {
+ if (this.openedLeft || this.openedRight) {
+ this.close();
+ }
+ }
+
+ // left & right button click
+ onBtnClick(btn) {
+ const onPress = btn.onPress;
+ if (onPress) {
+ onPress();
+ }
+ if (this.props.autoClose) {
+ this.close();
+ }
+ }
+
+ _getContentEasing(value, limit) {
+ // limit content style left when value > actions width
+ if (value < 0 && value < limit) {
+ return limit - Math.pow(limit - value, 0.85);
+ } else if (value > 0 && value > limit) {
+ return limit + Math.pow(value - limit, 0.85);
+ }
+ return value;
+ }
+
+ // set content & actions style
+ _setStyle(value) {
+ const { left, right } = this.props;
+ const limit = value > 0 ? this.btnsLeftWidth : -this.btnsRightWidth;
+ const contentLeft = this._getContentEasing(value, limit);
+ this.refs.content.style.left = `${contentLeft}px`;
+ if (left.length) {
+ const leftWidth = Math.max(Math.min(value, Math.abs(limit)), 0);
+ this.refs.left.style.width = `${leftWidth}px`;
+ }
+ if (right.length) {
+ const rightWidth = Math.max(Math.min(-value, Math.abs(limit)), 0);
+ this.refs.right.style.width = `${rightWidth}px`;
+ }
+ }
+
+ open(value, openedLeft, openedRight) {
+ const onOpen = this.props.onOpen;
+ if (onOpen && !this.openedLeft && !this.openedRight) {
+ onOpen();
+ }
+
+ this.openedLeft = openedLeft;
+ this.openedRight = openedRight;
+ this._setStyle(value);
+ }
+
+ close() {
+ const onClose = this.props.onClose;
+ if (onClose && (this.openedLeft || this.openedRight)) {
+ onClose();
+ }
+ this.openedLeft = false;
+ this.openedRight = false;
+ this._setStyle(0);
+ }
+
+ renderButtons(buttons, ref) {
+ const prefixCls = this.props.prefixCls;
+
+ return (buttons && buttons.length) ? (
+
+ {buttons.map((btn, i) => {
+ return (
+
this.onBtnClick(btn)}
+ >
+
{btn.text || 'Click'}
+
+ );
+ })}
+
+ ) : null;
+ }
+
+ render() {
+ const [{ prefixCls, left, right, children }, restProps] = splitObject(
+ this.props,
+ ['prefixCls', 'left', 'right', 'children']
+ );
+ const divProps = omit(restProps, [
+ 'disabled',
+ 'autoClose',
+ 'onOpen',
+ 'onClose',
+ ]);
+
+ let direction = 'DIRECTION_HORIZONTAL';
+ if (left.length && right.length === 0) {
+ direction = 'DIRECTION_RIGHT';
+ }
+ if (right.length && left.length === 0) {
+ direction = 'DIRECTION_LEFT';
+ }
+ return (left.length || right.length) ? (
+
+
+
+ {children}
+
+
+
+ { this.renderButtons(left, 'left') }
+ { this.renderButtons(right, 'right') }
+
+ ) : (
+ {children}
+ );
+ }
+}
+
+export default Swipeout;
diff --git a/src/index.js b/src/index.js
index e3543d7..e196e39 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,2 +1 @@
-import Swipeout from './Swipeout';
-export default Swipeout;
+export { default } from './Swipeout';
diff --git a/src/index.web.js b/src/index.web.js
new file mode 100644
index 0000000..e0d152f
--- /dev/null
+++ b/src/index.web.js
@@ -0,0 +1 @@
+export { default } from './Swipeout.web';
diff --git a/src/util/splitObject.js b/src/util/splitObject.js
new file mode 100644
index 0000000..802caec
--- /dev/null
+++ b/src/util/splitObject.js
@@ -0,0 +1,12 @@
+export default function splitObject(obj, parts) {
+ const left = {};
+ const right = {};
+ Object.keys(obj).forEach((k) => {
+ if (parts.indexOf(k) !== -1) {
+ left[k] = obj[k];
+ } else {
+ right[k] = obj[k];
+ }
+ });
+ return [left, right];
+}
diff --git a/tests/index.js b/tests/index.js
index 2e43f47..15cfadb 100644
--- a/tests/index.js
+++ b/tests/index.js
@@ -1,3 +1,2 @@
require('../assets/index.less');
-const req = require.context('.', false, /\.spec\.js$/);
-req.keys().forEach(req);
+import './usage';
diff --git a/tests/usage.spec.js b/tests/usage.js
similarity index 98%
rename from tests/usage.spec.js
rename to tests/usage.js
index c1164ed..d48ae5b 100644
--- a/tests/usage.spec.js
+++ b/tests/usage.js
@@ -1,7 +1,7 @@
const expect = require('expect.js');
-const React = require('react');
-const ReactDOM = require('react-dom');
-const TestUtils = require('react-addons-test-utils');
+import React from 'react';
+import ReactDOM from 'react-dom';
+import TestUtils from 'react-addons-test-utils';
require('hammer-simulator');
const Simulator = window.Simulator;
Simulator.setType('pointer');