diff --git a/Libraries/Experimental/SwipeableRow/SwipeableListView.js b/Libraries/Experimental/SwipeableRow/SwipeableListView.js
new file mode 100644
index 000000000..f026473a7
--- /dev/null
+++ b/Libraries/Experimental/SwipeableRow/SwipeableListView.js
@@ -0,0 +1,115 @@
+/**
+ * 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 SwipeableListView
+ * @flow
+ */
+'use strict';
+
+const ListView = require('ListView');
+const React = require('React');
+const SwipeableListViewDataSource = require('SwipeableListViewDataSource');
+const SwipeableRow = require('SwipeableRow');
+
+const {PropTypes} = React;
+
+/**
+ * A container component that renders multiple SwipeableRow's in a ListView
+ * implementation.
+ */
+const SwipeableListView = React.createClass({
+ statics: {
+ getNewDataSource(): Object {
+ return new SwipeableListViewDataSource({
+ getRowData: (data, sectionID, rowID) => data[rowID],
+ getSectionHeaderData: (data, sectionID) => data[sectionID],
+ sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
+ rowHasChanged: (row1, row2) => row1 !== row2,
+ });
+ },
+ },
+
+ propTypes: {
+ dataSource: PropTypes.object.isRequired, // SwipeableListViewDataSource
+ maxSwipeDistance: PropTypes.number,
+ // Callback method to render the swipeable view
+ renderRow: PropTypes.func.isRequired,
+ // Callback method to render the view that will be unveiled on swipe
+ renderQuickActions: PropTypes.func.isRequired,
+ },
+
+ getDefaultProps(): Object {
+ return {
+ renderQuickActions: () => null,
+ };
+ },
+
+ getInitialState(): Object {
+ return {
+ dataSource: this.props.dataSource.getDataSource(),
+ };
+ },
+
+ componentWillReceiveProps(nextProps: Object): void {
+ if ('dataSource' in nextProps && this.state.dataSource !== nextProps.dataSource) {
+ this.setState({
+ dataSource: nextProps.dataSource.getDataSource(),
+ });
+ }
+ },
+
+ render(): ReactElement {
+ return (
+
+ );
+ },
+
+ _renderRow(rowData: Object, sectionID: string, rowID: string): ReactElement {
+ const slideoutView = this.props.renderQuickActions(rowData, sectionID, rowID);
+
+ // If renderRowSlideout is unspecified or returns falsey, don't allow swipe
+ if (!slideoutView) {
+ return this.props.renderRow(rowData, sectionID, rowID);
+ }
+
+ return (
+ this._onOpen(rowData.id)}>
+ {this.props.renderRow(rowData, sectionID, rowID)}
+
+ );
+ },
+
+ _onOpen(rowID: string): void {
+ this.setState({
+ dataSource: this.props.dataSource.setOpenRowID(rowID),
+ });
+ },
+});
+
+module.exports = SwipeableListView;
diff --git a/Libraries/Experimental/SwipeableRow/SwipeableListViewDataSource.js b/Libraries/Experimental/SwipeableRow/SwipeableListViewDataSource.js
new file mode 100644
index 000000000..8d57f37ef
--- /dev/null
+++ b/Libraries/Experimental/SwipeableRow/SwipeableListViewDataSource.js
@@ -0,0 +1,103 @@
+/**
+ * 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 SwipeableListViewDataSource
+ */
+'use strict';
+
+const ListViewDataSource = require('ListViewDataSource');
+
+/**
+ * Data source wrapper around ListViewDataSource to allow for tracking of
+ * which row is swiped open and close opened row(s) when another row is swiped
+ * open.
+ *
+ * See https://github.com/facebook/react-native/pull/5602 for why
+ * ListViewDataSource is not subclassed.
+ */
+class SwipeableListViewDataSource {
+ _previousOpenRowID: string;
+ _openRowID: string;
+
+ _dataBlob: any;
+ _dataSource: ListViewDataSource;
+
+ rowIdentities: Array>;
+ sectionIdentities: Array;
+
+ constructor(params: Object) {
+ this._dataSource = new ListViewDataSource({
+ getRowData: params.getRowData,
+ getSectionHeaderData: params.getSectionHeaderData,
+ rowHasChanged: (row1, row2) => {
+ /**
+ * Row needs to be re-rendered if its swiped open/close status is
+ * changed, or its data blob changed.
+ */
+ return (
+ (row1.id !== this._previousOpenRowID && row2.id === this._openRowID) ||
+ (row1.id === this._previousOpenRowID && row2.id !== this._openRowID) ||
+ params.rowHasChanged(row1, row2)
+ );
+ },
+ sectionHeaderHasChanged: params.sectionHeaderHasChanged,
+ });
+ }
+
+ cloneWithRowsAndSections(
+ dataBlob: any,
+ sectionIdentities: ?Array,
+ rowIdentities: ?Array>
+ ): SwipeableListViewDataSource {
+ this._dataSource = this._dataSource.cloneWithRowsAndSections(
+ dataBlob,
+ sectionIdentities,
+ rowIdentities
+ );
+
+ this._dataBlob = dataBlob;
+ this.rowIdentities = this._dataSource.rowIdentities;
+ this.sectionIdentities = this._dataSource.sectionIdentities;
+
+ return this;
+ }
+
+ // For the actual ListView to use
+ getDataSource(): ListViewDataSource {
+ return this._dataSource;
+ }
+
+ getOpenRowID(): ?string {
+ return this._openRowID;
+ }
+
+ setOpenRowID(rowID: string): ListViewDataSource {
+ this._previousOpenRowID = this._openRowID;
+ this._openRowID = rowID;
+
+ return this._dataSource.cloneWithRowsAndSections(
+ this._dataBlob,
+ this.sectionIdentities,
+ this.rowIdentities
+ );
+ }
+}
+
+module.exports = SwipeableListViewDataSource;
diff --git a/Libraries/Experimental/SwipeableRow/SwipeableRow.js b/Libraries/Experimental/SwipeableRow/SwipeableRow.js
index 0e409241d..d3c9a5e90 100644
--- a/Libraries/Experimental/SwipeableRow/SwipeableRow.js
+++ b/Libraries/Experimental/SwipeableRow/SwipeableRow.js
@@ -25,6 +25,7 @@
const Animated = require('Animated');
const PanResponder = require('PanResponder');
+const Platform = require('Platform');
const React = require('React');
const StyleSheet = require('StyleSheet');
const View = require('View');
@@ -114,16 +115,20 @@ const SwipeableRow = React.createClass({
},
render(): ReactElement {
+ const slideoutStyle = [
+ styles.slideOutContainer,
+ {
+ right: -this.state.scrollViewWidth,
+ width: this.state.scrollViewWidth,
+ },
+ ];
+ if (Platform.OS === 'ios') {
+ slideoutStyle.push({opacity: this._isSwipeableViewRendered ? 1 : 0});
+ }
+
// The view hidden behind the main view
const slideOutView = (
-
+
{this.props.slideoutView}
);
diff --git a/Libraries/Experimental/SwipeableRow/SwipeableRowListView.js b/Libraries/Experimental/SwipeableRow/SwipeableRowListView.js
deleted file mode 100644
index b9f2c3e03..000000000
--- a/Libraries/Experimental/SwipeableRow/SwipeableRowListView.js
+++ /dev/null
@@ -1,117 +0,0 @@
-/**
- * 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 {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,
- // Callback method to render the view that will be unveiled on swipe
- renderRowSlideout: PropTypes.func.isRequired,
- // Callback method to render the swipeable view
- renderRowSwipeable: 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 (
- this._onRowOpen(rowID)}>
- {this.props.renderRowSwipeable(
- 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;