diff --git a/Libraries/Experimental/SwipeableRow.js b/Libraries/Experimental/SwipeableRow/SwipeableRow.js
similarity index 95%
rename from Libraries/Experimental/SwipeableRow.js
rename to Libraries/Experimental/SwipeableRow/SwipeableRow.js
index 3f7a0350f..d480a4143 100644
--- a/Libraries/Experimental/SwipeableRow.js
+++ b/Libraries/Experimental/SwipeableRow/SwipeableRow.js
@@ -45,11 +45,13 @@ const SwipeableRow = React.createClass({
_previousLeft: CLOSED_LEFT_POSITION,
propTypes: {
+ isOpen: PropTypes.bool,
/**
* Left position of the maximum open swipe. If unspecified, swipe will open
* fully to the left
*/
maxSwipeDistance: PropTypes.number,
+ onOpen: PropTypes.func,
/**
* A ReactElement that is unveiled when the user swipes
*/
@@ -75,6 +77,7 @@ const SwipeableRow = React.createClass({
getDefaultProps(): Object {
return {
+ isOpen: false,
swipeThreshold: 50,
};
},
@@ -93,6 +96,16 @@ const SwipeableRow = React.createClass({
});
},
+ componentWillReceiveProps(nextProps: Object): void {
+ /**
+ * We do not need an "animateOpen(noCallback)" because this animation is
+ * handled internally by this component.
+ */
+ if (this.props.isOpen && !nextProps.isOpen) {
+ this._animateClose();
+ }
+ },
+
render(): ReactElement {
// The view hidden behind the main view
const slideOutView = (
@@ -260,14 +273,12 @@ const SwipeableRow = React.createClass({
_animateTo(toValue: number): void {
Animated.timing(this.state.currentLeft, {toValue: toValue}).start(() => {
this._previousLeft = toValue;
-
- this.setState({
- currentLeft: new Animated.Value(this._previousLeft),
- });
});
},
_animateOpen(): void {
+ this.props.onOpen && this.props.onOpen();
+
const toValue = this.props.maxSwipeDistance
? -this.props.maxSwipeDistance
: -this.state.scrollViewWidth;
diff --git a/Libraries/Experimental/SwipeableRow/SwipeableRowListView.js b/Libraries/Experimental/SwipeableRow/SwipeableRowListView.js
new file mode 100644
index 000000000..47336dd1b
--- /dev/null
+++ b/Libraries/Experimental/SwipeableRow/SwipeableRowListView.js
@@ -0,0 +1,110 @@
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * The examples provided by Facebook are for non-commercial testing and
+ * evaluation purposes only.
+ *
+ * Facebook reserves all rights not expressly granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
+ * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
+ *
+ * @providesModule SwipeableRowListView
+ * @flow
+ */
+'use strict';
+
+const ListViewDataSource = require('ListViewDataSource');
+const React = require('React');
+const SwipeableRow = require('SwipeableRow');
+const View = require('View');
+
+const {PropTypes} = React;
+
+/**
+ * A container component that renders multiple SwipeableRow's in a provided
+ * ListView implementation and allows a maximum of 1 SwipeableRow to be open at
+ * any given time.
+ */
+const SwipeableRowListView = React.createClass({
+ propTypes: {
+ // Raw data blob for the ListView
+ dataBlob: PropTypes.object.isRequired,
+ /**
+ * Provided implementation of ListView that will be used to render
+ * SwipeableRow elements from dataBlob
+ */
+ listView: PropTypes.func.isRequired,
+ maxSwipeDistance: PropTypes.number,
+ renderRow: PropTypes.func.isRequired,
+ rowIDs: PropTypes.array.isRequired,
+ sectionIDs: PropTypes.array.isRequired,
+ },
+
+ getInitialState(): Object {
+ const ds = new ListViewDataSource({
+ getRowData: (data, sectionID, rowID) => data[rowID],
+ getSectionHeaderData: (data, sectionID) => data[sectionID],
+ rowHasChanged: (row1, row2) => row1 !== row2,
+ sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
+ });
+
+ return {
+ dataSource: ds.cloneWithRowsAndSections(
+ this.props.dataBlob,
+ this.props.sectionIDs,
+ this.props.rowIDs,
+ ),
+ };
+ },
+
+ render(): ReactElement {
+ const CustomListView = this.props.listView;
+
+ return (
+
+ );
+ },
+
+ _renderRow(rowData: Object, sectionID: string, rowID: string): ReactElement {
+ return (
+ }
+ isOpen={rowData.isOpen}
+ maxSwipeDistance={this.props.maxSwipeDistance}
+ key={rowID}
+ onOpen={() => this._onRowOpen(rowID)}>
+ {this.props.renderRow(rowData, sectionID, rowID, this.state.dataSource)}
+
+ );
+ },
+
+ _onRowOpen(rowID: string): void {
+ // Need to recreate dataSource object and not just update existing
+ const blob = JSON.parse(JSON.stringify(this.props.dataBlob));
+ blob[rowID].isOpen = true;
+
+ this.setState({
+ dataSource: this.state.dataSource.cloneWithRowsAndSections(
+ blob,
+ this.props.sectionIDs,
+ this.props.rowIDs,
+ ),
+ });
+ },
+});
+
+module.exports = SwipeableRowListView;