Compare commits

..

1 Commits

Author SHA1 Message Date
Timur Gibadullin
6e23480b2f Update Horizontal example 2017-08-14 15:46:45 +03:00
36 changed files with 2158 additions and 2821 deletions

1
.gitignore vendored
View File

@ -1,4 +1,3 @@
.DS_Store
*.log
node_modules
*.idea

21
LICENSE
View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2019 Tim Gibadullin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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 NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS 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.

View File

@ -1,8 +1,5 @@
## Sortable list view for react-native
![GitHub license](https://img.shields.io/badge/license-MIT-green.svg)
[![npm](https://img.shields.io/npm/v/react-native-sortable-list.svg?style=flat)](https://npmjs.com/package/react-native-sortable-list)
### Content
- [Demo](#demo)
- [Installation](#installation)
@ -29,28 +26,16 @@ npm i react-native-sortable-list --save
- **order?** (Array) an array of keys from data, the order of keys from the array will be used to initial rows order
- **style?** (Object, Array)
- **contentContainerStyle?** (Object, Array) these styles will be applied to the inner scroll view content container
- **innerContainerStyle?** (Object, Array) these styles will be applied to the inner scroll view content container, excluding the header and footer
- **horizontal?** (boolean) when true, the SortableList's children are arranged horizontally in a row instead of vertically in a column. The default value is false.
- **showsVerticalScrollIndicator** (boolean) when false, the vertical scroll indicator will not be visible. The default value is true.
- **showsHorizontalScrollIndicator** (boolean) when false, the horizontal scroll indicator will not be visible. The default value is true.
- **sortingEnabled?** (boolean) when false, rows are not sortable. The default value is true.
- **scrollEnabled?** (boolean) when false, the content does not scrollable. The default value is true.
- **keyboardShouldPersistTaps** (string)<br />
Determines when the keyboard should stay visible after a tap.
- 'never' (the default), tapping outside of the focused text input when the keyboard is up dismisses the keyboard. When this happens, children won't receive the tap.
- 'always', the keyboard will not dismiss automatically, and the scroll view will not catch taps, but children of the scroll view can catch taps.
- 'handled', the keyboard will not dismiss automatically when the tap was handled by a children, (or captured by an ancestor.<br/>
- **manuallyActivateRows?** (bool) whether you intend to use the `toggleRowActive` method to activate a row or use the out of box solution.
- **autoscrollAreaSize?** (number) determines the height for vertical list and the width for horizontal list of the area at the begining and the end of the list that will trigger autoscrolling. Defaults to 60.<br />
- **rowActivationTime?** (number) determines time delay in ms before pressed row becomes active. Defaults to 200 ms.<br />
- **refreshControl?** (element)<br />
A RefreshControl that works the same way as a ScrollView's refreshControl.
- **renderRow** (function)<br />
`({key, index, data, disabled, active}) => renderable`<br />
Takes a row key, row index, data entry from the data source and its statuses disabled, active and should return a renderable component to be rendered as the row. The child component will receive a method called `toggleRowActive` (only if `manuallyActivateRows={true}`) to manually activate the row. Useful if you have multiple touch responders in your view.<br />
- **renderHeader?** (function)<br />
`() => renderable`<br />
Renders returned component at the top of the list.
Takes a row key, row index, data entry from the data source and its statuses disabled, active and should return a renderable component to be rendered as the row.<br />
- **renderFooter?** (function)<br />
`() => renderable`<br />
Renders returned component at the bottom of the list.
@ -61,8 +46,8 @@ Called when rows were reordered, takes an array of rows keys of the next rows or
`(key) => void`<br />
Called when a row was activated (user long tapped).
- **onReleaseRow?** (function)<br />
`(key, currentOrder) => void`<br />
Called when the active row was released. Returns the key and the new list order.
`(key) => void`<br />
Called when the active row was released.
- **onPressRow?** (function)<br />
`(key) => void`<br />
Called when a row was pressed.

View File

@ -1,48 +0,0 @@
[ignore]
; We fork some components by platform
.*/*[.]android.js
; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/\.buckd/
; Ignore unexpected extra "@providesModule"
.*/node_modules/.*/node_modules/fbjs/.*
; Ignore duplicate module providers
; For RN Apps installed via npm, "Libraries" folder is inside
; "node_modules/react-native" but in the source repo it is in the root
.*/Libraries/react-native/React.js
; Ignore polyfills
.*/Libraries/polyfills/.*
[include]
[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow/
[options]
emoji=true
module.system=haste
munge_underscores=true
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_type=$FixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
unsafe.enable_getters_and_setters=true
[version]
^0.56.0

View File

@ -46,8 +46,8 @@ buck-out/
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
*/fastlane/report.xml
*/fastlane/Preview.html
*/fastlane/screenshots
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots

View File

@ -1,226 +0,0 @@
/**
* Sample React Native App
* httpss://github.com/facebook/react-native
* @flow
*/
import React, { Component } from 'react';
import {
Animated,
Easing,
StyleSheet,
Text,
Image,
View,
Dimensions,
Platform,
} from 'react-native';
import SortableList from 'react-native-sortable-list';
const window = Dimensions.get('window');
const data = {
0: {
image: 'https://placekitten.com/200/240',
text: 'Chloe',
},
1: {
image: 'https://placekitten.com/200/201',
text: 'Jasper',
},
2: {
image: 'https://placekitten.com/200/202',
text: 'Pepper',
},
3: {
image: 'https://placekitten.com/200/203',
text: 'Oscar',
},
4: {
image: 'https://placekitten.com/200/204',
text: 'Dusty',
},
5: {
image: 'https://placekitten.com/200/205',
text: 'Spooky',
},
6: {
image: 'https://placekitten.com/200/210',
text: 'Kiki',
},
7: {
image: 'https://placekitten.com/200/215',
text: 'Smokey',
},
8: {
image: 'https://placekitten.com/200/220',
text: 'Gizmo',
},
9: {
image: 'https://placekitten.com/220/239',
text: 'Kitty',
},
};
export default class Basic extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.title}>React Native Sortable List</Text>
<SortableList
style={styles.list}
contentContainerStyle={styles.contentContainer}
data={data}
renderRow={this._renderRow} />
</View>
);
}
_renderRow = ({data, active}) => {
return <Row data={data} active={active} />
}
}
class Row extends Component {
constructor(props) {
super(props);
this._active = new Animated.Value(0);
this._style = {
...Platform.select({
ios: {
transform: [{
scale: this._active.interpolate({
inputRange: [0, 1],
outputRange: [1, 1.1],
}),
}],
shadowRadius: this._active.interpolate({
inputRange: [0, 1],
outputRange: [2, 10],
}),
},
android: {
transform: [{
scale: this._active.interpolate({
inputRange: [0, 1],
outputRange: [1, 1.07],
}),
}],
elevation: this._active.interpolate({
inputRange: [0, 1],
outputRange: [2, 6],
}),
},
})
};
}
componentWillReceiveProps(nextProps) {
if (this.props.active !== nextProps.active) {
Animated.timing(this._active, {
duration: 300,
easing: Easing.bounce,
toValue: Number(nextProps.active),
}).start();
}
}
render() {
const {data, active} = this.props;
return (
<Animated.View style={[
styles.row,
this._style,
]}>
<Image source={{uri: data.image}} style={styles.image} />
<Text style={styles.text}>{data.text}</Text>
</Animated.View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#eee',
...Platform.select({
ios: {
paddingTop: 20,
},
}),
},
title: {
fontSize: 20,
paddingVertical: 20,
color: '#999999',
},
list: {
flex: 1,
},
contentContainer: {
width: window.width,
...Platform.select({
ios: {
paddingHorizontal: 30,
},
android: {
paddingHorizontal: 0,
}
})
},
row: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#fff',
padding: 16,
height: 80,
flex: 1,
marginTop: 7,
marginBottom: 12,
borderRadius: 4,
...Platform.select({
ios: {
width: window.width - 30 * 2,
shadowColor: 'rgba(0,0,0,0.2)',
shadowOpacity: 1,
shadowOffset: {height: 2, width: 2},
shadowRadius: 2,
},
android: {
width: window.width - 30 * 2,
elevation: 0,
marginHorizontal: 30,
},
})
},
image: {
width: 50,
height: 50,
marginRight: 30,
borderRadius: 25,
},
text: {
fontSize: 24,
color: '#222222',
},
});

View File

@ -0,0 +1,12 @@
import 'react-native';
import React from 'react';
import Index from '../index.android.js';
// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';
it('renders correctly', () => {
const tree = renderer.create(
<Index />
);
});

View File

@ -1,12 +1,12 @@
import 'react-native';
import React from 'react';
import App from '../App';
import Index from '../index.ios.js';
// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';
it('renders correctly', () => {
const tree = renderer.create(
<App />
<Index />
);
});

View File

@ -72,10 +72,6 @@ import com.android.build.OutputFile
* ]
*/
project.ext.react = [
entryFile: "index.js"
]
apply from: "../../node_modules/react-native/react.gradle"
/**

View File

@ -25,11 +25,6 @@ public class MainApplication extends Application implements ReactApplication {
new MainReactPackage()
);
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override

View File

@ -0,0 +1 @@
import './index.js';

View File

@ -0,0 +1 @@
import './index.js';

View File

@ -1,4 +1,229 @@
import { AppRegistry } from 'react-native';
import App from './App';
/**
* Sample React Native App
* httpss://github.com/facebook/react-native
* @flow
*/
AppRegistry.registerComponent('Basic', () => App);
import React, { Component } from 'react';
import {
Animated,
Easing,
AppRegistry,
StyleSheet,
Text,
Image,
View,
Dimensions,
Platform,
} from 'react-native';
import SortableList from 'react-native-sortable-list';
const window = Dimensions.get('window');
const data = {
0: {
image: 'https://placekitten.com/200/240',
text: 'Chloe',
},
1: {
image: 'https://placekitten.com/200/201',
text: 'Jasper',
},
2: {
image: 'https://placekitten.com/200/202',
text: 'Pepper',
},
3: {
image: 'https://placekitten.com/200/203',
text: 'Oscar',
},
4: {
image: 'https://placekitten.com/200/204',
text: 'Dusty',
},
5: {
image: 'https://placekitten.com/200/205',
text: 'Spooky',
},
6: {
image: 'https://placekitten.com/200/210',
text: 'Kiki',
},
7: {
image: 'https://placekitten.com/200/215',
text: 'Smokey',
},
8: {
image: 'https://placekitten.com/200/220',
text: 'Gizmo',
},
9: {
image: 'https://placekitten.com/220/239',
text: 'Kitty',
},
};
class Basic extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.title}>React Native Sortable List</Text>
<SortableList
style={styles.list}
contentContainerStyle={styles.contentContainer}
data={data}
renderRow={this._renderRow} />
</View>
);
}
_renderRow = ({data, active}) => {
return <Row data={data} active={active} />
}
}
class Row extends Component {
constructor(props) {
super(props);
this._active = new Animated.Value(0);
this._style = {
...Platform.select({
ios: {
transform: [{
scale: this._active.interpolate({
inputRange: [0, 1],
outputRange: [1, 1.1],
}),
}],
shadowRadius: this._active.interpolate({
inputRange: [0, 1],
outputRange: [2, 10],
}),
},
android: {
transform: [{
scale: this._active.interpolate({
inputRange: [0, 1],
outputRange: [1, 1.07],
}),
}],
elevation: this._active.interpolate({
inputRange: [0, 1],
outputRange: [2, 6],
}),
},
})
};
}
componentWillReceiveProps(nextProps) {
if (this.props.active !== nextProps.active) {
Animated.timing(this._active, {
duration: 300,
easing: Easing.bounce,
toValue: Number(nextProps.active),
}).start();
}
}
render() {
const {data, active} = this.props;
return (
<Animated.View style={[
styles.row,
this._style,
]}>
<Image source={{uri: data.image}} style={styles.image} />
<Text style={styles.text}>{data.text}</Text>
</Animated.View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#eee',
...Platform.select({
ios: {
paddingTop: 20,
},
}),
},
title: {
fontSize: 20,
paddingVertical: 20,
color: '#999999',
},
list: {
flex: 1,
},
contentContainer: {
width: window.width,
...Platform.select({
ios: {
paddingHorizontal: 30,
},
android: {
paddingHorizontal: 0,
}
})
},
row: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#fff',
padding: 16,
height: 80,
flex: 1,
marginTop: 7,
marginBottom: 12,
borderRadius: 4,
...Platform.select({
ios: {
width: window.width - 30 * 2,
shadowColor: 'rgba(0,0,0,0.2)',
shadowOpacity: 1,
shadowOffset: {height: 2, width: 2},
shadowRadius: 2,
},
android: {
width: window.width - 30 * 2,
elevation: 0,
marginHorizontal: 30,
},
})
},
image: {
width: 50,
height: 50,
marginRight: 30,
borderRadius: 25,
},
text: {
fontSize: 24,
color: '#222222',
},
});
AppRegistry.registerComponent('Basic', () => Basic);

View File

@ -36,7 +36,6 @@
2DCD954D1E0B4F2C00145EB5 /* BasicTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* BasicTests.m */; };
5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; };
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
ADBDB9381DFEBF1600ED6528 /* libRCTBlob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -229,13 +228,6 @@
remoteGlobalIDString = 58B5119B1A9E6C1200147676;
remoteInfo = RCTText;
};
ADBDB9261DFEBF0700ED6528 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 358F4ED71D1E81A9004DF814;
remoteInfo = RCTBlob;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
@ -263,7 +255,6 @@
5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = "<group>"; };
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTBlob.xcodeproj; path = "../node_modules/react-native/Libraries/Blob/RCTBlob.xcodeproj"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -279,8 +270,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
ADBDB9381DFEBF1600ED6528 /* libRCTBlob.a in Frameworks */,
5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */,
146834051AC3E58100842450 /* libReact.a in Frameworks */,
5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */,
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */,
@ -422,7 +411,6 @@
3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */,
3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */,
3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */,
3DAD3EA31DF850E9000B6D8A /* libReact-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
@ -451,7 +439,6 @@
5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */,
146833FF1AC3E56700842450 /* React.xcodeproj */,
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */,
ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */,
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */,
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */,
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */,
@ -484,7 +471,6 @@
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
usesTabs = 0;
};
83CBBA001A601CBA00E9B192 /* Products */ = {
isa = PBXGroup;
@ -497,14 +483,6 @@
name = Products;
sourceTree = "<group>";
};
ADBDB9201DFEBF0600ED6528 /* Products */ = {
isa = PBXGroup;
children = (
ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -624,10 +602,6 @@
ProductGroup = 5E91572E1DD0AC6500FF2AA8 /* Products */;
ProjectRef = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */;
},
{
ProductGroup = ADBDB9201DFEBF0600ED6528 /* Products */;
ProjectRef = ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */;
},
{
ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */;
ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */;
@ -774,10 +748,10 @@
remoteRef = 3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
3DAD3EA31DF850E9000B6D8A /* libReact-tvOS.a */ = {
3DAD3EA31DF850E9000B6D8A /* libReact.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libReact-tvOS.a";
path = libReact.a;
remoteRef = 3DAD3EA21DF850E9000B6D8A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
@ -851,13 +825,6 @@
remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTBlob.a;
remoteRef = ADBDB9261DFEBF0700ED6528 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */

View File

@ -18,7 +18,7 @@
{
NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"Basic"

View File

@ -1,6 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -3,12 +3,11 @@
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest"
"start": "node node_modules/react-native/local-cli/cli.js start"
},
"dependencies": {
"react": "16.0.0",
"react-native": "0.50.3",
"react-native-sortable-list": "0.0.17"
"react": "16.0.0-alpha.12",
"react-native": "0.46.3",
"react-native-sortable-list": "../.."
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,48 +0,0 @@
[ignore]
; We fork some components by platform
.*/*[.]android.js
; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/\.buckd/
; Ignore unexpected extra "@providesModule"
.*/node_modules/.*/node_modules/fbjs/.*
; Ignore duplicate module providers
; For RN Apps installed via npm, "Libraries" folder is inside
; "node_modules/react-native" but in the source repo it is in the root
.*/Libraries/react-native/React.js
; Ignore polyfills
.*/Libraries/polyfills/.*
[include]
[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow/
[options]
emoji=true
module.system=haste
munge_underscores=true
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_type=$FixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
unsafe.enable_getters_and_setters=true
[version]
^0.56.0

View File

@ -46,8 +46,8 @@ buck-out/
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
*/fastlane/report.xml
*/fastlane/Preview.html
*/fastlane/screenshots
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots

View File

@ -1,223 +0,0 @@
/**
* Sample React Native App
* httpss://github.com/facebook/react-native
* @flow
*/
import React, { Component } from 'react';
import {
Animated,
Easing,
StyleSheet,
Text,
Image,
View,
Dimensions,
Platform,
} from 'react-native';
import SortableList from 'react-native-sortable-list';
const window = Dimensions.get('window');
const data = {
0: {
image: 'https://placekitten.com/200/240',
text: 'Chloe',
},
1: {
image: 'https://placekitten.com/200/201',
text: 'Jasper',
},
2: {
image: 'https://placekitten.com/200/202',
text: 'Pepper',
},
3: {
image: 'https://placekitten.com/200/203',
text: 'Oscar',
},
4: {
image: 'https://placekitten.com/200/204',
text: 'Dusty',
},
5: {
image: 'https://placekitten.com/200/205',
text: 'Spooky',
},
6: {
image: 'https://placekitten.com/200/210',
text: 'Kiki',
},
7: {
image: 'https://placekitten.com/200/215',
text: 'Smokey',
},
8: {
image: 'https://placekitten.com/200/220',
text: 'Gizmo',
},
9: {
image: 'https://placekitten.com/220/239',
text: 'Kitty',
},
};
export default class Horizontal extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.title}>React Native Sortable List</Text>
<SortableList
horizontal
style={styles.list}
contentContainerStyle={styles.contentContainer}
data={data}
renderRow={this._renderRow} />
</View>
);
}
_renderRow = ({data, active}) => {
return <Row data={data} active={active} />
}
}
class Row extends Component {
constructor(props) {
super(props);
this._active = new Animated.Value(0);
this._style = {
...Platform.select({
ios: {
transform: [{
scale: this._active.interpolate({
inputRange: [0, 1],
outputRange: [1, 1.1],
}),
}],
shadowRadius: this._active.interpolate({
inputRange: [0, 1],
outputRange: [2, 10],
}),
},
android: {
transform: [{
scale: this._active.interpolate({
inputRange: [0, 1],
outputRange: [1, 1.07],
}),
}],
elevation: this._active.interpolate({
inputRange: [0, 1],
outputRange: [2, 6],
}),
},
})
};
}
componentWillReceiveProps(nextProps) {
if (this.props.active !== nextProps.active) {
Animated.timing(this._active, {
duration: 300,
easing: Easing.bounce,
toValue: Number(nextProps.active),
}).start();
}
}
render() {
const {data, active} = this.props;
return (
<Animated.View style={[
styles.row,
this._style,
]}>
<Image source={{uri: data.image}} style={styles.image} />
<Text style={styles.text}>{data.text}</Text>
</Animated.View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#eee',
...Platform.select({
ios: {
paddingTop: 20,
},
}),
},
title: {
fontSize: 20,
paddingVertical: 20,
color: '#999999',
},
list: {
height: 210,
width: window.width,
},
contentContainer: {
...Platform.select({
ios: {
paddingVertical: 30,
},
android: {
paddingVertical: 0,
}
})
},
row: {
flexDirection: 'column',
alignItems: 'center',
backgroundColor: '#fff',
padding: 16,
width: 110,
height: 150,
marginHorizontal: 10,
borderRadius: 4,
...Platform.select({
ios: {
shadowColor: 'rgba(0,0,0,0.2)',
shadowOpacity: 1,
shadowOffset: {height: 2, width: 2},
shadowRadius: 2,
},
android: {
elevation: 0,
marginHorizontal: 30,
},
})
},
image: {
width: 50,
height: 50,
marginBottom: 15,
borderRadius: 25,
},
text: {
fontSize: 18,
color: '#222222',
},
});

View File

@ -0,0 +1,12 @@
import 'react-native';
import React from 'react';
import Index from '../index.android.js';
// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';
it('renders correctly', () => {
const tree = renderer.create(
<Index />
);
});

View File

@ -1,12 +1,12 @@
import 'react-native';
import React from 'react';
import App from '../App';
import Index from '../index.ios.js';
// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';
it('renders correctly', () => {
const tree = renderer.create(
<App />
<Index />
);
});

View File

@ -72,10 +72,6 @@ import com.android.build.OutputFile
* ]
*/
project.ext.react = [
entryFile: "index.js"
]
apply from: "../../node_modules/react-native/react.gradle"
/**

View File

@ -25,11 +25,6 @@ public class MainApplication extends Application implements ReactApplication {
new MainReactPackage()
);
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override

View File

@ -0,0 +1 @@
import './index.js';

View File

@ -0,0 +1 @@
import './index.js';

View File

@ -1,4 +1,240 @@
import { AppRegistry } from 'react-native';
import App from './App';
/**
* Sample React Native App
* httpss://github.com/facebook/react-native
* @flow
*/
AppRegistry.registerComponent('Horizontal', () => App);
import React, { Component } from 'react';
import {
Animated,
Easing,
AppRegistry,
StyleSheet,
Text,
Image,
View,
Dimensions,
Platform,
} from 'react-native';
import SortableList from 'react-native-sortable-list';
const window = Dimensions.get('window');
const data = {
0: {
image: 'https://placekitten.com/200/240',
text: 'Chloe',
},
1: {
image: 'https://placekitten.com/200/201',
text: 'Jasper',
},
2: {
image: 'https://placekitten.com/200/202',
text: 'Pepper',
},
3: {
image: 'https://placekitten.com/200/203',
text: 'Oscar',
},
4: {
image: 'https://placekitten.com/200/204',
text: 'Dusty',
},
5: {
image: 'https://placekitten.com/200/205',
text: 'Spooky',
},
6: {
image: 'https://placekitten.com/200/210',
text: 'Kiki',
},
7: {
image: 'https://placekitten.com/200/215',
text: 'Smokey',
},
8: {
image: 'https://placekitten.com/200/220',
text: 'Gizmo',
},
9: {
image: 'https://placekitten.com/220/239',
text: 'Kitty',
},
};
class Horizontal extends Component {
state = {data};
componentDidMount() {
setTimeout(() => {
this.setState(({data}) => {
const nextData = {...data};
delete nextData[1];
return {data: nextData};
});
}, 3000);
}
render() {
return (
<View style={styles.container}>
<Text style={styles.title}>React Native Sortable List</Text>
<SortableList
horizontal
style={styles.list}
contentContainerStyle={styles.contentContainer}
data={this.state.data}
renderRow={this._renderRow} />
</View>
);
}
_renderRow = ({data, active}) => {
return <Row data={data} active={active} />
}
}
class Row extends Component {
constructor(props) {
super(props);
this._active = new Animated.Value(0);
this._style = {
...Platform.select({
ios: {
transform: [{
scale: this._active.interpolate({
inputRange: [0, 1],
outputRange: [1, 1.1],
}),
}],
shadowRadius: this._active.interpolate({
inputRange: [0, 1],
outputRange: [2, 10],
}),
},
android: {
transform: [{
scale: this._active.interpolate({
inputRange: [0, 1],
outputRange: [1, 1.07],
}),
}],
elevation: this._active.interpolate({
inputRange: [0, 1],
outputRange: [2, 6],
}),
},
})
};
}
componentWillReceiveProps(nextProps) {
if (this.props.active !== nextProps.active) {
Animated.timing(this._active, {
duration: 300,
easing: Easing.bounce,
toValue: Number(nextProps.active),
}).start();
}
}
render() {
const {data, active} = this.props;
return (
<Animated.View style={[
styles.row,
this._style,
]}>
<Image source={{uri: data.image}} style={styles.image} />
<Text style={styles.text}>{data.text}</Text>
</Animated.View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#eee',
...Platform.select({
ios: {
paddingTop: 20,
},
}),
},
title: {
fontSize: 20,
paddingVertical: 20,
color: '#999999',
},
list: {
height: 210,
width: window.width,
},
contentContainer: {
...Platform.select({
ios: {
paddingVertical: 30,
},
android: {
paddingVertical: 0,
}
})
},
row: {
flexDirection: 'column',
alignItems: 'center',
backgroundColor: '#fff',
padding: 16,
width: 110,
height: 150,
marginHorizontal: 10,
borderRadius: 4,
...Platform.select({
ios: {
shadowColor: 'rgba(0,0,0,0.2)',
shadowOpacity: 1,
shadowOffset: {height: 2, width: 2},
shadowRadius: 2,
},
android: {
elevation: 0,
marginHorizontal: 30,
},
})
},
image: {
width: 50,
height: 50,
marginBottom: 15,
borderRadius: 25,
},
text: {
fontSize: 18,
color: '#222222',
},
});
AppRegistry.registerComponent('Horizontal', () => Horizontal);

View File

@ -36,7 +36,6 @@
2DCD954D1E0B4F2C00145EB5 /* HorizontalTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* HorizontalTests.m */; };
5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; };
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
ADBDB9381DFEBF1600ED6528 /* libRCTBlob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -229,13 +228,6 @@
remoteGlobalIDString = 58B5119B1A9E6C1200147676;
remoteInfo = RCTText;
};
ADBDB9261DFEBF0700ED6528 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 358F4ED71D1E81A9004DF814;
remoteInfo = RCTBlob;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
@ -263,7 +255,6 @@
5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = "<group>"; };
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTBlob.xcodeproj; path = "../node_modules/react-native/Libraries/Blob/RCTBlob.xcodeproj"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -279,8 +270,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
ADBDB9381DFEBF1600ED6528 /* libRCTBlob.a in Frameworks */,
5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */,
146834051AC3E58100842450 /* libReact.a in Frameworks */,
5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */,
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */,
@ -422,7 +411,6 @@
3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */,
3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */,
3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */,
3DAD3EA31DF850E9000B6D8A /* libReact-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
@ -451,7 +439,6 @@
5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */,
146833FF1AC3E56700842450 /* React.xcodeproj */,
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */,
ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */,
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */,
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */,
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */,
@ -484,7 +471,6 @@
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
usesTabs = 0;
};
83CBBA001A601CBA00E9B192 /* Products */ = {
isa = PBXGroup;
@ -497,14 +483,6 @@
name = Products;
sourceTree = "<group>";
};
ADBDB9201DFEBF0600ED6528 /* Products */ = {
isa = PBXGroup;
children = (
ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -624,10 +602,6 @@
ProductGroup = 5E91572E1DD0AC6500FF2AA8 /* Products */;
ProjectRef = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */;
},
{
ProductGroup = ADBDB9201DFEBF0600ED6528 /* Products */;
ProjectRef = ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */;
},
{
ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */;
ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */;
@ -774,10 +748,10 @@
remoteRef = 3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
3DAD3EA31DF850E9000B6D8A /* libReact-tvOS.a */ = {
3DAD3EA31DF850E9000B6D8A /* libReact.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libReact-tvOS.a";
path = libReact.a;
remoteRef = 3DAD3EA21DF850E9000B6D8A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
@ -851,13 +825,6 @@
remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTBlob.a;
remoteRef = ADBDB9261DFEBF0700ED6528 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */

View File

@ -18,7 +18,7 @@
{
NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"Horizontal"

View File

@ -1,6 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -1,14 +1,13 @@
{
"name": "Horizontal",
"name": "Horizontal",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest"
"start": "node node_modules/react-native/local-cli/cli.js start"
},
"dependencies": {
"react": "16.0.0",
"react-native": "0.50.3",
"react-native-sortable-list": "0.0.17"
"react": "16.0.0-alpha.12",
"react-native": "0.47.1",
"react-native-sortable-list": "0.0.16"
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "react-native-sortable-list",
"version": "0.0.24",
"version": "0.0.16",
"description": "React Native Sortable List component",
"main": "index.js",
"repository": {

View File

@ -1,6 +1,6 @@
import React, {Component, cloneElement} from 'react';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {Animated, PanResponder, StyleSheet, ViewPropTypes} from 'react-native';
import {Animated, PanResponder, StyleSheet} from 'react-native';
import {shallowEqual} from './utils';
export default class Row extends Component {
@ -9,12 +9,11 @@ export default class Row extends Component {
animated: PropTypes.bool,
disabled: PropTypes.bool,
horizontal: PropTypes.bool,
style: ViewPropTypes.style,
style: Animated.View.propTypes.style,
location: PropTypes.shape({
x: PropTypes.number,
y: PropTypes.number,
}),
manuallyActivateRows: PropTypes.bool,
activationTime: PropTypes.number,
// Will be called on long press.
@ -46,14 +45,7 @@ export default class Row extends Component {
_panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => !this._isDisabled(),
onMoveShouldSetPanResponder: (e, gestureState) => {
if (this._isDisabled()) return false;
const vy = Math.abs(gestureState.vy)
const vx = Math.abs(gestureState.vx)
return this._active && (this.props.horizontal ? vx > vy : vy > vx);
},
onMoveShouldSetPanResponder: () => !this._isDisabled(),
onShouldBlockNativeResponder: () => {
// Returns whether this component should block native components from becoming the JS
@ -64,19 +56,16 @@ export default class Row extends Component {
onPanResponderGrant: (e, gestureState) => {
e.persist();
this._target = e.nativeEvent.target;
this._prevGestureState = {
...gestureState,
moveX: gestureState.x0,
moveY: gestureState.y0,
};
if (this.props.manuallyActivateRows) return;
this._wasLongPress = false;
this._longPressTimer = setTimeout(() => {
if (this._active) return;
this._wasLongPress = true;
this._target = e.nativeEvent.target;
this._prevGestureState = {
...gestureState,
moveX: gestureState.x0,
moveY: gestureState.y0,
};
this._toggleActive(e, gestureState);
}, this.props.activationTime);
},
@ -104,13 +93,13 @@ export default class Row extends Component {
},
onPanResponderRelease: (e, gestureState) => {
if (this._active) {
if (this._wasLongPress) {
this._toggleActive(e, gestureState);
} else {
} else if (this._isTouchInsideElement(e)) {
this._cancelLongPress();
if (this._isTouchInsideElement(e) && this.props.onPress) {
if (this.props.onPress) {
this.props.onPress();
}
}
@ -142,11 +131,10 @@ export default class Row extends Component {
},
});
componentDidUpdate() {
const {animated, location} = this.props;
if (!this._active && !shallowEqual(this._location, location)) {
this._relocate(location, !this._active && animated);
componentWillReceiveProps(nextProps) {
if (!this._active && !shallowEqual(this._location, nextProps.location)) {
const animated = !this._active && nextProps.animated;
this._relocate(nextProps.location, animated);
}
}
@ -165,23 +153,14 @@ export default class Row extends Component {
}
render() {
const {children, style, horizontal} = this.props;
const rowStyle = [
style, styles.container, this._animatedLocation.getLayout(),
horizontal ? styles.horizontalContainer : styles.verticalContainer,
];
const {children, style} = this.props;
return (
<Animated.View
{...this._panResponder.panHandlers}
style={rowStyle}
style={[style, styles.container, this._animatedLocation.getLayout()]}
onLayout={this._onLayout}>
{this.props.manuallyActivateRows && children
? cloneElement(children, {
toggleRowActive: this._toggleActive,
})
: children
}
{children}
</Animated.View>
);
}
@ -198,7 +177,6 @@ export default class Row extends Component {
Animated.timing(this._animatedLocation, {
toValue: nextLocation,
duration: 300,
useNativeDriver: false,
}).start(() => {
this._isAnimationRunning = false;
});
@ -207,7 +185,7 @@ export default class Row extends Component {
}
}
_toggleActive = (e, gestureState) => {
_toggleActive(e, gestureState) {
const callback = this._active ? this.props.onRelease : this.props.onActivate;
this._active = !this._active;
@ -215,7 +193,7 @@ export default class Row extends Component {
if (callback) {
callback(e, gestureState, this._location);
}
};
}
_mapGestureToMove(prevGestureState, gestureState) {
return this.props.horizontal
@ -253,12 +231,4 @@ const styles = StyleSheet.create({
container: {
position: 'absolute',
},
horizontalContainer: {
top: 0,
bottom: 0,
},
verticalContainer: {
left: 0,
right: 0,
},
});

View File

@ -1,6 +1,6 @@
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {ScrollView, View, StyleSheet, Platform, RefreshControl, ViewPropTypes} from 'react-native';
import {ScrollView, View, StyleSheet, Platform, RefreshControl} from 'react-native';
import {shallowEqual, swapArrayElements} from './utils';
import Row from './Row';
@ -15,51 +15,29 @@ uniqueRowKey.id = 0
export default class SortableList extends Component {
static propTypes = {
data: PropTypes.oneOfType([PropTypes.array, PropTypes.object]).isRequired,
data: PropTypes.object.isRequired,
order: PropTypes.arrayOf(PropTypes.any),
style: ViewPropTypes.style,
contentContainerStyle: ViewPropTypes.style,
innerContainerStyle: ViewPropTypes.style,
style: View.propTypes.style,
contentContainerStyle: View.propTypes.style,
sortingEnabled: PropTypes.bool,
scrollEnabled: PropTypes.bool,
horizontal: PropTypes.bool,
showsVerticalScrollIndicator: PropTypes.bool,
showsHorizontalScrollIndicator: PropTypes.bool,
refreshControl: PropTypes.element,
autoscrollAreaSize: PropTypes.number,
snapToAlignment: PropTypes.string,
rowActivationTime: PropTypes.number,
manuallyActivateRows: PropTypes.bool,
keyboardShouldPersistTaps: PropTypes.oneOf(['never', 'always', 'handled']),
scrollEventThrottle: PropTypes.number,
decelerationRate: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
pagingEnabled: PropTypes.bool,
nestedScrollEnabled: PropTypes.bool,
disableIntervalMomentum: PropTypes.bool,
renderRow: PropTypes.func.isRequired,
renderHeader: PropTypes.func,
renderFooter: PropTypes.func,
onChangeOrder: PropTypes.func,
onActivateRow: PropTypes.func,
onReleaseRow: PropTypes.func,
onScroll: PropTypes.func,
};
static defaultProps = {
sortingEnabled: true,
scrollEnabled: true,
keyboardShouldPersistTaps: 'never',
autoscrollAreaSize: 60,
snapToAlignment: 'start',
manuallyActivateRows: false,
showsVerticalScrollIndicator: true,
showsHorizontalScrollIndicator: true,
scrollEventThrottle: 2,
decelerationRate: 'normal',
pagingEnabled: false,
onScroll: () => {}
}
/**
@ -88,32 +66,27 @@ export default class SortableList extends Component {
scrollEnabled: this.props.scrollEnabled
};
componentDidMount() {
componentWillMount() {
this.state.order.forEach((key) => {
this._rowsLayouts[key] = new Promise((resolve) => {
this._resolveRowLayout[key] = resolve;
});
});
if (this.props.renderHeader && !this.props.horizontal) {
this._headerLayout = new Promise((resolve) => {
this._resolveHeaderLayout = resolve;
});
}
if (this.props.renderFooter && !this.props.horizontal) {
this._footerLayout = new Promise((resolve) => {
this._resolveFooterLayout = resolve;
});
}
}
componentDidMount() {
this._onUpdateLayouts();
}
componentDidUpdate(prevProps, prevState) {
const {data, order, scrollEnabled} = this.state;
let {data: nextData, order: nextOrder} = this.props;
const {data: prevData} = prevState;
componentWillReceiveProps(nextProps) {
const {data, order} = this.state;
let {data: nextData, order: nextOrder} = nextProps;
if (data && nextData && !shallowEqual(data, nextData)) {
nextOrder = nextOrder || Object.keys(nextData)
@ -124,32 +97,26 @@ export default class SortableList extends Component {
this._resolveRowLayout[key] = resolve;
});
});
if (Object.keys(nextData).length > Object.keys(data).length) {
this.setState({
animated: false,
data: nextData,
containerLayout: null,
rowsLayouts: null,
order: nextOrder
});
} else {
this.setState({
data: nextData,
order: nextOrder
});
}
this.setState({
animated: false,
data: nextData,
containerLayout: null,
rowsLayouts: null,
order: nextOrder
});
} else if (order && nextOrder && !shallowEqual(order, nextOrder)) {
this.setState({order: nextOrder});
}
}
componentDidUpdate(prevProps, prevState) {
const {data} = this.state;
const {data: prevData} = prevState;
if (data && prevData && !shallowEqual(data, prevData)) {
this._onUpdateLayouts();
}
if (prevProps.scrollEnabled !== scrollEnabled) {
this.setState({scrollEnabled: prevProps.scrollEnabled})
}
}
scrollBy({dx = 0, dy = 0, animated = false}) {
@ -204,30 +171,18 @@ export default class SortableList extends Component {
}
render() {
let {
contentContainerStyle,
innerContainerStyle,
horizontal,
style,
showsVerticalScrollIndicator,
showsHorizontalScrollIndicator,
snapToAlignment,
scrollEventThrottle,
decelerationRate,
pagingEnabled,
nestedScrollEnabled,
disableIntervalMomentum,
keyboardShouldPersistTaps,
} = this.props;
const {contentContainerStyle, horizontal, style} = this.props;
const {animated, contentHeight, contentWidth, scrollEnabled} = this.state;
const containerStyle = StyleSheet.flatten([style, {opacity: Number(animated)}])
innerContainerStyle = [
styles.rowsContainer,
horizontal ? {width: contentWidth} : {height: contentHeight},
innerContainerStyle
];
const innerContainerStyle = [styles.rowsContainer];
let {refreshControl} = this.props;
if (horizontal) {
innerContainerStyle.push({width: contentWidth});
} else {
innerContainerStyle.push({height: contentHeight});
}
if (refreshControl && refreshControl.type === RefreshControl) {
refreshControl = React.cloneElement(this.props.refreshControl, {
enabled: scrollEnabled, // fix for Android
@ -237,23 +192,13 @@ export default class SortableList extends Component {
return (
<View style={containerStyle} ref={this._onRefContainer}>
<ScrollView
nestedScrollEnabled={nestedScrollEnabled}
disableIntervalMomentum={disableIntervalMomentum}
refreshControl={refreshControl}
ref={this._onRefScrollView}
horizontal={horizontal}
contentContainerStyle={contentContainerStyle}
scrollEventThrottle={scrollEventThrottle}
pagingEnabled={pagingEnabled}
decelerationRate={decelerationRate}
scrollEventThrottle={2}
scrollEnabled={scrollEnabled}
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
showsHorizontalScrollIndicator={showsHorizontalScrollIndicator}
showsVerticalScrollIndicator={showsVerticalScrollIndicator}
snapToAlignment={snapToAlignment}
onScroll={this._onScroll}
>
{this._renderHeader()}
onScroll={this._onScroll}>
<View style={innerContainerStyle}>
{this._renderRows()}
</View>
@ -267,6 +212,15 @@ export default class SortableList extends Component {
const {horizontal, rowActivationTime, sortingEnabled, renderRow} = this.props;
const {animated, order, data, activeRowKey, releasedRowKey, rowsLayouts} = this.state;
let rowHeight = 0;
let rowWidth = 0;
if (rowsLayouts) {
Object.keys(rowsLayouts).forEach((key) => {
rowHeight = Math.max(rowHeight, rowsLayouts[key].height);
rowWidth = Math.max(rowWidth, rowsLayouts[key].width);
});
}
let nextX = 0;
let nextY = 0;
@ -277,11 +231,13 @@ export default class SortableList extends Component {
if (rowsLayouts) {
if (horizontal) {
style.height = rowHeight;
location.x = nextX;
nextX += rowsLayouts[key] ? rowsLayouts[key].width : 0;
nextX += rowsLayouts[key].width;
} else {
style.width = rowWidth;
location.y = nextY;
nextY += rowsLayouts[key] ? rowsLayouts[key].height : 0;
nextY += rowsLayouts[key].height;
}
}
@ -306,8 +262,7 @@ export default class SortableList extends Component {
onActivate={this._onActivateRow.bind(this, key, index)}
onPress={this._onPressRow.bind(this, key)}
onRelease={this._onReleaseRow.bind(this, key)}
onMove={this._onMoveRow}
manuallyActivateRows={this.props.manuallyActivateRows}>
onMove={this._onMoveRow}>
{renderRow({
key,
data: data[key],
@ -320,20 +275,6 @@ export default class SortableList extends Component {
});
}
_renderHeader() {
if (!this.props.renderHeader || this.props.horizontal) {
return null;
}
const {headerLayout} = this.state;
return (
<View onLayout={!headerLayout ? this._onLayoutHeader : null}>
{this.props.renderHeader()}
</View>
);
}
_renderFooter() {
if (!this.props.renderFooter || this.props.horizontal) {
return null;
@ -349,8 +290,8 @@ export default class SortableList extends Component {
}
_onUpdateLayouts() {
Promise.all([this._headerLayout, this._footerLayout, ...Object.values(this._rowsLayouts)])
.then(([headerLayout, footerLayout, ...rowsLayouts]) => {
Promise.all([this._footerLayout, ...Object.values(this._rowsLayouts)])
.then(([footerLayout, ...rowsLayouts]) => {
// Can get correct containers layout only after rowss layouts.
this._container.measure((x, y, width, height, pageX, pageY) => {
const rowsLayoutsByKey = {};
@ -366,7 +307,6 @@ export default class SortableList extends Component {
this.setState({
containerLayout: {x, y, width, height, pageX, pageY},
rowsLayouts: rowsLayoutsByKey,
headerLayout,
footerLayout,
contentHeight,
contentWidth,
@ -593,10 +533,6 @@ export default class SortableList extends Component {
this._resolveRowLayout[rowKey]({rowKey, layout});
}
_onLayoutHeader = ({nativeEvent: {layout}}) => {
this._resolveHeaderLayout(layout);
};
_onLayoutFooter = ({nativeEvent: {layout}}) => {
this._resolveFooterLayout(layout);
};
@ -632,7 +568,7 @@ export default class SortableList extends Component {
}));
if (this.props.onReleaseRow) {
this.props.onReleaseRow(rowKey, this.state.order);
this.props.onReleaseRow(rowKey);
}
};
@ -654,9 +590,8 @@ export default class SortableList extends Component {
}
};
_onScroll = (e) => {
this._contentOffset = e.nativeEvent.contentOffset;
this.props.onScroll(e)
_onScroll = ({nativeEvent: {contentOffset}}) => {
this._contentOffset = contentOffset;
};
_onRefContainer = (component) => {