; }
50 |
51 | -dontwarn com.facebook.react.**
52 |
53 | # okhttp
54 |
55 | -keepattributes Signature
56 | -keepattributes *Annotation*
57 | -keep class okhttp3.** { *; }
58 | -keep interface okhttp3.** { *; }
59 | -dontwarn okhttp3.**
60 |
61 | # okio
62 |
63 | -keep class sun.misc.Unsafe { *; }
64 | -dontwarn java.nio.file.*
65 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
66 | -dontwarn okio.**
67 |
--------------------------------------------------------------------------------
/example/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/example/customAnimationExample.js:
--------------------------------------------------------------------------------
1 |
2 | import React, { Component } from 'react'
3 | import {
4 | StyleSheet,
5 | Text,
6 | Animated,
7 | View
8 | } from 'react-native'
9 |
10 | import SortableGrid from 'react-native-sortable-grid'
11 |
12 | export default class basicExample extends Component {
13 |
14 | constructor() {
15 | super()
16 | this.numbers = [0,1,2,3,4,5,6,7,8,9,10,11]
17 | this.state = {
18 | animation: new Animated.Value(0),
19 | }
20 | }
21 |
22 | getColor() {
23 | let r = this.randomRGB()
24 | let g = this.randomRGB()
25 | let b = this.randomRGB()
26 | return 'rgb(' + r + ', ' + g + ', ' + b + ')'
27 | }
28 | randomRGB = () => 160 + Math.random()*85
29 |
30 | startCustomAnimation = () => {
31 | console.log("Custom animation started!")
32 |
33 | Animated.timing(
34 | this.state.animation,
35 | { toValue: 100, duration: 500 }
36 | ).start( () => {
37 |
38 | Animated.timing(
39 | this.state.animation,
40 | { toValue: 0, duration: 500 }
41 | ).start()
42 |
43 | })
44 | }
45 |
46 | getDragStartAnimation = () => {
47 | return { transform: [
48 | {
49 | scaleX: this.state.animation.interpolate({
50 | inputRange: [0, 100],
51 | outputRange: [1, -1.5],
52 | })
53 | },
54 | {
55 | scaleY: this.state.animation.interpolate({
56 | inputRange: [0, 100],
57 | outputRange: [1, 1.5],
58 | })
59 | },
60 | { rotate: this.state.animation.interpolate({
61 | inputRange: [0, 100],
62 | outputRange: ['0 deg', '450 deg']})
63 | }
64 | ]}
65 | }
66 |
67 | render() {
68 | return (
69 |
70 | Custom animation
71 | console.log("Drag was released, the blocks are in the following order: ", itemOrder) }
78 | onDragStart = { this.startCustomAnimation }
79 | ref = { 'SortableGrid' }
80 | >
81 | {
82 | this.numbers.map( (letter, index) =>
83 |
91 | {letter}
92 |
93 | )
94 | }
95 |
96 |
97 | )
98 | }
99 |
100 | }
101 |
102 | const styles = StyleSheet.create({
103 | block: {
104 | flex: 1,
105 | margin: 8,
106 | borderRadius: 20,
107 | justifyContent: 'center',
108 | alignItems: 'center'
109 | }
110 | });
111 |
--------------------------------------------------------------------------------
/example/ios/example/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/example/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
47 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
65 |
66 |
67 |
68 |
78 |
80 |
86 |
87 |
88 |
89 |
90 |
91 |
97 |
99 |
105 |
106 |
107 |
108 |
110 |
111 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/example/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 |
3 | import com.android.build.OutputFile
4 |
5 | /**
6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
7 | * and bundleReleaseJsAndAssets).
8 | * These basically call `react-native bundle` with the correct arguments during the Android build
9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
10 | * bundle directly from the development server. Below you can see all the possible configurations
11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the
12 | * `apply from: "../../node_modules/react-native/react.gradle"` line.
13 | *
14 | * project.ext.react = [
15 | * // the name of the generated asset file containing your JS bundle
16 | * bundleAssetName: "index.android.bundle",
17 | *
18 | * // the entry file for bundle generation
19 | * entryFile: "index.android.js",
20 | *
21 | * // whether to bundle JS and assets in debug mode
22 | * bundleInDebug: false,
23 | *
24 | * // whether to bundle JS and assets in release mode
25 | * bundleInRelease: true,
26 | *
27 | * // whether to bundle JS and assets in another build variant (if configured).
28 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
29 | * // The configuration property can be in the following formats
30 | * // 'bundleIn${productFlavor}${buildType}'
31 | * // 'bundleIn${buildType}'
32 | * // bundleInFreeDebug: true,
33 | * // bundleInPaidRelease: true,
34 | * // bundleInBeta: true,
35 | *
36 | * // the root of your project, i.e. where "package.json" lives
37 | * root: "../../",
38 | *
39 | * // where to put the JS bundle asset in debug mode
40 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
41 | *
42 | * // where to put the JS bundle asset in release mode
43 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release",
44 | *
45 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
46 | * // require('./image.png')), in debug mode
47 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
48 | *
49 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
50 | * // require('./image.png')), in release mode
51 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
52 | *
53 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means
54 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
55 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle
56 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
57 | * // for example, you might want to remove it from here.
58 | * inputExcludes: ["android/**", "ios/**"],
59 | *
60 | * // override which node gets called and with what additional arguments
61 | * nodeExecutableAndArgs: ["node"]
62 | *
63 | * // supply additional arguments to the packager
64 | * extraPackagerArgs: []
65 | * ]
66 | */
67 |
68 | apply from: "../../node_modules/react-native/react.gradle"
69 |
70 | /**
71 | * Set this to true to create two separate APKs instead of one:
72 | * - An APK that only works on ARM devices
73 | * - An APK that only works on x86 devices
74 | * The advantage is the size of the APK is reduced by about 4MB.
75 | * Upload all the APKs to the Play Store and people will download
76 | * the correct one based on the CPU architecture of their device.
77 | */
78 | def enableSeparateBuildPerCPUArchitecture = false
79 |
80 | /**
81 | * Run Proguard to shrink the Java bytecode in release builds.
82 | */
83 | def enableProguardInReleaseBuilds = false
84 |
85 | android {
86 | compileSdkVersion 23
87 | buildToolsVersion "23.0.1"
88 |
89 | defaultConfig {
90 | applicationId "com.example"
91 | minSdkVersion 16
92 | targetSdkVersion 22
93 | versionCode 1
94 | versionName "1.0"
95 | ndk {
96 | abiFilters "armeabi-v7a", "x86"
97 | }
98 | }
99 | splits {
100 | abi {
101 | reset()
102 | enable enableSeparateBuildPerCPUArchitecture
103 | universalApk false // If true, also generate a universal APK
104 | include "armeabi-v7a", "x86"
105 | }
106 | }
107 | buildTypes {
108 | release {
109 | minifyEnabled enableProguardInReleaseBuilds
110 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
111 | }
112 | }
113 | // applicationVariants are e.g. debug, release
114 | applicationVariants.all { variant ->
115 | variant.outputs.each { output ->
116 | // For each separate APK per architecture, set a unique version code as described here:
117 | // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
118 | def versionCodes = ["armeabi-v7a":1, "x86":2]
119 | def abi = output.getFilter(OutputFile.ABI)
120 | if (abi != null) { // null for the universal-debug, universal-release variants
121 | output.versionCodeOverride =
122 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
123 | }
124 | }
125 | }
126 | }
127 |
128 | dependencies {
129 | compile fileTree(dir: "libs", include: ["*.jar"])
130 | compile "com.android.support:appcompat-v7:23.0.1"
131 | compile "com.facebook.react:react-native:+" // From node_modules
132 | }
133 |
134 | // Run this once to be able to run the application with BUCK
135 | // puts all compile dependencies into folder libs for BUCK to use
136 | task copyDownloadableDepsToLibs(type: Copy) {
137 | from configurations.compile
138 | into 'libs'
139 | }
140 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | Drag-and-drop -style rearrangable grid view
4 |
5 |
6 |
7 |
8 |
9 |
10 | # react-native-sortable-grid
11 |
12 | [](https://gitter.im/react-native-sortable-grid/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
13 | []()
14 | [](https://leonidasoy.fi/opensource)
15 | []()
16 | []()
17 | []()
18 |
19 | ## Installation
20 |
21 | ``` npm i react-native-sortable-grid --save ```
22 |
23 | ## Usage
24 |
25 | ```
26 | import SortableGrid from 'react-native-sortable-grid'
27 |
28 | ...
29 |
30 |
31 | {
32 | ['a', 'b', 'c'].map( (letter, index) =>
33 |
34 |
35 | {letter}
36 |
37 |
38 | )
39 | }
40 |
41 |
42 | ```
43 |
44 | ## SortableGrid properties
45 |
46 | - ``` style ``` **Object**
47 |
48 | Custom styles to override or complement the sortableGrid native style.
49 |
50 | * When a row becomes empty of items due to item deletion, the height of the grid is smoothly adjusted to fit the new rows. However, passing ```flex:1``` inside the style prop will cause the grid to fill up the available space and not adjust height when rows become empty.
51 | * User cannot drag items outside of the grid. Assigning ```flex:1``` will expand the grid, therefore giving more space for the items to be dragged in.
52 | * When deleting items from the last row on Android, the items can get clipped. You can workaround this by giving the grid ```bottomPadding```. (This is a known issue with ```overflow```-property on Android)
53 |
54 |
55 | - ``` blockTransitionDuration ``` **Number**
56 |
57 | How long should the transition of a passive block take when the active block takes its place (milliseconds)
58 |
59 | - ``` activeBlockCenteringDuration ``` **Number**
60 |
61 | How long should it take for the block that is being dragged to seek its place after it's released (milliseconds)
62 |
63 | - ``` itemsPerRow ``` **Number**
64 |
65 | How many items should be placed on one row
66 |
67 | - ``` itemWidth ``` **Number**
68 |
69 | If set, itemsPerRow will be calculated to fit items of this size
70 |
71 | - ``` itemHeight ``` **Number**
72 |
73 | When used together with itemsPerRow, sets the size of a block to something other than the default square
74 |
75 | - ``` dragActivationTreshold ``` **Number**
76 |
77 | How long must the user hold the press on the block until it becomes active and can be dragged (milliseconds)
78 |
79 | - ``` doubleTapTreshold ``` **Number**
80 |
81 | How long will the execution wait for the second tap before deciding it was a single tap (milliseconds).
82 | Will be omitted if no onDoubleTap-property is given to the item being tapped - In which case single-tap callback will be executed instantly
83 |
84 | - ``` onDragStart ``` **Callback** *(activeItem)*
85 |
86 | Function that is called when the dragging starts. This can be used to lock other touch responders from listening to the touch such as ScrollViews and Swipers.
87 |
88 | - ``` onDragRelease ``` **Callback** *(itemOrder)*
89 |
90 | Function that is executed after the drag is released. Will return the new item order.
91 |
92 | - ``` onDeleteItem ``` **Callback** *(item)*
93 |
94 | Function that is executed item is deleted. Will return the properties of the deleted item.
95 |
96 | - ``` dragStartAnimation ``` **Object**
97 |
98 | Custom animation to override the default wiggle. Must be an object containing a key ```transform```, which is an array of transformations. Read about [transforms](https://facebook.github.io/react-native/docs/transforms.html) and [animations](https://facebook.github.io/react-native/docs/animated.html) and [see the example](example/customAnimationExample.js#L47) to learn how to use this.
99 |
100 | ## SortableGrid methods
101 |
102 | - ``` toggleDeleteMode ``` accepts no arguments
103 |
104 | Calling this will toggle item deletion mode on/off. Will return object ```{ deleteModeOn: true/false }```.
105 |
106 |
107 | ## SortableGrid's children's properties
108 |
109 | - ``` onTap ``` **Callback**
110 |
111 | Function that is executed when the block is tapped once, but not pressed for long enough to activate the drag.
112 |
113 | - ``` onDoubleTap ``` **Callback**
114 |
115 | Function that is executed when the block is double tapped within a timeframe of ```doubleTapTreshold``` (default 150ms). Assigning this will delay the execution of ```onTap```. Omitting this will cause all taps to be handled as single taps, regardless of their frequency.
116 |
117 | - ``` inactive ``` **Boolean**
118 |
119 | Flag to mark a child node as being inactive. If set, no touch events will be fired when users interact with the node.
120 |
121 | ## onDragRelease return value looks like this:
122 |
123 | ```
124 | Object {
125 |
126 | itemOrder: Array [
127 | 0: Object {
128 | key: "1"
129 | order: 0
130 | ref: null
131 | }
132 | 1: Object {
133 | key: "5"
134 | order: 1
135 | ref: null
136 | }
137 | n: Object ...
138 | ]
139 |
140 | }
141 | ```
142 |
143 | ## Full SortableGrid example:
144 |
145 | ```
146 | console.log("Drag was released, the blocks are in the following order: ", itemOrder) }
152 | onDragStart = { () => console.log("Some block is being dragged now!") } >
153 |
154 | {
155 | ['a', 'b', 'c'].map( (letter, index) =>
156 |
157 | console.log("Item number:", index, "was tapped!") }>
158 | {letter}
159 |
160 |
161 | )
162 | }
163 |
164 |
165 |
166 | ```
167 |
168 | ## Demos
169 |
170 |
171 |
172 | Basic item deletion toggleDeleteMode() is called during onTap in this example
173 |
174 |
175 |
176 | Custom block animation can be passed to the grid
177 |
178 |
179 |
180 | Smooth resizing of the grid when the last row becomes empty:
181 |
182 |
183 |
184 | No grid resizing if the grid has flex:1 assigned:
185 |
186 |
187 |
188 | The item drag is constrained within the grid:
189 |
190 |
191 |
192 | With flex:1 there is more space to drag:
193 |
194 |
195 |
196 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import {
3 | StyleSheet,
4 | Animated,
5 | TouchableWithoutFeedback,
6 | PanResponder,
7 | Image,
8 | View
9 | } from 'react-native'
10 |
11 | import _ from 'lodash'
12 |
13 | // Default values
14 | const ITEMS_PER_ROW = 4
15 | const DRAG_ACTIVATION_TRESHOLD = 200 // Milliseconds
16 | const BLOCK_TRANSITION_DURATION = 300 // Milliseconds
17 | const ACTIVE_BLOCK_CENTERING_DURATION = 200 // Milliseconds
18 | const DOUBLETAP_TRESHOLD = 150 // Milliseconds
19 | const NULL_FN = () => {}
20 |
21 | class Block extends Component {
22 |
23 | render = () =>
24 |
29 | this.props.inactive || this.props.onLongPress() }
33 | onPress = { () => this.props.inactive || this.props.onPress() }>
34 |
35 |
36 |
37 | {this.props.children}
38 |
39 | { this.props.deletionView }
40 |
41 |
42 |
43 |
44 |
45 | }
46 |
47 | class SortableGrid extends Component {
48 |
49 | render = () =>
50 |
54 | { this.state.gridLayout &&
55 | this.items.map( (item, key) =>
56 |
68 | {item}
69 |
70 | )}
71 |
72 |
73 | constructor() {
74 | super()
75 |
76 | this.blockTransitionDuration = BLOCK_TRANSITION_DURATION
77 | this.activeBlockCenteringDuration = ACTIVE_BLOCK_CENTERING_DURATION
78 | this.itemsPerRow = ITEMS_PER_ROW
79 | this.dragActivationTreshold = DRAG_ACTIVATION_TRESHOLD
80 | this.doubleTapTreshold = DOUBLETAP_TRESHOLD
81 | this.onDragRelease = NULL_FN
82 | this.onDragStart = NULL_FN
83 | this.onDeleteItem = NULL_FN
84 | this.dragStartAnimation = null
85 |
86 | this.rows = null
87 | this.dragPosition = null
88 | this.activeBlockOffset = null
89 | this.blockWidth = null
90 | this.blockHeight = null
91 | this.itemWidth = null
92 | this.itemHeight = null
93 | this.gridHeightTarget = null
94 | this.ghostBlocks = []
95 | this.itemOrder = []
96 | this.panCapture = false
97 | this.items = []
98 | this.initialLayoutDone = false
99 | this.initialDragDone = false
100 |
101 | this.tapTimer = null
102 | this.tapIgnore = false
103 | this.doubleTapWait = false
104 |
105 | this.state = {
106 | gridLayout: null,
107 | blockPositions: [],
108 | startDragWiggle: new Animated.Value(0),
109 | activeBlock: null,
110 | blockWidth: null,
111 | blockHeight: null,
112 | gridHeight: new Animated.Value(0),
113 | blockPositionsSetCount: 0,
114 | deleteModeOn: false,
115 | deletionSwipePercent: 0,
116 | deleteBlock: null,
117 | deleteBlockOpacity: new Animated.Value(1),
118 | deletedItems: []
119 | }
120 | }
121 |
122 | toggleDeleteMode = () => {
123 | let deleteModeOn = !this.state.deleteModeOn
124 | this.setState({ deleteModeOn })
125 | return { deleteModeOn }
126 | }
127 |
128 | componentWillMount = () => this.createTouchHandlers()
129 |
130 | componentDidMount = () => this.handleNewProps(this.props)
131 |
132 | componentWillUnmount = () => { if (this.tapTimer) clearTimeout(this.tapTimer) }
133 |
134 | componentWillReceiveProps = (properties) => this.handleNewProps(properties)
135 |
136 | handleNewProps = (properties) => {
137 | this._assignReceivedPropertiesIntoThis(properties)
138 | this._saveItemOrder(properties.children)
139 | this._removeDisappearedChildren(properties.children)
140 | }
141 |
142 | onStartDrag = (evt, gestureState) => {
143 | if (this.state.activeBlock != null) {
144 | let activeBlockPosition = this._getActiveBlock().origin
145 | let x = activeBlockPosition.x - gestureState.x0
146 | let y = activeBlockPosition.y - gestureState.y0
147 | this.activeBlockOffset = { x, y }
148 | this._getActiveBlock().currentPosition.setOffset({ x, y })
149 | this._getActiveBlock().currentPosition.setValue({ x: gestureState.moveX, y: gestureState.moveY })
150 | }
151 | }
152 |
153 | onMoveBlock = (evt, {moveX, moveY, dx, dy}) => {
154 | if (this.state.activeBlock != null && this._blockPositionsSet()) {
155 | if (this.state.deleteModeOn) return this.deleteModeMove({ x: moveX, y: moveY })
156 |
157 | if (dx != 0 || dy != 0) this.initialDragDone = true
158 |
159 | let yChokeAmount = Math.max(0, (this.activeBlockOffset.y + moveY) - (this.state.gridLayout.height - this.blockHeight))
160 | let xChokeAmount = Math.max(0, (this.activeBlockOffset.x + moveX) - (this.state.gridLayout.width - this.blockWidth))
161 | let yMinChokeAmount = Math.min(0, this.activeBlockOffset.y + moveY)
162 | let xMinChokeAmount = Math.min(0, this.activeBlockOffset.x + moveX)
163 |
164 | let dragPosition = { x: moveX - xChokeAmount - xMinChokeAmount, y: moveY - yChokeAmount - yMinChokeAmount }
165 | this.dragPosition = dragPosition
166 | let originalPosition = this._getActiveBlock().origin
167 | let distanceToOrigin = this._getDistanceTo(originalPosition)
168 | this._getActiveBlock().currentPosition.setValue(dragPosition)
169 |
170 | let closest = this.state.activeBlock
171 | let closestDistance = distanceToOrigin
172 | this.state.blockPositions.forEach( (block, index) => {
173 | if (index !== this.state.activeBlock && block.origin) {
174 | let blockPosition = block.origin
175 | let distance = this._getDistanceTo(blockPosition)
176 |
177 | if (distance < closestDistance && distance < this.state.blockWidth) {
178 | closest = index
179 | closestDistance = distance
180 | }
181 | }
182 | })
183 |
184 | this.ghostBlocks.forEach( ghostBlockPosition => {
185 | let distance = this._getDistanceTo(ghostBlockPosition)
186 | if (distance < closestDistance) {
187 | closest = this.state.activeBlock
188 | closestDistance = distance
189 | }
190 | })
191 | if (closest !== this.state.activeBlock) {
192 | Animated.timing(
193 | this._getBlock(closest).currentPosition,
194 | {
195 | toValue: this._getActiveBlock().origin,
196 | duration: this.blockTransitionDuration
197 | }
198 | ).start()
199 | let blockPositions = this.state.blockPositions
200 | this._getActiveBlock().origin = blockPositions[closest].origin
201 | blockPositions[closest].origin = originalPosition
202 | this.setState({ blockPositions })
203 |
204 | var tempOrderIndex = this.itemOrder[this.state.activeBlock].order
205 | this.itemOrder[this.state.activeBlock].order = this.itemOrder[closest].order
206 | this.itemOrder[closest].order = tempOrderIndex
207 | }
208 | }
209 | }
210 |
211 | onReleaseBlock = (evt, gestureState) => {
212 | this.returnBlockToOriginalPosition()
213 | if (this.state.deleteModeOn && this.state.deletionSwipePercent == 100)
214 | this.deleteBlock()
215 | else
216 | this.afterDragRelease()
217 | }
218 |
219 | deleteBlock = () => {
220 | this.setState({ deleteBlock: this.state.activeBlock })
221 | this.blockAnimateFadeOut()
222 | .then( () => {
223 | let activeBlock = this.state.activeBlock
224 | this.setState({ activeBlock: null, deleteBlock: null }, () => {
225 | this.onDeleteItem({ item: this.itemOrder[ activeBlock ] })
226 | this.deleteBlocks([ activeBlock ])
227 | this.afterDragRelease()
228 | })
229 | })
230 | }
231 |
232 | blockAnimateFadeOut = () => {
233 | this.state.deleteBlockOpacity.setValue(1)
234 | return new Promise( (resolve, reject) => {
235 | Animated.timing(
236 | this.state.deleteBlockOpacity,
237 | { toValue: 0, duration: 2 * this.activeBlockCenteringDuration }
238 | ).start(resolve)
239 | })
240 | }
241 |
242 | animateBlockMove = (blockIndex, position) => {
243 | Animated.timing(
244 | this._getBlock(blockIndex).currentPosition,
245 | {
246 | toValue: position,
247 | duration: this.blockTransitionDuration
248 | }
249 | ).start()
250 | }
251 |
252 | returnBlockToOriginalPosition = () => {
253 | let activeBlockCurrentPosition = this._getActiveBlock().currentPosition
254 | activeBlockCurrentPosition.flattenOffset()
255 | Animated.timing(
256 | activeBlockCurrentPosition,
257 | {
258 | toValue: this._getActiveBlock().origin,
259 | duration: this.activeBlockCenteringDuration
260 | }
261 | ).start()
262 | }
263 |
264 | afterDragRelease = () => {
265 | let itemOrder = _.sortBy( this.itemOrder, item => item.order )
266 | this.onDragRelease({ itemOrder })
267 | this.setState({ activeBlock: null })
268 | this.panCapture = false
269 | }
270 |
271 | deleteModeMove = ({x, y}) => {
272 | let slideDistance = 50
273 | let moveY = y + this.activeBlockOffset.y - this._getActiveBlock().origin.y
274 | let adjustY = 0
275 | if (moveY < 0) adjustY = moveY
276 | else if (moveY > slideDistance) adjustY = moveY - slideDistance
277 | let deletionSwipePercent = (moveY - adjustY) / slideDistance * 100
278 | this._getActiveBlock().currentPosition.y.setValue(y - adjustY)
279 | this.setState({deletionSwipePercent})
280 | }
281 |
282 | assessGridSize = ({nativeEvent}) => {
283 | console.log("Calculating grid size");
284 | if (this.props.itemWidth && this.props.itemWidth < nativeEvent.layout.width) {
285 | this.itemsPerRow = Math.floor(nativeEvent.layout.width / this.props.itemWidth)
286 | this.blockWidth = nativeEvent.layout.width / this.itemsPerRow
287 | this.blockHeight = this.props.itemHeight || this.blockWidth
288 | }
289 | else {
290 | this.blockWidth = nativeEvent.layout.width / this.itemsPerRow
291 | this.blockHeight = this.blockWidth
292 | }
293 | if (this.state.gridLayout != nativeEvent.layout) {
294 | this.setState({
295 | gridLayout: nativeEvent.layout,
296 | blockWidth: this.blockWidth,
297 | blockHeight: this.blockHeight
298 | })
299 | }
300 | }
301 |
302 | reAssessGridRows = () => {
303 | let oldRows = this.rows
304 | this.rows = Math.ceil(this.items.length / this.itemsPerRow)
305 | if (this.state.blockWidth && oldRows != this.rows) this._animateGridHeight()
306 | }
307 |
308 | saveBlockPositions = (key) => ({nativeEvent}) => {
309 | let blockPositions = this.state.blockPositions
310 | if (!blockPositions[key]) {
311 | let blockPositionsSetCount = blockPositions[key] ? this.state.blockPositionsSetCount : ++this.state.blockPositionsSetCount
312 | let thisPosition = {
313 | x: nativeEvent.layout.x,
314 | y: nativeEvent.layout.y
315 | }
316 |
317 | blockPositions[key] = {
318 | currentPosition : new Animated.ValueXY( thisPosition ),
319 | origin : thisPosition
320 | }
321 | this.setState({ blockPositions, blockPositionsSetCount })
322 |
323 | if (this._blockPositionsSet()) {
324 | this.setGhostPositions()
325 | this.initialLayoutDone = true
326 | }
327 | }
328 | }
329 |
330 | getNextBlockCoordinates = () => {
331 | let blockWidth = this.state.blockWidth
332 | let blockHeight = this.state.blockHeight
333 | let placeOnRow = this.items.length % this.itemsPerRow
334 | let y = blockHeight * Math.floor(this.items.length / this.itemsPerRow)
335 | let x = placeOnRow * blockWidth
336 | return { x, y }
337 | }
338 |
339 | setGhostPositions = () => {
340 | this.ghostBlocks = []
341 | this.reAssessGridRows()
342 | let blockWidth = this.state.blockWidth
343 | let blockHeight = this.state.blockHeight
344 | let fullGridItemCount = this.rows * this.itemsPerRow
345 | let ghostBlockCount = fullGridItemCount - this.items.length
346 | let y = blockHeight * (this.rows - 1)
347 | let initialX = blockWidth * (this.itemsPerRow - ghostBlockCount)
348 |
349 | for (let i = 0; i < ghostBlockCount; ++i) {
350 | let x = initialX + blockWidth * i
351 | this.ghostBlocks.push({x, y})
352 | }
353 | }
354 |
355 | activateDrag = (key) => () => {
356 | this.panCapture = true
357 | this.onDragStart( this.itemOrder[key] )
358 | this.setState({ activeBlock: key })
359 | this._defaultDragActivationWiggle()
360 | }
361 |
362 | handleTap = ({ onTap = NULL_FN, onDoubleTap = NULL_FN }) => () => {
363 | if (this.tapIgnore) this._resetTapIgnoreTime()
364 | else if (onDoubleTap != null) {
365 | this.doubleTapWait ? this._onDoubleTap(onDoubleTap) : this._onSingleTap(onTap)
366 | } else onTap()
367 | }
368 |
369 | // Helpers & other boring stuff
370 |
371 | _getActiveBlock = () => this.state.blockPositions[ this.state.activeBlock ]
372 |
373 | _getBlock = (blockIndex) => this.state.blockPositions[ blockIndex ]
374 |
375 | _blockPositionsSet = () => this.state.blockPositionsSetCount === this.items.length
376 |
377 | _saveItemOrder = (items) => {
378 | items.forEach( (item, index) => {
379 | const foundKey = _.findKey(this.itemOrder, oldItem => oldItem.key === item.key);
380 |
381 | if (foundKey) {
382 | this.items[foundKey] = item;
383 | }
384 | else {
385 | this.itemOrder.push({ key: item.key, ref: item.ref, order: this.items.length });
386 | if (!this.initialLayoutDone) {
387 | this.items.push(item);
388 | }
389 | else {
390 | let blockPositions = this.state.blockPositions
391 | let blockPositionsSetCount = ++this.state.blockPositionsSetCount
392 | let thisPosition = this.getNextBlockCoordinates()
393 | blockPositions.push({
394 | currentPosition : new Animated.ValueXY( thisPosition ),
395 | origin : thisPosition
396 | })
397 | this.items.push(item)
398 | this.setState({ blockPositions, blockPositionsSetCount })
399 | this.setGhostPositions()
400 | }
401 | }
402 | })
403 | }
404 |
405 | _removeDisappearedChildren = (items) => {
406 | let deleteBlockIndices = []
407 | _.cloneDeep(this.itemOrder).forEach( (item, index) => {
408 | if (!_.findKey(items, (oldItem) => oldItem.key === item.key)) {
409 | deleteBlockIndices.push(index)
410 | }
411 | })
412 | if (deleteBlockIndices.length > 0) {
413 | this.deleteBlocks(deleteBlockIndices)
414 | }
415 | }
416 |
417 | deleteBlocks = (deleteBlockIndices) => {
418 | let blockPositions = this.state.blockPositions
419 | let blockPositionsSetCount = this.state.blockPositionsSetCount
420 | _.sortBy(deleteBlockIndices, index => -index).forEach(index => {
421 | --blockPositionsSetCount
422 | let order = this.itemOrder[index].order
423 | blockPositions.splice(index, 1)
424 | this._fixItemOrderOnDeletion(this.itemOrder[index])
425 | this.itemOrder.splice(index, 1)
426 | this.items.splice(index, 1)
427 | })
428 | this.setState({ blockPositions, blockPositionsSetCount }, () => {
429 | this.items.forEach( (item, order) => {
430 | let blockIndex = _.findIndex(this.itemOrder, item => item.order === order)
431 | let x = (order * this.state.blockWidth) % (this.itemsPerRow * this.state.blockWidth)
432 | let y = Math.floor(order / this.itemsPerRow) * this.state.blockHeight
433 | this.state.blockPositions[blockIndex].origin = {x, y}
434 | this.animateBlockMove(blockIndex, {x, y})
435 | })
436 | this.setGhostPositions()
437 | })
438 | }
439 |
440 | _fixItemOrderOnDeletion = (orderItem) => {
441 | if (!orderItem) return false
442 | orderItem.order--
443 | this._fixItemOrderOnDeletion(_.find(this.itemOrder, item => item.order === orderItem.order + 2))
444 | }
445 |
446 | _animateGridHeight = () => {
447 | this.gridHeightTarget = this.rows * this.state.blockHeight
448 | if (this.gridHeightTarget === this.state.gridLayout.height || this.state.gridLayout.height === 0)
449 | this.state.gridHeight.setValue(this.gridHeightTarget)
450 | else if (this.state.gridHeight._value !== this.gridHeightTarget) {
451 | Animated.timing(
452 | this.state.gridHeight,
453 | {
454 | toValue: this.gridHeightTarget,
455 | duration: this.blockTransitionDuration
456 | }
457 | ).start()
458 | }
459 | }
460 |
461 | _getDistanceTo = (point) => {
462 | let xDistance = this.dragPosition.x + this.activeBlockOffset.x - point.x
463 | let yDistance = this.dragPosition.y + this.activeBlockOffset.y - point.y
464 | return Math.sqrt( Math.pow(xDistance, 2) + Math.pow(yDistance, 2) )
465 | }
466 |
467 | _defaultDragActivationWiggle = () => {
468 | if (!this.dragStartAnimation) {
469 | this.state.startDragWiggle.setValue(20)
470 | Animated.spring(this.state.startDragWiggle, {
471 | toValue: 0,
472 | velocity: 2000,
473 | tension: 2000,
474 | friction: 5
475 | }).start()
476 | }
477 | }
478 |
479 | _blockActivationWiggle = () => {
480 | return this.dragStartAnimation ||
481 | { transform: [{ rotate: this.state.startDragWiggle.interpolate({
482 | inputRange: [0, 360],
483 | outputRange: ['0 deg', '360 deg']})}]}
484 | }
485 |
486 | _assignReceivedPropertiesIntoThis(properties) {
487 | Object.keys(properties).forEach(property => {
488 | if (this[property])
489 | this[property] = properties[property]
490 | })
491 | this.dragStartAnimation = properties.dragStartAnimation
492 | }
493 |
494 | _onSingleTap = (onTap) => {
495 | this.doubleTapWait = true
496 | this.tapTimer = setTimeout( () => {
497 | this.doubleTapWait = false
498 | onTap()
499 | }, this.doubleTapTreshold)
500 | }
501 |
502 | _onDoubleTap = (onDoubleTap) => {
503 | this._resetTapIgnoreTime()
504 | this.doubleTapWait = false
505 | this.tapIgnore = true
506 | onDoubleTap()
507 | }
508 |
509 | _resetTapIgnoreTime = () => {
510 | clearTimeout(this.tapTimer)
511 | this.tapTimer = setTimeout(() => this.tapIgnore = false, this.doubleTapTreshold)
512 | }
513 |
514 | createTouchHandlers = () =>
515 | this._panResponder = PanResponder.create({
516 | onPanResponderTerminate: (evt, gestureState) => {},
517 | onStartShouldSetPanResponder: (evt, gestureState) => true,
518 | onStartShouldSetPanResponderCapture: (evt, gestureState) => false,
519 | onMoveShouldSetPanResponder: (evt, gestureState) => this.panCapture,
520 | onMoveShouldSetPanResponderCapture: (evt, gestureState) => this.panCapture,
521 | onShouldBlockNativeResponder: (evt, gestureState) => false,
522 | onPanResponderTerminationRequest: (evt, gestureState) => false,
523 | onPanResponderGrant: this.onActiveBlockIsSet(this.onStartDrag),
524 | onPanResponderMove: this.onActiveBlockIsSet(this.onMoveBlock),
525 | onPanResponderRelease: this.onActiveBlockIsSet(this.onReleaseBlock)
526 | })
527 |
528 | onActiveBlockIsSet = (fn) => (evt, gestureState) => {
529 | if (this.state.activeBlock != null) fn(evt, gestureState)
530 | }
531 |
532 | // Style getters
533 |
534 | _getGridStyle = () => [
535 | styles.sortableGrid,
536 | this.props.style,
537 | this._blockPositionsSet() && { height: this.state.gridHeight }
538 | ]
539 |
540 | _getDeletionView = (key) => {
541 | if (this.state.deleteModeOn)
542 | return
543 | }
544 |
545 | _getItemWrapperStyle = (key) => [
546 | { flex: 1 },
547 | this.state.activeBlock == key
548 | && this.state.deleteModeOn
549 | && this._getBlock( key ).origin
550 | &&
551 | { opacity: 1.5 - this._getDynamicOpacity(key) }
552 | ]
553 |
554 | _getImageDeleteIconStyle = (key) => [
555 | { position: 'absolute',
556 | top: this.state.blockHeight/2 - 15,
557 | left: this.state.blockWidth/2 - 15,
558 | width: 30,
559 | height: 30,
560 | opacity: .5
561 | },
562 | this.state.activeBlock == key
563 | && this._getBlock( key ).origin
564 | &&
565 | { opacity: .5 + this._getDynamicOpacity(key) }
566 | ]
567 |
568 | _getDynamicOpacity = (key) =>
569 | ( this._getBlock( key ).currentPosition.y._value
570 | + this._getBlock( key ).currentPosition.y._offset
571 | - this._getBlock( key ).origin.y
572 | ) / 50
573 |
574 | _getBlockStyle = (key) => [
575 | { width: this.state.blockWidth,
576 | height: this.state.blockHeight,
577 | justifyContent: 'center' },
578 | this._blockPositionsSet() && (this.initialDragDone || this.state.deleteModeOn) &&
579 | { position: 'absolute',
580 | top: this._getBlock(key).currentPosition.getLayout().top,
581 | left: this._getBlock(key).currentPosition.getLayout().left
582 | },
583 | this.state.activeBlock == key && this._blockActivationWiggle(),
584 | this.state.activeBlock == key && { zIndex: 1 },
585 | this.state.deleteBlock != null && { zIndex: 2 },
586 | this.state.deleteBlock == key && { opacity: this.state.deleteBlockOpacity },
587 | this.state.deletedItems.indexOf(key) !== -1 && styles.deletedBlock
588 | ]
589 | }
590 |
591 | const styles = StyleSheet.create(
592 | {
593 | sortableGrid: {
594 | flexDirection: 'row',
595 | flexWrap: 'wrap'
596 | },
597 | deletedBlock: {
598 | opacity: 0,
599 | position: 'absolute',
600 | left: 0,
601 | top: 0,
602 | height: 0,
603 | width: 0
604 | },
605 | itemImageContainer: {
606 | flex: 1,
607 | justifyContent: 'center'
608 | }
609 | })
610 |
611 | module.exports = SortableGrid
612 |
--------------------------------------------------------------------------------
/example/ios/example.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
11 | 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; };
12 | 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; };
13 | 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; };
14 | 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
15 | 00E356F31AD99517003FC87E /* exampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* exampleTests.m */; };
16 | 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; };
17 | 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; };
18 | 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; };
19 | 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
20 | 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
21 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
22 | 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
23 | 140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
24 | 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
25 | 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
26 | /* End PBXBuildFile section */
27 |
28 | /* Begin PBXContainerItemProxy section */
29 | 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = {
30 | isa = PBXContainerItemProxy;
31 | containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */;
32 | proxyType = 2;
33 | remoteGlobalIDString = 134814201AA4EA6300B7C361;
34 | remoteInfo = RCTActionSheet;
35 | };
36 | 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = {
37 | isa = PBXContainerItemProxy;
38 | containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */;
39 | proxyType = 2;
40 | remoteGlobalIDString = 134814201AA4EA6300B7C361;
41 | remoteInfo = RCTGeolocation;
42 | };
43 | 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = {
44 | isa = PBXContainerItemProxy;
45 | containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
46 | proxyType = 2;
47 | remoteGlobalIDString = 58B5115D1A9E6B3D00147676;
48 | remoteInfo = RCTImage;
49 | };
50 | 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = {
51 | isa = PBXContainerItemProxy;
52 | containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
53 | proxyType = 2;
54 | remoteGlobalIDString = 58B511DB1A9E6C8500147676;
55 | remoteInfo = RCTNetwork;
56 | };
57 | 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = {
58 | isa = PBXContainerItemProxy;
59 | containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */;
60 | proxyType = 2;
61 | remoteGlobalIDString = 832C81801AAF6DEF007FA2F7;
62 | remoteInfo = RCTVibration;
63 | };
64 | 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = {
65 | isa = PBXContainerItemProxy;
66 | containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
67 | proxyType = 1;
68 | remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
69 | remoteInfo = example;
70 | };
71 | 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = {
72 | isa = PBXContainerItemProxy;
73 | containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
74 | proxyType = 2;
75 | remoteGlobalIDString = 134814201AA4EA6300B7C361;
76 | remoteInfo = RCTSettings;
77 | };
78 | 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = {
79 | isa = PBXContainerItemProxy;
80 | containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
81 | proxyType = 2;
82 | remoteGlobalIDString = 3C86DF461ADF2C930047B81A;
83 | remoteInfo = RCTWebSocket;
84 | };
85 | 146834031AC3E56700842450 /* PBXContainerItemProxy */ = {
86 | isa = PBXContainerItemProxy;
87 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
88 | proxyType = 2;
89 | remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192;
90 | remoteInfo = React;
91 | };
92 | 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = {
93 | isa = PBXContainerItemProxy;
94 | containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
95 | proxyType = 2;
96 | remoteGlobalIDString = 134814201AA4EA6300B7C361;
97 | remoteInfo = RCTLinking;
98 | };
99 | 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = {
100 | isa = PBXContainerItemProxy;
101 | containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
102 | proxyType = 2;
103 | remoteGlobalIDString = 58B5119B1A9E6C1200147676;
104 | remoteInfo = RCTText;
105 | };
106 | /* End PBXContainerItemProxy section */
107 |
108 | /* Begin PBXFileReference section */
109 | 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; };
110 | 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = ""; };
111 | 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = ""; };
112 | 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = ""; };
113 | 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = ""; };
114 | 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = ""; };
115 | 00E356EE1AD99517003FC87E /* exampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = exampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
116 | 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
117 | 00E356F21AD99517003FC87E /* exampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = exampleTests.m; sourceTree = ""; };
118 | 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = ""; };
119 | 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = ""; };
120 | 13B07F961A680F5B00A75B9A /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; };
121 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = example/AppDelegate.h; sourceTree = ""; };
122 | 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = example/AppDelegate.m; sourceTree = ""; };
123 | 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; };
124 | 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = example/Images.xcassets; sourceTree = ""; };
125 | 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = example/Info.plist; sourceTree = ""; };
126 | 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = example/main.m; sourceTree = ""; };
127 | 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; };
128 | 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; };
129 | 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; };
130 | /* End PBXFileReference section */
131 |
132 | /* Begin PBXFrameworksBuildPhase section */
133 | 00E356EB1AD99517003FC87E /* Frameworks */ = {
134 | isa = PBXFrameworksBuildPhase;
135 | buildActionMask = 2147483647;
136 | files = (
137 | 140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */,
138 | );
139 | runOnlyForDeploymentPostprocessing = 0;
140 | };
141 | 13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
142 | isa = PBXFrameworksBuildPhase;
143 | buildActionMask = 2147483647;
144 | files = (
145 | 146834051AC3E58100842450 /* libReact.a in Frameworks */,
146 | 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */,
147 | 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */,
148 | 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */,
149 | 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */,
150 | 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */,
151 | 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */,
152 | 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */,
153 | 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */,
154 | 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */,
155 | );
156 | runOnlyForDeploymentPostprocessing = 0;
157 | };
158 | /* End PBXFrameworksBuildPhase section */
159 |
160 | /* Begin PBXGroup section */
161 | 00C302A81ABCB8CE00DB3ED1 /* Products */ = {
162 | isa = PBXGroup;
163 | children = (
164 | 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */,
165 | );
166 | name = Products;
167 | sourceTree = "";
168 | };
169 | 00C302B61ABCB90400DB3ED1 /* Products */ = {
170 | isa = PBXGroup;
171 | children = (
172 | 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */,
173 | );
174 | name = Products;
175 | sourceTree = "";
176 | };
177 | 00C302BC1ABCB91800DB3ED1 /* Products */ = {
178 | isa = PBXGroup;
179 | children = (
180 | 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */,
181 | );
182 | name = Products;
183 | sourceTree = "";
184 | };
185 | 00C302D41ABCB9D200DB3ED1 /* Products */ = {
186 | isa = PBXGroup;
187 | children = (
188 | 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */,
189 | );
190 | name = Products;
191 | sourceTree = "";
192 | };
193 | 00C302E01ABCB9EE00DB3ED1 /* Products */ = {
194 | isa = PBXGroup;
195 | children = (
196 | 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */,
197 | );
198 | name = Products;
199 | sourceTree = "";
200 | };
201 | 00E356EF1AD99517003FC87E /* exampleTests */ = {
202 | isa = PBXGroup;
203 | children = (
204 | 00E356F21AD99517003FC87E /* exampleTests.m */,
205 | 00E356F01AD99517003FC87E /* Supporting Files */,
206 | );
207 | path = exampleTests;
208 | sourceTree = "";
209 | };
210 | 00E356F01AD99517003FC87E /* Supporting Files */ = {
211 | isa = PBXGroup;
212 | children = (
213 | 00E356F11AD99517003FC87E /* Info.plist */,
214 | );
215 | name = "Supporting Files";
216 | sourceTree = "";
217 | };
218 | 139105B71AF99BAD00B5F7CC /* Products */ = {
219 | isa = PBXGroup;
220 | children = (
221 | 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */,
222 | );
223 | name = Products;
224 | sourceTree = "";
225 | };
226 | 139FDEE71B06529A00C62182 /* Products */ = {
227 | isa = PBXGroup;
228 | children = (
229 | 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */,
230 | );
231 | name = Products;
232 | sourceTree = "";
233 | };
234 | 13B07FAE1A68108700A75B9A /* example */ = {
235 | isa = PBXGroup;
236 | children = (
237 | 008F07F21AC5B25A0029DE68 /* main.jsbundle */,
238 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */,
239 | 13B07FB01A68108700A75B9A /* AppDelegate.m */,
240 | 13B07FB51A68108700A75B9A /* Images.xcassets */,
241 | 13B07FB61A68108700A75B9A /* Info.plist */,
242 | 13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
243 | 13B07FB71A68108700A75B9A /* main.m */,
244 | );
245 | name = example;
246 | sourceTree = "";
247 | };
248 | 146834001AC3E56700842450 /* Products */ = {
249 | isa = PBXGroup;
250 | children = (
251 | 146834041AC3E56700842450 /* libReact.a */,
252 | );
253 | name = Products;
254 | sourceTree = "";
255 | };
256 | 78C398B11ACF4ADC00677621 /* Products */ = {
257 | isa = PBXGroup;
258 | children = (
259 | 78C398B91ACF4ADC00677621 /* libRCTLinking.a */,
260 | );
261 | name = Products;
262 | sourceTree = "";
263 | };
264 | 832341AE1AAA6A7D00B99B32 /* Libraries */ = {
265 | isa = PBXGroup;
266 | children = (
267 | 146833FF1AC3E56700842450 /* React.xcodeproj */,
268 | 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */,
269 | 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */,
270 | 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */,
271 | 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */,
272 | 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */,
273 | 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */,
274 | 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */,
275 | 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */,
276 | 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */,
277 | );
278 | name = Libraries;
279 | sourceTree = "";
280 | };
281 | 832341B11AAA6A8300B99B32 /* Products */ = {
282 | isa = PBXGroup;
283 | children = (
284 | 832341B51AAA6A8300B99B32 /* libRCTText.a */,
285 | );
286 | name = Products;
287 | sourceTree = "";
288 | };
289 | 83CBB9F61A601CBA00E9B192 = {
290 | isa = PBXGroup;
291 | children = (
292 | 13B07FAE1A68108700A75B9A /* example */,
293 | 832341AE1AAA6A7D00B99B32 /* Libraries */,
294 | 00E356EF1AD99517003FC87E /* exampleTests */,
295 | 83CBBA001A601CBA00E9B192 /* Products */,
296 | );
297 | indentWidth = 2;
298 | sourceTree = "";
299 | tabWidth = 2;
300 | };
301 | 83CBBA001A601CBA00E9B192 /* Products */ = {
302 | isa = PBXGroup;
303 | children = (
304 | 13B07F961A680F5B00A75B9A /* example.app */,
305 | 00E356EE1AD99517003FC87E /* exampleTests.xctest */,
306 | );
307 | name = Products;
308 | sourceTree = "";
309 | };
310 | /* End PBXGroup section */
311 |
312 | /* Begin PBXNativeTarget section */
313 | 00E356ED1AD99517003FC87E /* exampleTests */ = {
314 | isa = PBXNativeTarget;
315 | buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "exampleTests" */;
316 | buildPhases = (
317 | 00E356EA1AD99517003FC87E /* Sources */,
318 | 00E356EB1AD99517003FC87E /* Frameworks */,
319 | 00E356EC1AD99517003FC87E /* Resources */,
320 | );
321 | buildRules = (
322 | );
323 | dependencies = (
324 | 00E356F51AD99517003FC87E /* PBXTargetDependency */,
325 | );
326 | name = exampleTests;
327 | productName = exampleTests;
328 | productReference = 00E356EE1AD99517003FC87E /* exampleTests.xctest */;
329 | productType = "com.apple.product-type.bundle.unit-test";
330 | };
331 | 13B07F861A680F5B00A75B9A /* example */ = {
332 | isa = PBXNativeTarget;
333 | buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "example" */;
334 | buildPhases = (
335 | 13B07F871A680F5B00A75B9A /* Sources */,
336 | 13B07F8C1A680F5B00A75B9A /* Frameworks */,
337 | 13B07F8E1A680F5B00A75B9A /* Resources */,
338 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
339 | );
340 | buildRules = (
341 | );
342 | dependencies = (
343 | );
344 | name = example;
345 | productName = "Hello World";
346 | productReference = 13B07F961A680F5B00A75B9A /* example.app */;
347 | productType = "com.apple.product-type.application";
348 | };
349 | /* End PBXNativeTarget section */
350 |
351 | /* Begin PBXProject section */
352 | 83CBB9F71A601CBA00E9B192 /* Project object */ = {
353 | isa = PBXProject;
354 | attributes = {
355 | LastUpgradeCheck = 0800;
356 | ORGANIZATIONNAME = Facebook;
357 | TargetAttributes = {
358 | 00E356ED1AD99517003FC87E = {
359 | CreatedOnToolsVersion = 6.2;
360 | TestTargetID = 13B07F861A680F5B00A75B9A;
361 | };
362 | 13B07F861A680F5B00A75B9A = {
363 | DevelopmentTeam = X9N4ZRBGW2;
364 | };
365 | };
366 | };
367 | buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "example" */;
368 | compatibilityVersion = "Xcode 3.2";
369 | developmentRegion = English;
370 | hasScannedForEncodings = 0;
371 | knownRegions = (
372 | en,
373 | Base,
374 | );
375 | mainGroup = 83CBB9F61A601CBA00E9B192;
376 | productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
377 | projectDirPath = "";
378 | projectReferences = (
379 | {
380 | ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */;
381 | ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */;
382 | },
383 | {
384 | ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */;
385 | ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */;
386 | },
387 | {
388 | ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */;
389 | ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
390 | },
391 | {
392 | ProductGroup = 78C398B11ACF4ADC00677621 /* Products */;
393 | ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
394 | },
395 | {
396 | ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */;
397 | ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
398 | },
399 | {
400 | ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */;
401 | ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
402 | },
403 | {
404 | ProductGroup = 832341B11AAA6A8300B99B32 /* Products */;
405 | ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
406 | },
407 | {
408 | ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */;
409 | ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */;
410 | },
411 | {
412 | ProductGroup = 139FDEE71B06529A00C62182 /* Products */;
413 | ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
414 | },
415 | {
416 | ProductGroup = 146834001AC3E56700842450 /* Products */;
417 | ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */;
418 | },
419 | );
420 | projectRoot = "";
421 | targets = (
422 | 13B07F861A680F5B00A75B9A /* example */,
423 | 00E356ED1AD99517003FC87E /* exampleTests */,
424 | );
425 | };
426 | /* End PBXProject section */
427 |
428 | /* Begin PBXReferenceProxy section */
429 | 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = {
430 | isa = PBXReferenceProxy;
431 | fileType = archive.ar;
432 | path = libRCTActionSheet.a;
433 | remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */;
434 | sourceTree = BUILT_PRODUCTS_DIR;
435 | };
436 | 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = {
437 | isa = PBXReferenceProxy;
438 | fileType = archive.ar;
439 | path = libRCTGeolocation.a;
440 | remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */;
441 | sourceTree = BUILT_PRODUCTS_DIR;
442 | };
443 | 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = {
444 | isa = PBXReferenceProxy;
445 | fileType = archive.ar;
446 | path = libRCTImage.a;
447 | remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */;
448 | sourceTree = BUILT_PRODUCTS_DIR;
449 | };
450 | 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = {
451 | isa = PBXReferenceProxy;
452 | fileType = archive.ar;
453 | path = libRCTNetwork.a;
454 | remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */;
455 | sourceTree = BUILT_PRODUCTS_DIR;
456 | };
457 | 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = {
458 | isa = PBXReferenceProxy;
459 | fileType = archive.ar;
460 | path = libRCTVibration.a;
461 | remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */;
462 | sourceTree = BUILT_PRODUCTS_DIR;
463 | };
464 | 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = {
465 | isa = PBXReferenceProxy;
466 | fileType = archive.ar;
467 | path = libRCTSettings.a;
468 | remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */;
469 | sourceTree = BUILT_PRODUCTS_DIR;
470 | };
471 | 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */ = {
472 | isa = PBXReferenceProxy;
473 | fileType = archive.ar;
474 | path = libRCTWebSocket.a;
475 | remoteRef = 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */;
476 | sourceTree = BUILT_PRODUCTS_DIR;
477 | };
478 | 146834041AC3E56700842450 /* libReact.a */ = {
479 | isa = PBXReferenceProxy;
480 | fileType = archive.ar;
481 | path = libReact.a;
482 | remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */;
483 | sourceTree = BUILT_PRODUCTS_DIR;
484 | };
485 | 78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = {
486 | isa = PBXReferenceProxy;
487 | fileType = archive.ar;
488 | path = libRCTLinking.a;
489 | remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */;
490 | sourceTree = BUILT_PRODUCTS_DIR;
491 | };
492 | 832341B51AAA6A8300B99B32 /* libRCTText.a */ = {
493 | isa = PBXReferenceProxy;
494 | fileType = archive.ar;
495 | path = libRCTText.a;
496 | remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */;
497 | sourceTree = BUILT_PRODUCTS_DIR;
498 | };
499 | /* End PBXReferenceProxy section */
500 |
501 | /* Begin PBXResourcesBuildPhase section */
502 | 00E356EC1AD99517003FC87E /* Resources */ = {
503 | isa = PBXResourcesBuildPhase;
504 | buildActionMask = 2147483647;
505 | files = (
506 | );
507 | runOnlyForDeploymentPostprocessing = 0;
508 | };
509 | 13B07F8E1A680F5B00A75B9A /* Resources */ = {
510 | isa = PBXResourcesBuildPhase;
511 | buildActionMask = 2147483647;
512 | files = (
513 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
514 | 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,
515 | );
516 | runOnlyForDeploymentPostprocessing = 0;
517 | };
518 | /* End PBXResourcesBuildPhase section */
519 |
520 | /* Begin PBXShellScriptBuildPhase section */
521 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
522 | isa = PBXShellScriptBuildPhase;
523 | buildActionMask = 2147483647;
524 | files = (
525 | );
526 | inputPaths = (
527 | );
528 | name = "Bundle React Native code and images";
529 | outputPaths = (
530 | );
531 | runOnlyForDeploymentPostprocessing = 0;
532 | shellPath = /bin/sh;
533 | shellScript = "export NODE_BINARY=node\n../node_modules/react-native/packager/react-native-xcode.sh";
534 | };
535 | /* End PBXShellScriptBuildPhase section */
536 |
537 | /* Begin PBXSourcesBuildPhase section */
538 | 00E356EA1AD99517003FC87E /* Sources */ = {
539 | isa = PBXSourcesBuildPhase;
540 | buildActionMask = 2147483647;
541 | files = (
542 | 00E356F31AD99517003FC87E /* exampleTests.m in Sources */,
543 | );
544 | runOnlyForDeploymentPostprocessing = 0;
545 | };
546 | 13B07F871A680F5B00A75B9A /* Sources */ = {
547 | isa = PBXSourcesBuildPhase;
548 | buildActionMask = 2147483647;
549 | files = (
550 | 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
551 | 13B07FC11A68108700A75B9A /* main.m in Sources */,
552 | );
553 | runOnlyForDeploymentPostprocessing = 0;
554 | };
555 | /* End PBXSourcesBuildPhase section */
556 |
557 | /* Begin PBXTargetDependency section */
558 | 00E356F51AD99517003FC87E /* PBXTargetDependency */ = {
559 | isa = PBXTargetDependency;
560 | target = 13B07F861A680F5B00A75B9A /* example */;
561 | targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
562 | };
563 | /* End PBXTargetDependency section */
564 |
565 | /* Begin PBXVariantGroup section */
566 | 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = {
567 | isa = PBXVariantGroup;
568 | children = (
569 | 13B07FB21A68108700A75B9A /* Base */,
570 | );
571 | name = LaunchScreen.xib;
572 | path = example;
573 | sourceTree = "";
574 | };
575 | /* End PBXVariantGroup section */
576 |
577 | /* Begin XCBuildConfiguration section */
578 | 00E356F61AD99517003FC87E /* Debug */ = {
579 | isa = XCBuildConfiguration;
580 | buildSettings = {
581 | BUNDLE_LOADER = "$(TEST_HOST)";
582 | GCC_PREPROCESSOR_DEFINITIONS = (
583 | "DEBUG=1",
584 | "$(inherited)",
585 | );
586 | INFOPLIST_FILE = exampleTests/Info.plist;
587 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
588 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
589 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
590 | PRODUCT_NAME = "$(TARGET_NAME)";
591 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/example";
592 | };
593 | name = Debug;
594 | };
595 | 00E356F71AD99517003FC87E /* Release */ = {
596 | isa = XCBuildConfiguration;
597 | buildSettings = {
598 | BUNDLE_LOADER = "$(TEST_HOST)";
599 | COPY_PHASE_STRIP = NO;
600 | INFOPLIST_FILE = exampleTests/Info.plist;
601 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
602 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
603 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
604 | PRODUCT_NAME = "$(TARGET_NAME)";
605 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/example";
606 | };
607 | name = Release;
608 | };
609 | 13B07F941A680F5B00A75B9A /* Debug */ = {
610 | isa = XCBuildConfiguration;
611 | buildSettings = {
612 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
613 | CURRENT_PROJECT_VERSION = 1;
614 | DEAD_CODE_STRIPPING = NO;
615 | DEVELOPMENT_TEAM = X9N4ZRBGW2;
616 | HEADER_SEARCH_PATHS = (
617 | "$(inherited)",
618 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
619 | "$(SRCROOT)/../node_modules/react-native/React/**",
620 | );
621 | INFOPLIST_FILE = example/Info.plist;
622 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
623 | OTHER_LDFLAGS = (
624 | "$(inherited)",
625 | "-ObjC",
626 | "-lc++",
627 | );
628 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
629 | PRODUCT_NAME = example;
630 | VERSIONING_SYSTEM = "apple-generic";
631 | };
632 | name = Debug;
633 | };
634 | 13B07F951A680F5B00A75B9A /* Release */ = {
635 | isa = XCBuildConfiguration;
636 | buildSettings = {
637 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
638 | CURRENT_PROJECT_VERSION = 1;
639 | DEVELOPMENT_TEAM = X9N4ZRBGW2;
640 | HEADER_SEARCH_PATHS = (
641 | "$(inherited)",
642 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
643 | "$(SRCROOT)/../node_modules/react-native/React/**",
644 | );
645 | INFOPLIST_FILE = example/Info.plist;
646 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
647 | OTHER_LDFLAGS = (
648 | "$(inherited)",
649 | "-ObjC",
650 | "-lc++",
651 | );
652 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
653 | PRODUCT_NAME = example;
654 | VERSIONING_SYSTEM = "apple-generic";
655 | };
656 | name = Release;
657 | };
658 | 83CBBA201A601CBA00E9B192 /* Debug */ = {
659 | isa = XCBuildConfiguration;
660 | buildSettings = {
661 | ALWAYS_SEARCH_USER_PATHS = NO;
662 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
663 | CLANG_CXX_LIBRARY = "libc++";
664 | CLANG_ENABLE_MODULES = YES;
665 | CLANG_ENABLE_OBJC_ARC = YES;
666 | CLANG_WARN_BOOL_CONVERSION = YES;
667 | CLANG_WARN_CONSTANT_CONVERSION = YES;
668 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
669 | CLANG_WARN_EMPTY_BODY = YES;
670 | CLANG_WARN_ENUM_CONVERSION = YES;
671 | CLANG_WARN_INFINITE_RECURSION = YES;
672 | CLANG_WARN_INT_CONVERSION = YES;
673 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
674 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
675 | CLANG_WARN_UNREACHABLE_CODE = YES;
676 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
677 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
678 | COPY_PHASE_STRIP = NO;
679 | ENABLE_STRICT_OBJC_MSGSEND = YES;
680 | ENABLE_TESTABILITY = YES;
681 | GCC_C_LANGUAGE_STANDARD = gnu99;
682 | GCC_DYNAMIC_NO_PIC = NO;
683 | GCC_NO_COMMON_BLOCKS = YES;
684 | GCC_OPTIMIZATION_LEVEL = 0;
685 | GCC_PREPROCESSOR_DEFINITIONS = (
686 | "DEBUG=1",
687 | "$(inherited)",
688 | );
689 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
690 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
691 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
692 | GCC_WARN_UNDECLARED_SELECTOR = YES;
693 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
694 | GCC_WARN_UNUSED_FUNCTION = YES;
695 | GCC_WARN_UNUSED_VARIABLE = YES;
696 | HEADER_SEARCH_PATHS = (
697 | "$(inherited)",
698 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
699 | "$(SRCROOT)/../node_modules/react-native/React/**",
700 | );
701 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
702 | MTL_ENABLE_DEBUG_INFO = YES;
703 | ONLY_ACTIVE_ARCH = YES;
704 | SDKROOT = iphoneos;
705 | };
706 | name = Debug;
707 | };
708 | 83CBBA211A601CBA00E9B192 /* Release */ = {
709 | isa = XCBuildConfiguration;
710 | buildSettings = {
711 | ALWAYS_SEARCH_USER_PATHS = NO;
712 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
713 | CLANG_CXX_LIBRARY = "libc++";
714 | CLANG_ENABLE_MODULES = YES;
715 | CLANG_ENABLE_OBJC_ARC = YES;
716 | CLANG_WARN_BOOL_CONVERSION = YES;
717 | CLANG_WARN_CONSTANT_CONVERSION = YES;
718 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
719 | CLANG_WARN_EMPTY_BODY = YES;
720 | CLANG_WARN_ENUM_CONVERSION = YES;
721 | CLANG_WARN_INFINITE_RECURSION = YES;
722 | CLANG_WARN_INT_CONVERSION = YES;
723 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
724 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
725 | CLANG_WARN_UNREACHABLE_CODE = YES;
726 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
727 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
728 | COPY_PHASE_STRIP = YES;
729 | ENABLE_NS_ASSERTIONS = NO;
730 | ENABLE_STRICT_OBJC_MSGSEND = YES;
731 | GCC_C_LANGUAGE_STANDARD = gnu99;
732 | GCC_NO_COMMON_BLOCKS = YES;
733 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
734 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
735 | GCC_WARN_UNDECLARED_SELECTOR = YES;
736 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
737 | GCC_WARN_UNUSED_FUNCTION = YES;
738 | GCC_WARN_UNUSED_VARIABLE = YES;
739 | HEADER_SEARCH_PATHS = (
740 | "$(inherited)",
741 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
742 | "$(SRCROOT)/../node_modules/react-native/React/**",
743 | );
744 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
745 | MTL_ENABLE_DEBUG_INFO = NO;
746 | SDKROOT = iphoneos;
747 | VALIDATE_PRODUCT = YES;
748 | };
749 | name = Release;
750 | };
751 | /* End XCBuildConfiguration section */
752 |
753 | /* Begin XCConfigurationList section */
754 | 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "exampleTests" */ = {
755 | isa = XCConfigurationList;
756 | buildConfigurations = (
757 | 00E356F61AD99517003FC87E /* Debug */,
758 | 00E356F71AD99517003FC87E /* Release */,
759 | );
760 | defaultConfigurationIsVisible = 0;
761 | defaultConfigurationName = Release;
762 | };
763 | 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "example" */ = {
764 | isa = XCConfigurationList;
765 | buildConfigurations = (
766 | 13B07F941A680F5B00A75B9A /* Debug */,
767 | 13B07F951A680F5B00A75B9A /* Release */,
768 | );
769 | defaultConfigurationIsVisible = 0;
770 | defaultConfigurationName = Release;
771 | };
772 | 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "example" */ = {
773 | isa = XCConfigurationList;
774 | buildConfigurations = (
775 | 83CBBA201A601CBA00E9B192 /* Debug */,
776 | 83CBBA211A601CBA00E9B192 /* Release */,
777 | );
778 | defaultConfigurationIsVisible = 0;
779 | defaultConfigurationName = Release;
780 | };
781 | /* End XCConfigurationList section */
782 | };
783 | rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
784 | }
785 |
--------------------------------------------------------------------------------