├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .travis.yml
├── README.md
├── examples
├── .gitkeep
├── complex-react-nav-with-guest-access
│ ├── .babelrc
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── App.js
│ ├── App.test.js
│ ├── README.md
│ ├── app.json
│ ├── package.json
│ ├── src
│ │ ├── api
│ │ │ └── auth.js
│ │ ├── components
│ │ │ ├── Button.js
│ │ │ └── Input.js
│ │ ├── index.js
│ │ ├── router.js
│ │ └── screens
│ │ │ ├── Feed.js
│ │ │ ├── Loading.js
│ │ │ ├── Profile.js
│ │ │ ├── SignIn.js
│ │ │ └── SignUp.js
│ └── yarn.lock
└── rotating-local-images
│ ├── .babelrc
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── App.js
│ ├── app.json
│ ├── assets
│ ├── icon.png
│ └── splash.png
│ ├── package.json
│ └── src
│ ├── images
│ ├── 1.jpg
│ ├── 2.jpg
│ └── 3.jpg
│ └── index.js
├── package.json
├── tutorials
├── complex-react-nav-with-guest-access
│ └── .expo
│ │ └── packager-info.json
├── detect-multiple-touches-with-gesture-responder-system
│ ├── App.js
│ ├── app.json
│ └── package.json
├── flexbox-basics
│ ├── .babelrc
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── App.js
│ ├── app.json
│ ├── assets
│ │ ├── icon.png
│ │ └── splash.png
│ └── package.json
├── global-alerts
│ ├── .babelrc
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── App.js
│ ├── GlobalAlerts.js
│ ├── app.json
│ ├── assets
│ │ ├── close.png
│ │ ├── close@2x.png
│ │ ├── close@3x.png
│ │ ├── icon.png
│ │ └── splash.png
│ ├── package.json
│ └── yarn.lock
├── image-basics
│ ├── .babelrc
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── App.js
│ ├── app.json
│ ├── assets
│ │ ├── icon.png
│ │ ├── rn-school-logo.png
│ │ ├── rn-school-logo@2x.png
│ │ ├── rn-school-logo@3x.png
│ │ └── splash.png
│ └── package.json
├── instagram-style-animated-image-overlay
│ ├── .babelrc
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── App.js
│ ├── App.test.js
│ ├── README.md
│ ├── Tutorial.md
│ ├── app.json
│ ├── package.json
│ ├── src
│ │ ├── DoubleTap.js
│ │ ├── images
│ │ │ ├── heart-outline.png
│ │ │ ├── heart-outline@2x.png
│ │ │ ├── heart-outline@3x.png
│ │ │ ├── heart.png
│ │ │ ├── heart@2x.png
│ │ │ └── heart@3x.png
│ │ └── index.js
│ ├── tutorial-assets
│ │ ├── 01.png
│ │ ├── 02.png
│ │ ├── 03.gif
│ │ └── src.zip
│ └── yarn.lock
├── instagram-style-double-tap
│ ├── .babelrc
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── App.js
│ ├── App.test.js
│ ├── README.md
│ ├── Tutorial.md
│ ├── app.json
│ ├── package.json
│ ├── src
│ │ ├── DoubleTap.js
│ │ ├── images
│ │ │ ├── heart-outline.png
│ │ │ ├── heart-outline@2x.png
│ │ │ ├── heart-outline@3x.png
│ │ │ ├── heart.png
│ │ │ ├── heart@2x.png
│ │ │ └── heart@3x.png
│ │ └── index.js
│ ├── tutorial-assets
│ │ └── demo.gif
│ └── yarn.lock
├── long-press-with-gesture-responder-system
│ ├── App.js
│ ├── app.json
│ └── package.json
├── multi-theme-button
│ ├── .babelrc
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── App.js
│ ├── App.test.js
│ ├── Button.js
│ ├── README.md
│ ├── app.json
│ ├── package.json
│ └── yarn.lock
├── progressive-image-loading
│ ├── .babelrc
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── App.js
│ ├── App.test.js
│ ├── ProgressiveImage.js
│ ├── README.md
│ ├── Tutorial.md
│ ├── app.json
│ ├── package.json
│ ├── tutorial-assets
│ │ ├── 01.png
│ │ ├── 02.gif
│ │ ├── 03.gif
│ │ ├── 04.gif
│ │ ├── 05.gif
│ │ ├── 06.png
│ │ ├── 07.png
│ │ └── 08.gif
│ └── yarn.lock
└── section-list-basics
│ ├── .babelrc
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── App.js
│ ├── app.json
│ ├── assets
│ ├── icon.png
│ └── splash.png
│ └── package.json
└── yarn.lock
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "extends": "airbnb",
3 | "parser": "babel-eslint",
4 | "env": {
5 | "browser": true,
6 | "jest": true
7 | },
8 | "rules": {
9 | "react/jsx-filename-extension": 0,
10 | "react/prop-types": 0,
11 | "react/destructuring-assignment": 0,
12 | "global-require": 0,
13 | "import/no-unresolved": 0,
14 | "react/prefer-stateless-function": 0,
15 | "prefer-destructuring": 0,
16 | "import/prefer-default-export": 0,
17 | "no-underscore-dangle": 0,
18 | "react/no-multi-comp": 0,
19 | "no-alert": 0
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "8"
4 | install:
5 | - yarn install
6 | script:
7 | - yarn run lint
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Native Examples and Tutorials
2 |
3 | A single place to find all of the React Native tutorials created at Handlebar Labs.
4 |
5 | **Looking for more in-depth content? Check out any of [Handlebar Labs' courses](https://learn.handlebarlabs.com/courses)!**
6 |
7 | * [Tutorials](#tutorials)
8 | * [Examples](#examples)
9 | * [Courses](#courses)
10 |
11 | ## Tutorials
12 |
13 | * Layout with Flexbox - React Native Basics (November 7, 2018) - [video](https://www.youtube.com/watch?v=qBFMnFXVw2c), [code](https://github.com/HandlebarLabs/react-native-examples-and-tutorials/tree/master/tutorials/flexbox-basics)
14 |
15 | * How to use the Image Component - React Native Basics (November 7, 2018) - [video](https://www.youtube.com/watch?v=v-3sNvMNosY), [code](https://github.com/HandlebarLabs/react-native-examples-and-tutorials/tree/master/tutorials/image-basics)
16 |
17 | * Building a Global Alert System in React Native (November 5, 2018) - [video series](https://www.youtube.com/watch?v=NsAQTbAV2zE&list=PLG02JlJZbKbtNnOx2vlUqF_HBve5HVlH1), [code](https://github.com/HandlebarLabs/react-native-examples-and-tutorials/tree/master/tutorials/global-alerts)
18 |
19 | * How to Use the SectionList Component - React Native Basics (October 24, 2018) - [video tutorial](https://www.youtube.com/watch?v=I-6oheM6cz0&feature=youtu.be), [code](https://github.com/HandlebarLabs/react-native-examples-and-tutorials/tree/master/tutorials/section-list-basics)
20 |
21 | * Implement a Long Press Button with React Native's Gesture Responder System (October 17, 2018) - [video tutorial](https://www.youtube.com/watch?v=zYIvl9rEEK8), [code](https://github.com/HandlebarLabs/react-native-examples-and-tutorials/tree/master/tutorials/long-press-with-gesture-responder-system)
22 |
23 | * Detect Multiple Touches in React Native with the Gesture Responder System (October 17, 2018) - [video tutorial](https://www.youtube.com/watch?v=j7LK96nRXP4), [code](https://github.com/HandlebarLabs/react-native-examples-and-tutorials/tree/master/tutorials/detect-multiple-touches-with-gesture-responder-system)
24 |
25 | * Fix Broken React Native Projects in Xcode 10 (October 9, 2018) - [video tutorial](https://www.youtube.com/watch?v=sLMCmXT4i9A)
26 |
27 | * Progressive Image Loading (September 26, 2018) - [tutorial](https://medium.com/react-native-training/progressive-image-loading-in-react-native-e7a01827feb7), [code](https://github.com/HandlebarLabs/react-native-examples-and-tutorials/tree/master/tutorials/progressive-image-loading)
28 |
29 | * Building a React Native Multi-Theme Button (September 19, 2018) - [video tutorial](https://youtu.be/UUcaqUUeHWE), [code](https://github.com/HandlebarLabs/react-native-examples-and-tutorials/tree/master/tutorials/multi-theme-button)
30 |
31 | * Animated Image Overlay in React Native (September 12, 2018) - [written tutorial](https://medium.com/@spencer_carli/animated-image-overlay-in-react-native-9b8dd22a9c6e), [code](https://github.com/HandlebarLabs/react-native-examples-and-tutorials/tree/master/tutorials/instagram-style-animated-image-overlays)
32 |
33 | * Instagram Style Double Tap with React Native (September 6, 2018) - [written tutorial](https://medium.com/handlebar-labs/instagram-style-double-tap-with-react-native-49e757f68de), [code](https://github.com/HandlebarLabs/react-native-examples-and-tutorials/tree/master/tutorials/instagram-style-double-tap)
34 |
35 | * Install React Native Firebase Core [iOS] (August 16, 2018) - [video tutorial](https://www.youtube.com/watch?v=qiOGMcX6Xtw)
36 |
37 | * Install React Native Firebase Core [Android] (August 16, 2018) - [video tutorial](https://www.youtube.com/watch?v=_7iKm233n_M)
38 |
39 | * Intro to Gestures in React Native (July 24, 2018) - [written tutorial](https://medium.com/handlebar-labs/intro-to-gestures-in-react-native-e9b63dd3305), [code](https://snack.expo.io/@spencercarli/basic-javascript-navigator-example)
40 |
41 | * Build A JavaScript Navigator for React Native (July 24, 2018) - [written tutorial](https://www.fullstackreact.com/articles/build-your-own-javascript-navigator-for-react-native/)
42 |
43 | * How to Search a React Native FlatList (May 23, 2018) - [video tutorial](https://www.youtube.com/watch?v=b5P6LIjQZEU), [code](https://github.com/spencercarli/searchable-react-native-flatlist/tree/finished)
44 |
45 | * React Native Animated Answer Bar [Playlist - 7 Videos] (May 21, 2018) - [playlist](https://www.youtube.com/watch?v=LPieikcEMN0&list=PLG02JlJZbKbvWvjVhbjTq1IRg7Q3A1667), [code](https://github.com/HandlebarLabs/react-native-animated-answer-bar)
46 |
47 | * Enable Scroll in a React Native ScrollView Based on the Content Size (April 26, 2018) - [written tutorial](https://medium.com/@spencer_carli/enable-scroll-in-a-react-native-scrollview-based-on-the-content-size-87430ccf319b), [video tutorial](https://www.youtube.com/watch?v=riWf6CtFkUA), [code](https://github.com/spencercarli/react-native-dynamic-scrollview)
48 |
49 | * Use the SwitchNavigator in React Navigation for an Authentication Flow (React Native) (March 27, 2018) - [video tutorial](https://www.youtube.com/watch?v=L0ZsVjh2zBo), [code](https://github.com/spencercarli/react-navigation-auth-flow/tree/finished-code)
50 |
51 | * 4 Questions to Ask When Choosing a Push Notification Provider for a React Native App (March 21, 2018) - [written tutorial](https://medium.com/handlebar-labs/4-questions-to-ask-when-choosing-a-push-notification-provider-for-a-react-native-app-fc0949eebc40)
52 |
53 | * Migrating from the Old to New React Context API (March 13, 2018) - [video tutorial](https://www.youtube.com/watch?v=pfHk-k0R48A), [code](https://github.com/HandlebarLabs/currency-converter-starter/compare/dfd226dee89d2aa723470b567fab3a957e294822...6c51edf0d2984002f89585482aa15bdba826a59d)
54 |
55 | * Migrating from Redux to the New React Context API (March 13, 2018) - [video tutorial](https://www.youtube.com/watch?v=ISgz8F9z0aM), [code](https://github.com/HandlebarLabs/currency-converter-starter/compare/e96b8b31529291029ce56d9cd1dab352a4a09102...dfd226dee89d2aa723470b567fab3a957e294822)
56 |
57 | * React Native FlatList Grid (March 13, 2018) - [video tutorial](https://www.youtube.com/watch?v=8wv0kjsirso), [code](https://snack.expo.io/@spencercarli/react-native-flatlist-grid)
58 |
59 | * Fade in an Image with React Native with the Animated API (March 13, 2018) - [video tutorial](https://www.youtube.com/watch?v=vzPmI0GCDPM), [code](https://snack.expo.io/@spencercarli/fade-in-an-image-with-react-native-with-the-animated-api)
60 |
61 | * iPhoneX Compatibility in React Native with SafeAreaView (March 13, 2018) - [video tutorial](https://www.youtube.com/watch?v=C5gMteV-4-Y), [code](https://snack.expo.io/@spencercarli/iphonex-compatibility-in-react-native-with-safeareaview)
62 |
63 | * Dismiss the Keyboard in React Native from Anywhere (March 13, 2018) - [video tutorial](https://www.youtube.com/watch?v=z_FVCeWloig), [code](https://snack.expo.io/@spencercarli/dismiss-the-keyboard-in-react-native-from-anywhere)
64 |
65 | * How 💩 dad jokes helped me better understand immutable data (March 7, 2018) - [written tutorial](https://medium.com/handlebar-labs/how-dad-jokes-helped-me-better-understand-immutable-data-d4256a0d4eea), [code](https://snack.expo.io/B1N7JqT_G)
66 |
67 | * Perfect React Native Splash Screen (Android) (February 27, 2018) - [written tutorial](https://medium.com/handlebar-labs/how-to-add-a-splash-screen-to-a-react-native-app-ios-and-android-30a3cec835ae), [video tutorial](https://www.youtube.com/watch?v=yFrx8HZlNtI), [code](https://github.com/spencercarli/react-native-splash-screen-demo)
68 |
69 | * Perfect React Native Splash Screen (iOS) (February 27, 2018) - [written tutorial](https://medium.com/handlebar-labs/how-to-add-a-splash-screen-to-a-react-native-app-ios-and-android-30a3cec835ae), [video tutorial](https://www.youtube.com/watch?v=H0CC1UsvjDQ), [code](https://github.com/spencercarli/react-native-splash-screen-demo)
70 |
71 | * Configure Visual Studio Code for React Native Development (February 21, 2018) - [video tutorial](https://www.youtube.com/watch?v=_srHOd6EFQ0&t=20s)
72 |
73 | * How to Become a React Native Developer in 2018 (January 26, 2018) - [written tutorial](https://hackernoon.com/how-to-become-a-react-native-developer-in-2018-d9bc85e1d91f)
74 |
75 | * Setup Continuous Integration with React Native (November 16, 2017) - [written tutorial](https://medium.com/react-native-training/setup-continuous-integration-with-react-native-50ad2f6145f4)
76 |
77 | ## Examples
78 |
79 | * Complex React Navigation with Guest Access - [code](https://github.com/HandlebarLabs/react-native-examples-and-tutorials/tree/master/examples/complex-react-nav-with-guest-access)
80 |
81 | ## Courses
82 |
83 | * [**React Native Basics: Build a Currency Converter** - Learn to Use Navigation, Setup Redux, Design Components, Work with a Remote API, and More](https://learn.handlebarlabs.com/p/react-native-basics-build-a-currency-converter) (FREE)
84 |
85 | * [**Production Ready React Native** - Learn Best Practices, Follow a Focused Curriculum, and Write Code You’re Proud Of](https://learn.handlebarlabs.com/p/learn-to-send-react-native-apps-to-production) (PREMIUM)
86 |
87 | * [**Master Push Notifications in React Native** - The Most Effective and Efficient Way to Add Push Notifications to Your App](https://learn.handlebarlabs.com/p/master-push-notifications-in-react-native) (PREMIUM)
88 |
89 | * [**React Native Exercises** - Ten Exercises to Make You a Better React Native Developer](https://learn.handlebarlabs.com/p/react-native-exercises ) (PREMIUM)
90 |
91 | * [**How to Set up a New React Native Project** - Tips, Tricks, and Tools to Optimize Your React Native Development Workflow](https://learn.handlebarlabs.com/p/react-native-exercises ) (FREE)
92 |
93 | * [**Learn React Native + Meteor** - A Premium Course on Building Real-World iOS and Android Apps with React Native and Meteor](https://learn.handlebarlabs.com/p/react-native-meteor) (PREMIUM)
94 |
--------------------------------------------------------------------------------
/examples/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/examples/.gitkeep
--------------------------------------------------------------------------------
/examples/complex-react-nav-with-guest-access/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"],
3 | "env": {
4 | "development": {
5 | "plugins": ["transform-react-jsx-source"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/examples/complex-react-nav-with-guest-access/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # expo
4 | .expo/
5 |
6 | # dependencies
7 | /node_modules
8 |
9 | # misc
10 | .env.local
11 | .env.development.local
12 | .env.test.local
13 | .env.production.local
14 |
15 | npm-debug.log*
16 | yarn-debug.log*
17 | yarn-error.log*
18 |
--------------------------------------------------------------------------------
/examples/complex-react-nav-with-guest-access/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/examples/complex-react-nav-with-guest-access/App.js:
--------------------------------------------------------------------------------
1 | import App from './src';
2 |
3 | export default App;
4 |
--------------------------------------------------------------------------------
/examples/complex-react-nav-with-guest-access/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const rendered = renderer.create().toJSON();
7 | expect(rendered).toBeTruthy();
8 | });
9 |
--------------------------------------------------------------------------------
/examples/complex-react-nav-with-guest-access/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React Native App](https://github.com/react-community/create-react-native-app).
2 |
3 | Below you'll find information about performing common tasks. The most recent version of this guide is available [here](https://github.com/react-community/create-react-native-app/blob/master/react-native-scripts/template/README.md).
4 |
5 | ## Table of Contents
6 |
7 | * [Updating to New Releases](#updating-to-new-releases)
8 | * [Available Scripts](#available-scripts)
9 | * [npm start](#npm-start)
10 | * [npm test](#npm-test)
11 | * [npm run ios](#npm-run-ios)
12 | * [npm run android](#npm-run-android)
13 | * [npm run eject](#npm-run-eject)
14 | * [Writing and Running Tests](#writing-and-running-tests)
15 | * [Environment Variables](#environment-variables)
16 | * [Configuring Packager IP Address](#configuring-packager-ip-address)
17 | * [Customizing App Display Name and Icon](#customizing-app-display-name-and-icon)
18 | * [Sharing and Deployment](#sharing-and-deployment)
19 | * [Publishing to Expo's React Native Community](#publishing-to-expos-react-native-community)
20 | * [Building an Expo "standalone" app](#building-an-expo-standalone-app)
21 | * [Ejecting from Create React Native App](#ejecting-from-create-react-native-app)
22 | * [Build Dependencies (Xcode & Android Studio)](#build-dependencies-xcode-android-studio)
23 | * [Should I Use ExpoKit?](#should-i-use-expokit)
24 | * [Troubleshooting](#troubleshooting)
25 | * [Networking](#networking)
26 | * [iOS Simulator won't open](#ios-simulator-wont-open)
27 | * [QR Code does not scan](#qr-code-does-not-scan)
28 |
29 | ## Updating to New Releases
30 |
31 | You should only need to update the global installation of `create-react-native-app` very rarely, ideally never.
32 |
33 | Updating the `react-native-scripts` dependency of your app should be as simple as bumping the version number in `package.json` and reinstalling your project's dependencies.
34 |
35 | Upgrading to a new version of React Native requires updating the `react-native`, `react`, and `expo` package versions, and setting the correct `sdkVersion` in `app.json`. See the [versioning guide](https://github.com/react-community/create-react-native-app/blob/master/VERSIONS.md) for up-to-date information about package version compatibility.
36 |
37 | ## Available Scripts
38 |
39 | If Yarn was installed when the project was initialized, then dependencies will have been installed via Yarn, and you should probably use it to run these commands as well. Unlike dependency installation, command running syntax is identical for Yarn and NPM at the time of this writing.
40 |
41 | ### `npm start`
42 |
43 | Runs your app in development mode.
44 |
45 | Open it in the [Expo app](https://expo.io) on your phone to view it. It will reload if you save edits to your files, and you will see build errors and logs in the terminal.
46 |
47 | Sometimes you may need to reset or clear the React Native packager's cache. To do so, you can pass the `--reset-cache` flag to the start script:
48 |
49 | ```
50 | npm start --reset-cache
51 | # or
52 | yarn start --reset-cache
53 | ```
54 |
55 | #### `npm test`
56 |
57 | Runs the [jest](https://github.com/facebook/jest) test runner on your tests.
58 |
59 | #### `npm run ios`
60 |
61 | Like `npm start`, but also attempts to open your app in the iOS Simulator if you're on a Mac and have it installed.
62 |
63 | #### `npm run android`
64 |
65 | Like `npm start`, but also attempts to open your app on a connected Android device or emulator. Requires an installation of Android build tools (see [React Native docs](https://facebook.github.io/react-native/docs/getting-started.html) for detailed setup). We also recommend installing Genymotion as your Android emulator. Once you've finished setting up the native build environment, there are two options for making the right copy of `adb` available to Create React Native App:
66 |
67 | ##### Using Android Studio's `adb`
68 |
69 | 1. Make sure that you can run adb from your terminal.
70 | 2. Open Genymotion and navigate to `Settings -> ADB`. Select “Use custom Android SDK tools” and update with your [Android SDK directory](https://stackoverflow.com/questions/25176594/android-sdk-location).
71 |
72 | ##### Using Genymotion's `adb`
73 |
74 | 1. Find Genymotion’s copy of adb. On macOS for example, this is normally `/Applications/Genymotion.app/Contents/MacOS/tools/`.
75 | 2. Add the Genymotion tools directory to your path (instructions for [Mac](http://osxdaily.com/2014/08/14/add-new-path-to-path-command-line/), [Linux](http://www.computerhope.com/issues/ch001647.htm), and [Windows](https://www.howtogeek.com/118594/how-to-edit-your-system-path-for-easy-command-line-access/)).
76 | 3. Make sure that you can run adb from your terminal.
77 |
78 | #### `npm run eject`
79 |
80 | This will start the process of "ejecting" from Create React Native App's build scripts. You'll be asked a couple of questions about how you'd like to build your project.
81 |
82 | **Warning:** Running eject is a permanent action (aside from whatever version control system you use). An ejected app will require you to have an [Xcode and/or Android Studio environment](https://facebook.github.io/react-native/docs/getting-started.html) set up.
83 |
84 | ## Customizing App Display Name and Icon
85 |
86 | You can edit `app.json` to include [configuration keys](https://docs.expo.io/versions/latest/guides/configuration.html) under the `expo` key.
87 |
88 | To change your app's display name, set the `expo.name` key in `app.json` to an appropriate string.
89 |
90 | To set an app icon, set the `expo.icon` key in `app.json` to be either a local path or a URL. It's recommended that you use a 512x512 png file with transparency.
91 |
92 | ## Writing and Running Tests
93 |
94 | This project is set up to use [jest](https://facebook.github.io/jest/) for tests. You can configure whatever testing strategy you like, but jest works out of the box. Create test files in directories called `__tests__` or with the `.test` extension to have the files loaded by jest. See the [the template project](https://github.com/react-community/create-react-native-app/blob/master/react-native-scripts/template/App.test.js) for an example test. The [jest documentation](https://facebook.github.io/jest/docs/en/getting-started.html) is also a wonderful resource, as is the [React Native testing tutorial](https://facebook.github.io/jest/docs/en/tutorial-react-native.html).
95 |
96 | ## Environment Variables
97 |
98 | You can configure some of Create React Native App's behavior using environment variables.
99 |
100 | ### Configuring Packager IP Address
101 |
102 | When starting your project, you'll see something like this for your project URL:
103 |
104 | ```
105 | exp://192.168.0.2:19000
106 | ```
107 |
108 | The "manifest" at that URL tells the Expo app how to retrieve and load your app's JavaScript bundle, so even if you load it in the app via a URL like `exp://localhost:19000`, the Expo client app will still try to retrieve your app at the IP address that the start script provides.
109 |
110 | In some cases, this is less than ideal. This might be the case if you need to run your project inside of a virtual machine and you have to access the packager via a different IP address than the one which prints by default. In order to override the IP address or hostname that is detected by Create React Native App, you can specify your own hostname via the `REACT_NATIVE_PACKAGER_HOSTNAME` environment variable:
111 |
112 | Mac and Linux:
113 |
114 | ```
115 | REACT_NATIVE_PACKAGER_HOSTNAME='my-custom-ip-address-or-hostname' npm start
116 | ```
117 |
118 | Windows:
119 | ```
120 | set REACT_NATIVE_PACKAGER_HOSTNAME='my-custom-ip-address-or-hostname'
121 | npm start
122 | ```
123 |
124 | The above example would cause the development server to listen on `exp://my-custom-ip-address-or-hostname:19000`.
125 |
126 | ## Sharing and Deployment
127 |
128 | Create React Native App does a lot of work to make app setup and development simple and straightforward, but it's very difficult to do the same for deploying to Apple's App Store or Google's Play Store without relying on a hosted service.
129 |
130 | ### Publishing to Expo's React Native Community
131 |
132 | Expo provides free hosting for the JS-only apps created by CRNA, allowing you to share your app through the Expo client app. This requires registration for an Expo account.
133 |
134 | Install the `exp` command-line tool, and run the publish command:
135 |
136 | ```
137 | $ npm i -g exp
138 | $ exp publish
139 | ```
140 |
141 | ### Building an Expo "standalone" app
142 |
143 | You can also use a service like [Expo's standalone builds](https://docs.expo.io/versions/latest/guides/building-standalone-apps.html) if you want to get an IPA/APK for distribution without having to build the native code yourself.
144 |
145 | ### Ejecting from Create React Native App
146 |
147 | If you want to build and deploy your app yourself, you'll need to eject from CRNA and use Xcode and Android Studio.
148 |
149 | This is usually as simple as running `npm run eject` in your project, which will walk you through the process. Make sure to install `react-native-cli` and follow the [native code getting started guide for React Native](https://facebook.github.io/react-native/docs/getting-started.html).
150 |
151 | #### Should I Use ExpoKit?
152 |
153 | If you have made use of Expo APIs while working on your project, then those API calls will stop working if you eject to a regular React Native project. If you want to continue using those APIs, you can eject to "React Native + ExpoKit" which will still allow you to build your own native code and continue using the Expo APIs. See the [ejecting guide](https://github.com/react-community/create-react-native-app/blob/master/EJECTING.md) for more details about this option.
154 |
155 | ## Troubleshooting
156 |
157 | ### Networking
158 |
159 | If you're unable to load your app on your phone due to a network timeout or a refused connection, a good first step is to verify that your phone and computer are on the same network and that they can reach each other. Create React Native App needs access to ports 19000 and 19001 so ensure that your network and firewall settings allow access from your device to your computer on both of these ports.
160 |
161 | Try opening a web browser on your phone and opening the URL that the packager script prints, replacing `exp://` with `http://`. So, for example, if underneath the QR code in your terminal you see:
162 |
163 | ```
164 | exp://192.168.0.1:19000
165 | ```
166 |
167 | Try opening Safari or Chrome on your phone and loading
168 |
169 | ```
170 | http://192.168.0.1:19000
171 | ```
172 |
173 | and
174 |
175 | ```
176 | http://192.168.0.1:19001
177 | ```
178 |
179 | If this works, but you're still unable to load your app by scanning the QR code, please open an issue on the [Create React Native App repository](https://github.com/react-community/create-react-native-app) with details about these steps and any other error messages you may have received.
180 |
181 | If you're not able to load the `http` URL in your phone's web browser, try using the tethering/mobile hotspot feature on your phone (beware of data usage, though), connecting your computer to that WiFi network, and restarting the packager. If you are using a VPN you may need to disable it.
182 |
183 | ### iOS Simulator won't open
184 |
185 | If you're on a Mac, there are a few errors that users sometimes see when attempting to `npm run ios`:
186 |
187 | * "non-zero exit code: 107"
188 | * "You may need to install Xcode" but it is already installed
189 | * and others
190 |
191 | There are a few steps you may want to take to troubleshoot these kinds of errors:
192 |
193 | 1. Make sure Xcode is installed and open it to accept the license agreement if it prompts you. You can install it from the Mac App Store.
194 | 2. Open Xcode's Preferences, the Locations tab, and make sure that the `Command Line Tools` menu option is set to something. Sometimes when the CLI tools are first installed by Homebrew this option is left blank, which can prevent Apple utilities from finding the simulator. Make sure to re-run `npm/yarn run ios` after doing so.
195 | 3. If that doesn't work, open the Simulator, and under the app menu select `Reset Contents and Settings...`. After that has finished, quit the Simulator, and re-run `npm/yarn run ios`.
196 |
197 | ### QR Code does not scan
198 |
199 | If you're not able to scan the QR code, make sure your phone's camera is focusing correctly, and also make sure that the contrast on the two colors in your terminal is high enough. For example, WebStorm's default themes may [not have enough contrast](https://github.com/react-community/create-react-native-app/issues/49) for terminal QR codes to be scannable with the system barcode scanners that the Expo app uses.
200 |
201 | If this causes problems for you, you may want to try changing your terminal's color theme to have more contrast, or running Create React Native App from a different terminal. You can also manually enter the URL printed by the packager script in the Expo app's search bar to load it manually.
202 |
--------------------------------------------------------------------------------
/examples/complex-react-nav-with-guest-access/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "sdkVersion": "27.0.0"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/examples/complex-react-nav-with-guest-access/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ComplexReactNavWithGuestAccess",
3 | "version": "0.1.0",
4 | "private": true,
5 | "devDependencies": {
6 | "jest-expo": "~27.0.0",
7 | "react-native-scripts": "1.14.0",
8 | "react-test-renderer": "16.3.1"
9 | },
10 | "main": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
11 | "scripts": {
12 | "start": "react-native-scripts start",
13 | "eject": "react-native-scripts eject",
14 | "android": "react-native-scripts android",
15 | "ios": "react-native-scripts ios",
16 | "test": "jest"
17 | },
18 | "jest": {
19 | "preset": "jest-expo"
20 | },
21 | "dependencies": {
22 | "expo": "^27.0.1",
23 | "react": "16.3.1",
24 | "react-native": "~0.55.2",
25 | "react-navigation": "^2.14.0"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/complex-react-nav-with-guest-access/src/api/auth.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AsyncStorage } from 'react-native';
3 |
4 | const defaultState = {
5 | checkedAuth: false,
6 | isLoggedIn: false,
7 | };
8 |
9 | export const AUTH_KEY = 'auth-demo-key';
10 |
11 | const AuthContext = React.createContext(defaultState);
12 |
13 | export const Consumer = AuthContext.Consumer;
14 |
15 | export class Provider extends React.Component {
16 | state = defaultState;
17 |
18 | componentDidMount() {
19 | AsyncStorage.getItem('authData')
20 | .then((state) => {
21 | this.setState({
22 | ...JSON.parse(state),
23 | checkedAuth: true,
24 | });
25 | });
26 | }
27 |
28 | componentDidUpdate() {
29 | AsyncStorage.setItem('authData', JSON.stringify({ ...this.state, checkedAuth: false }));
30 | }
31 |
32 | createAccount = () => {
33 | this.setState({ isLoggedIn: true, checkedAuth: true });
34 | };
35 |
36 | logIn = () => {
37 | this.setState({ isLoggedIn: true, checkedAuth: true });
38 | }
39 |
40 | logOut = () => {
41 | this.setState({ ...defaultState, checkedAuth: true });
42 | }
43 |
44 | render() {
45 | return (
46 |
54 | {this.props.children}
55 |
56 | );
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/examples/complex-react-nav-with-guest-access/src/components/Button.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, TouchableOpacity, Text } from 'react-native';
3 |
4 | const styles = StyleSheet.create({
5 | container: {
6 | backgroundColor: '#007991',
7 | marginVertical: 10,
8 | marginHorizontal: 15,
9 | borderRadius: 3,
10 | },
11 | containerClear: {
12 | backgroundColor: 'transparent',
13 | marginVertical: 5,
14 | },
15 | text: {
16 | textAlign: 'center',
17 | color: 'rgba(255, 255, 255, 0.9)',
18 | fontWeight: '600',
19 | fontSize: 16,
20 | padding: 15,
21 | },
22 | textClear: {
23 | color: '#007991',
24 | paddingVertical: 5,
25 | },
26 | });
27 |
28 | class Btn extends React.Component {
29 | static defaultProps = {
30 | theme: 'default',
31 | };
32 |
33 | render() {
34 | const { onPress, text, theme } = this.props;
35 | const containerStyles = [styles.container];
36 | const textStyles = [styles.text];
37 |
38 | if (theme === 'clear') {
39 | containerStyles.push(styles.containerClear);
40 | textStyles.push(styles.textClear);
41 | }
42 |
43 | return (
44 |
45 | {text}
46 |
47 | );
48 | }
49 | }
50 |
51 | export default Btn;
52 |
--------------------------------------------------------------------------------
/examples/complex-react-nav-with-guest-access/src/components/Input.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Text,
4 | View,
5 | TextInput,
6 | StyleSheet,
7 | } from 'react-native';
8 |
9 | const styles = StyleSheet.create({
10 | label: {
11 | color: '#98989d',
12 | paddingHorizontal: 20,
13 | marginBottom: 5,
14 | marginTop: 10,
15 | fontSize: 16,
16 | },
17 | inputContainer: {
18 | backgroundColor: '#fff',
19 | paddingHorizontal: 20,
20 | },
21 | input: {
22 | fontSize: 16,
23 | paddingVertical: 10,
24 | },
25 | });
26 |
27 | class Input extends React.Component {
28 | render() {
29 | const { label, ...props } = this.props;
30 | return (
31 |
32 | {label}
33 |
34 |
38 |
39 |
40 | );
41 | }
42 | }
43 |
44 | export default Input;
45 |
--------------------------------------------------------------------------------
/examples/complex-react-nav-with-guest-access/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Router from './router';
3 | import * as Auth from './api/auth';
4 |
5 | class App extends React.Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 | );
12 | }
13 | }
14 |
15 | export default App;
16 |
--------------------------------------------------------------------------------
/examples/complex-react-nav-with-guest-access/src/router.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | createBottomTabNavigator,
4 | createSwitchNavigator,
5 | createStackNavigator,
6 | } from 'react-navigation';
7 |
8 | import Feed from './screens/Feed';
9 | import Loading from './screens/Loading';
10 | import Profile from './screens/Profile';
11 | import SignIn from './screens/SignIn';
12 | import SignUp from './screens/SignUp';
13 |
14 | // Logged In
15 | const FeedStack = createStackNavigator({
16 | Feed: {
17 | screen: Feed,
18 | navigationOptions: {
19 | headerTitle: 'Feed',
20 | },
21 | },
22 | });
23 |
24 | const ProfileStack = createStackNavigator({
25 | Profile: {
26 | screen: Profile,
27 | navigationOptions: {
28 | headerTitle: 'Profile',
29 | },
30 | },
31 | });
32 |
33 | const AppTabs = createBottomTabNavigator({
34 | Feed: {
35 | screen: FeedStack,
36 | },
37 | Profile: {
38 | screen: ProfileStack,
39 | },
40 | });
41 |
42 | const InAppAuthStack = createStackNavigator({
43 | SignIn: {
44 | screen: props => ,
45 | navigationOptions: {
46 | headerTitle: 'Sign In',
47 | },
48 | },
49 | SignUp: {
50 | screen: props => ,
51 | navigationOptions: {
52 | headerTitle: 'Sign Up',
53 | },
54 | },
55 | });
56 |
57 | const AppStack = createStackNavigator({
58 | Tabs: {
59 | screen: AppTabs,
60 | },
61 | InAppAuth: {
62 | screen: InAppAuthStack,
63 | },
64 | }, {
65 | headerMode: 'none',
66 | mode: 'modal',
67 | });
68 |
69 | // Logged Out
70 | const AuthStack = createStackNavigator({
71 | SignIn: {
72 | screen: SignIn,
73 | navigationOptions: {
74 | headerTitle: 'Sign In',
75 | },
76 | },
77 | SignUp: {
78 | screen: SignUp,
79 | navigationOptions: {
80 | headerTitle: 'Sign Up',
81 | },
82 | },
83 | });
84 |
85 | // Main Navigator
86 | const AppNavigator = createSwitchNavigator({
87 | Loading: {
88 | screen: Loading,
89 | },
90 | LoggedOut: {
91 | screen: AuthStack,
92 | },
93 | App: {
94 | screen: AppStack,
95 | },
96 | });
97 |
98 | export default AppNavigator;
99 |
--------------------------------------------------------------------------------
/examples/complex-react-nav-with-guest-access/src/screens/Feed.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View } from 'react-native';
3 |
4 | import * as Auth from '../api/auth';
5 | import Button from '../components/Button';
6 |
7 | class Feed extends React.Component {
8 | handleProtectedAction = () => {
9 | const { isLoggedIn } = this.props;
10 |
11 | if (isLoggedIn) {
12 | alert('Authorized! Doing a protected thing.');
13 | } else {
14 | this.props.navigation.navigate('InAppAuth');
15 | }
16 | }
17 |
18 | render() {
19 | return (
20 |
21 |
25 |
26 | );
27 | }
28 | }
29 |
30 | export default props => (
31 |
32 | {({ isLoggedIn }) => (
33 |
37 | )}
38 |
39 | );
40 |
--------------------------------------------------------------------------------
/examples/complex-react-nav-with-guest-access/src/screens/Loading.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { ActivityIndicator, SafeAreaView } from 'react-native';
3 |
4 | import * as Auth from '../api/auth';
5 |
6 | class Loading extends React.Component {
7 | componentDidMount() {
8 | this.handleAuthNav();
9 | }
10 |
11 | componentDidUpdate() {
12 | this.handleAuthNav();
13 | }
14 |
15 | handleAuthNav = () => {
16 | const { navigation, isLoggedIn, checkedAuth } = this.props;
17 | if (!checkedAuth) return;
18 |
19 | if (isLoggedIn) {
20 | navigation.navigate('App');
21 | } else {
22 | navigation.navigate('LoggedOut');
23 | }
24 | }
25 |
26 | render() {
27 | return (
28 |
29 |
30 |
31 | );
32 | }
33 | }
34 |
35 | export default props => (
36 |
37 | {({ isLoggedIn, checkedAuth }) => (
38 |
43 | )}
44 |
45 | );
46 |
--------------------------------------------------------------------------------
/examples/complex-react-nav-with-guest-access/src/screens/Profile.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View, Text } from 'react-native';
3 |
4 | import Button from '../components/Button';
5 | import * as Auth from '../api/auth';
6 |
7 | class Profile extends React.Component {
8 | handleSignOutPress = () => {
9 | const { logOut, navigation } = this.props;
10 | logOut();
11 | navigation.navigate('LoggedOut');
12 | }
13 |
14 | handleSignIn = () => {
15 | this.props.navigation.navigate('InAppAuth');
16 | }
17 |
18 | renderLoggedOut = () => (
19 |
20 | Log in to view this page
21 |
25 |
26 | );
27 |
28 | renderLoggedIn = () => (
29 |
30 |
34 |
35 | );
36 |
37 | render() {
38 | const { isLoggedIn } = this.props;
39 |
40 | if (isLoggedIn) {
41 | return this.renderLoggedIn();
42 | }
43 |
44 | return this.renderLoggedOut();
45 | }
46 | }
47 |
48 | export default props => (
49 |
50 | {({ logOut, isLoggedIn }) => (
51 |
56 | )}
57 |
58 | );
59 |
--------------------------------------------------------------------------------
/examples/complex-react-nav-with-guest-access/src/screens/SignIn.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View, SafeAreaView } from 'react-native';
3 |
4 | import Input from '../components/Input';
5 | import Button from '../components/Button';
6 | import * as Auth from '../api/auth';
7 |
8 | class SignIn extends React.Component {
9 | static defaultProps = {
10 | isInAppAuth: false,
11 | };
12 |
13 | handleSignInPress = () => {
14 | const { isInAppAuth, navigation, logIn } = this.props;
15 |
16 | logIn();
17 | if (isInAppAuth) {
18 | navigation.goBack(null); // close modal
19 | } else {
20 | navigation.navigate('App');
21 | }
22 | };
23 |
24 | handleSignUpPress = () => {
25 | this.props.navigation.navigate('SignUp');
26 | };
27 |
28 | handleBrowsePress = () => {
29 | this.props.navigation.navigate('App');
30 | };
31 |
32 | render() {
33 | const { isInAppAuth } = this.props;
34 |
35 | return (
36 |
37 |
38 |
42 |
46 |
50 |
55 |
56 | {!isInAppAuth && (
57 |
61 | )}
62 |
63 | );
64 | }
65 | }
66 |
67 | export default props => (
68 |
69 | {({ logIn }) => (
70 |
74 | )}
75 |
76 | );
77 |
--------------------------------------------------------------------------------
/examples/complex-react-nav-with-guest-access/src/screens/SignUp.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View, SafeAreaView } from 'react-native';
3 |
4 | import Input from '../components/Input';
5 | import Button from '../components/Button';
6 | import * as Auth from '../api/auth';
7 |
8 | class SignUp extends React.Component {
9 | static defaultProps = {
10 | isInAppAuth: false,
11 | };
12 |
13 | handleSignUpPress = () => {
14 | const { isInAppAuth, navigation, createAccount } = this.props;
15 | createAccount();
16 | if (isInAppAuth) {
17 | navigation.popToTop(); // go to top of auth stack
18 | navigation.goBack(null); // close modal
19 | } else {
20 | navigation.navigate('App');
21 | }
22 | };
23 |
24 | handleBrowsePress = () => {
25 | this.props.navigation.navigate('App');
26 | };
27 |
28 | render() {
29 | const { isInAppAuth } = this.props;
30 | return (
31 |
32 |
33 |
37 |
41 |
45 |
49 |
50 | {!isInAppAuth && (
51 |
55 | )}
56 |
57 | );
58 | }
59 | }
60 |
61 | export default props => (
62 |
63 | {({ createAccount }) => (
64 |
68 | )}
69 |
70 | );
71 |
--------------------------------------------------------------------------------
/examples/rotating-local-images/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"],
3 | "env": {
4 | "development": {
5 | "plugins": ["transform-react-jsx-source"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/examples/rotating-local-images/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 |
--------------------------------------------------------------------------------
/examples/rotating-local-images/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/examples/rotating-local-images/App.js:
--------------------------------------------------------------------------------
1 | import App from './src';
2 |
3 | export default App;
4 |
--------------------------------------------------------------------------------
/examples/rotating-local-images/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "RotatingLocalImages",
4 | "description": "This project is really great.",
5 | "slug": "RotatingLocalImages",
6 | "privacy": "public",
7 | "sdkVersion": "30.0.0",
8 | "platforms": ["ios", "android"],
9 | "version": "1.0.0",
10 | "orientation": "portrait",
11 | "icon": "./assets/icon.png",
12 | "splash": {
13 | "image": "./assets/splash.png",
14 | "resizeMode": "contain",
15 | "backgroundColor": "#ffffff"
16 | },
17 | "updates": {
18 | "fallbackToCacheTimeout": 0
19 | },
20 | "assetBundlePatterns": [
21 | "**/*"
22 | ],
23 | "ios": {
24 | "supportsTablet": true
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/rotating-local-images/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/examples/rotating-local-images/assets/icon.png
--------------------------------------------------------------------------------
/examples/rotating-local-images/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/examples/rotating-local-images/assets/splash.png
--------------------------------------------------------------------------------
/examples/rotating-local-images/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "empty-project-template",
3 | "main": "node_modules/expo/AppEntry.js",
4 | "private": true,
5 | "scripts": {
6 | "start": "expo start",
7 | "android": "expo start --android",
8 | "ios": "expo start --ios",
9 | "eject": "expo eject"
10 | },
11 | "dependencies": {
12 | "expo": "^30.0.1",
13 | "react": "16.3.1",
14 | "react-native": "https://github.com/expo/react-native/archive/sdk-30.0.0.tar.gz"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/examples/rotating-local-images/src/images/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/examples/rotating-local-images/src/images/1.jpg
--------------------------------------------------------------------------------
/examples/rotating-local-images/src/images/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/examples/rotating-local-images/src/images/2.jpg
--------------------------------------------------------------------------------
/examples/rotating-local-images/src/images/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/examples/rotating-local-images/src/images/3.jpg
--------------------------------------------------------------------------------
/examples/rotating-local-images/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | StyleSheet,
4 | Text,
5 | View,
6 | Image,
7 | } from 'react-native';
8 |
9 | const styles = StyleSheet.create({
10 | container: {
11 | flex: 1,
12 | backgroundColor: '#fff',
13 | alignItems: 'center',
14 | justifyContent: 'center',
15 | },
16 | bannerImage: {
17 | width: '100%',
18 | },
19 | });
20 |
21 | const BANNER_ADS = [
22 | {
23 | name: 'Cat',
24 | path: require('./images/1.jpg'),
25 | // path: './images/1.jpg',
26 | },
27 | {
28 | name: 'Plant',
29 | path: require('./images/2.jpg'),
30 | // path: './images/2.jpg',
31 | },
32 | {
33 | name: 'Dog',
34 | path: require('./images/3.jpg'),
35 | // path: './images/3.jpg',
36 | },
37 | ];
38 |
39 | const getRandomIndex = () => Math.floor(
40 | Math.random() * BANNER_ADS.length,
41 | );
42 |
43 | export default class App extends React.Component {
44 | constructor(props) {
45 | super(props);
46 |
47 | this.state = {
48 | currentImageIndex: getRandomIndex(),
49 | };
50 | }
51 |
52 | componentDidMount() {
53 | setInterval(() => {
54 | this.displayRandomBannerAd();
55 | }, 4000);
56 | }
57 |
58 | displayRandomBannerAd = () => {
59 | this.setState({
60 | currentImageIndex: getRandomIndex(),
61 | });
62 | };
63 |
64 | render() {
65 | if (BANNER_ADS.length > 0) {
66 | return (
67 |
68 |
74 | {BANNER_ADS[this.state.currentImageIndex].name}
75 |
76 | );
77 | }
78 |
79 | return null;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-examples-and-tutorials",
3 | "version": "1.0.0",
4 | "description": "Coming soon...",
5 | "main": "index.js",
6 | "directories": {
7 | "example": "examples"
8 | },
9 | "scripts": {
10 | "test": "echo \"Error: no test specified\" && exit 1",
11 | "lint": "eslint .",
12 | "lint:fix": "eslint . --fix",
13 | "clear-node-modules": "find . -name 'node_modules' -exec rm -rf '{}' +"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/HandlebarLabs/react-native-examples-and-tutorials.git"
18 | },
19 | "keywords": [],
20 | "author": "",
21 | "license": "ISC",
22 | "bugs": {
23 | "url": "https://github.com/HandlebarLabs/react-native-examples-and-tutorials/issues"
24 | },
25 | "homepage": "https://github.com/HandlebarLabs/react-native-examples-and-tutorials#readme",
26 | "devDependencies": {
27 | "babel-eslint": "^9.0.0",
28 | "eslint": "^5.5.0",
29 | "eslint-config-airbnb": "^17.1.0",
30 | "eslint-plugin-import": "^2.14.0",
31 | "eslint-plugin-jsx-a11y": "^6.1.1",
32 | "eslint-plugin-react": "^7.11.1"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tutorials/complex-react-nav-with-guest-access/.expo/packager-info.json:
--------------------------------------------------------------------------------
1 | {
2 | "expoServerPort": null
3 | }
--------------------------------------------------------------------------------
/tutorials/detect-multiple-touches-with-gesture-responder-system/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, TouchableHighlight } from 'react-native';
3 |
4 | const styles = StyleSheet.create({
5 | container: {
6 | flex: 1,
7 | backgroundColor: '#fff',
8 | alignItems: 'center',
9 | justifyContent: 'center',
10 | },
11 | box: {
12 | backgroundColor: '#cc0000',
13 | width: 200,
14 | height: 200,
15 | borderRadius: 5,
16 | },
17 | });
18 |
19 | class MultiTap extends React.Component {
20 | static defaultProps = {
21 | onPress: () => null,
22 | numberOfTouches: 2,
23 | };
24 |
25 | onStartShouldSetResponder = (evt) => {
26 | if (evt.nativeEvent.touches.length === this.props.numberOfTouches) {
27 | return true;
28 | }
29 |
30 | return false;
31 | };
32 |
33 | onResponderRelease = () => {
34 | this.props.onPress();
35 | };
36 |
37 | render() {
38 | return (
39 |
43 | {this.props.children}
44 |
45 | );
46 | }
47 | }
48 |
49 | export default class App extends React.Component {
50 | render() {
51 | return (
52 |
53 | alert('double tap!')} numberOfTouches={1}>
54 | alert('box tapped!')}>
55 |
56 |
57 |
58 |
59 | );
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/tutorials/detect-multiple-touches-with-gesture-responder-system/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "detect-multiple-touches-with-gesture-responder-system",
4 | "description": "No description",
5 | "slug": "snack-HyyqO3ViX",
6 | "privacy": "unlisted",
7 | "sdkVersion": "30.0.0",
8 | "version": "1.0.0",
9 | "orientation": "portrait",
10 | "primaryColor": "#cccccc",
11 | "icon": "https://d1wp6m56sqw74a.cloudfront.net/~assets/c9aa1be8a6a6fe81e20c3ac4106a2ebc",
12 | "loading": {
13 | "icon": "https://d1wp6m56sqw74a.cloudfront.net/~assets/c9aa1be8a6a6fe81e20c3ac4106a2ebc",
14 | "hideExponentText": false
15 | },
16 | "packagerOpts": {
17 | "assetExts": [
18 | "ttf",
19 | "mp4",
20 | "otf",
21 | "xml"
22 | ]
23 | },
24 | "ios": {
25 | "supportsTablet": true
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/tutorials/detect-multiple-touches-with-gesture-responder-system/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "detect-multiple-touches-with-gesture-responder-system",
3 | "version": "0.0.0",
4 | "description": "No description",
5 | "author": null,
6 | "private": true,
7 | "main": "node_modules/expo/AppEntry.js",
8 | "dependencies": {
9 | "expo": "^30.0.0",
10 | "react": "16.3.1",
11 | "react-native": "https://github.com/expo/react-native/archive/sdk-30.0.0.tar.gz",
12 | "react-native-elements": "0.19.1"
13 | }
14 | }
--------------------------------------------------------------------------------
/tutorials/flexbox-basics/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"],
3 | "env": {
4 | "development": {
5 | "plugins": ["transform-react-jsx-source"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/tutorials/flexbox-basics/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 |
--------------------------------------------------------------------------------
/tutorials/flexbox-basics/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/tutorials/flexbox-basics/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | StyleSheet,
4 | Text,
5 | View,
6 | Image,
7 | } from 'react-native';
8 |
9 | const styles = StyleSheet.create({
10 | container: {
11 | backgroundColor: '#fff',
12 | flex: 1,
13 | alignItems: 'center',
14 | flexDirection: 'row',
15 | justifyContent: 'space-around',
16 | },
17 | image: {
18 | width: 250,
19 | height: 250,
20 | borderRadius: 125,
21 | borderWidth: 5,
22 | borderColor: '#4cfcf3',
23 | marginBottom: 20,
24 | },
25 | nameText: {
26 | fontSize: 30,
27 | fontWeight: 'bold',
28 | },
29 | });
30 |
31 | export default class App extends React.Component {
32 | render() {
33 | return (
34 |
35 |
36 |
41 | John Doe
42 | John Doe
43 |
44 |
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/tutorials/flexbox-basics/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "FlexboxBasics",
4 | "description": "This project is really great.",
5 | "slug": "FlexboxBasics",
6 | "privacy": "public",
7 | "sdkVersion": "30.0.0",
8 | "platforms": ["ios", "android"],
9 | "version": "1.0.0",
10 | "orientation": "portrait",
11 | "icon": "./assets/icon.png",
12 | "splash": {
13 | "image": "./assets/splash.png",
14 | "resizeMode": "contain",
15 | "backgroundColor": "#ffffff"
16 | },
17 | "updates": {
18 | "fallbackToCacheTimeout": 0
19 | },
20 | "assetBundlePatterns": [
21 | "**/*"
22 | ],
23 | "ios": {
24 | "supportsTablet": true
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tutorials/flexbox-basics/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/flexbox-basics/assets/icon.png
--------------------------------------------------------------------------------
/tutorials/flexbox-basics/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/flexbox-basics/assets/splash.png
--------------------------------------------------------------------------------
/tutorials/flexbox-basics/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "empty-project-template",
3 | "main": "node_modules/expo/AppEntry.js",
4 | "private": true,
5 | "scripts": {
6 | "start": "expo start",
7 | "android": "expo start --android",
8 | "ios": "expo start --ios",
9 | "eject": "expo eject"
10 | },
11 | "dependencies": {
12 | "expo": "^30.0.1",
13 | "react": "16.3.1",
14 | "react-native": "https://github.com/expo/react-native/archive/sdk-30.0.0.tar.gz"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/tutorials/global-alerts/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"],
3 | "env": {
4 | "development": {
5 | "plugins": ["transform-react-jsx-source"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/tutorials/global-alerts/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 |
--------------------------------------------------------------------------------
/tutorials/global-alerts/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/tutorials/global-alerts/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | StyleSheet,
4 | Text,
5 | View,
6 | Button,
7 | } from 'react-native';
8 | import { AlertProvider, AlertConsumer } from './GlobalAlerts';
9 |
10 | const styles = StyleSheet.create({
11 | container: {
12 | flex: 1,
13 | backgroundColor: '#fff',
14 | alignItems: 'center',
15 | justifyContent: 'center',
16 | },
17 | });
18 |
19 | class App extends React.Component {
20 | render() {
21 | return (
22 |
23 | Open up App.js to start working on your app!
24 |
59 | );
60 | }
61 | }
62 |
63 | export default () => (
64 |
95 |
96 | {({ alert }) => }
97 |
98 |
99 | );
100 |
--------------------------------------------------------------------------------
/tutorials/global-alerts/GlobalAlerts.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | View,
4 | StyleSheet,
5 | Text,
6 | TouchableWithoutFeedback,
7 | Button,
8 | Animated,
9 | Dimensions,
10 | } from 'react-native';
11 | import SafeAreaView from 'react-native-safe-area-view';
12 |
13 | const w = Dimensions.get('window');
14 |
15 | const styles = StyleSheet.create({
16 | alertContainer: {
17 | backgroundColor: '#fafbfc',
18 | paddingHorizontal: 20,
19 | paddingVertical: 10,
20 | },
21 | top: {
22 | position: 'absolute',
23 | left: 0,
24 | right: 0,
25 | top: 0,
26 | borderBottomWidth: 1,
27 | borderBottomColor: '#e1e4e8',
28 | },
29 | bottom: {
30 | position: 'absolute',
31 | left: 0,
32 | right: 0,
33 | bottom: 0,
34 | borderTopWidth: 1,
35 | borderTopColor: '#e1e4e8',
36 | },
37 | modalContainer: {
38 | flex: 1,
39 | backgroundColor: 'rgba(0, 0, 0, 0.5)',
40 | justifyContent: 'center',
41 | position: 'absolute',
42 | top: 0,
43 | bottom: 0,
44 | left: 0,
45 | right: 0,
46 | },
47 | modal: {
48 | backgroundColor: '#fafbfc',
49 | paddingHorizontal: 20,
50 | marginHorizontal: 10,
51 | },
52 | title: {
53 | fontSize: 20,
54 | },
55 | body: {
56 | fontSize: 16,
57 | },
58 | });
59 |
60 | export const AlertContext = React.createContext({});
61 |
62 | export const AlertConsumer = AlertContext.Consumer;
63 |
64 | const initialState = {
65 | visible: false,
66 | title: '',
67 | body: '',
68 | ctaOnPress: null,
69 | ctaText: '',
70 | contentHeight: w.height,
71 | theme: null,
72 | };
73 |
74 | export class AlertProvider extends React.Component {
75 | state = initialState;
76 |
77 | animatedValue = new Animated.Value(0);
78 |
79 | alert = ({
80 | title = '',
81 | body = '',
82 | display = 'bottom', // top, modal
83 | ctaText = '',
84 | ctaOnPress = null,
85 | theme = null,
86 | }) => {
87 | // alert('alert');
88 | this.setState({
89 | title,
90 | body,
91 | visible: true,
92 | display,
93 | ctaText,
94 | ctaOnPress,
95 | theme,
96 | }, () => {
97 | Animated.timing(this.animatedValue, {
98 | toValue: 1,
99 | useNativeDriver: true,
100 | duration: 150,
101 | }).start();
102 | });
103 | };
104 |
105 | close = () => {
106 | Animated.timing(this.animatedValue, {
107 | toValue: 0,
108 | useNativeDriver: true,
109 | duration: 150,
110 | }).start(() => {
111 | this.setState({ ...initialState });
112 | });
113 | }
114 |
115 | onLayout = ({ nativeEvent }) => {
116 | const height = nativeEvent.layout.height;
117 | this.setState({ contentHeight: height });
118 | }
119 |
120 | getCustomStyles = () => {
121 | const { customStyles } = this.props;
122 | const { theme } = this.state;
123 |
124 | const container = [];
125 | const text = [];
126 |
127 | if (theme) {
128 | const themeStyles = customStyles[theme];
129 | container.push(themeStyles.container);
130 | text.push(themeStyles.text);
131 | }
132 |
133 | return { container, text };
134 | }
135 |
136 | renderBody = () => {
137 | const {
138 | title,
139 | body,
140 | ctaText,
141 | ctaOnPress,
142 | } = this.state;
143 |
144 | const titleStyles = [styles.text];
145 | const bodyStyles = [styles.body];
146 |
147 | const { text } = this.getCustomStyles();
148 |
149 | titleStyles.push(text);
150 | bodyStyles.push(text);
151 |
152 | return (
153 |
154 | {title}
155 | {body}
156 | {ctaOnPress && (
157 | {
160 | ctaOnPress();
161 | this.close();
162 | }}
163 | />
164 | )}
165 |
166 | );
167 | }
168 |
169 | renderModal = () => {
170 | const modalStyles = [styles.modal, {
171 | opacity: this.animatedValue,
172 | transform: [
173 | {
174 | scale: this.animatedValue.interpolate({
175 | inputRange: [0, 1],
176 | outputRange: [0.5, 1],
177 | }),
178 | },
179 | ],
180 | }];
181 |
182 | return (
183 |
184 |
185 |
186 | {this.renderBody()}
187 |
188 |
189 |
190 | );
191 | }
192 |
193 | renderNotModal = () => {
194 | const { display, contentHeight } = this.state;
195 |
196 | const forceInset = {};
197 | const containerStyles = [styles.alertContainer];
198 |
199 | if (display === 'bottom') {
200 | containerStyles.push(styles.bottom);
201 | containerStyles.push({
202 | transform: [
203 | {
204 | translateY: this.animatedValue.interpolate({
205 | inputRange: [0, 1],
206 | outputRange: [contentHeight, 0],
207 | }),
208 | },
209 | ],
210 | });
211 | forceInset.bottom = 'always';
212 | } else if (display === 'top') {
213 | containerStyles.push(styles.top);
214 | containerStyles.push({
215 | transform: [
216 | {
217 | translateY: this.animatedValue.interpolate({
218 | inputRange: [0, 1],
219 | outputRange: [-contentHeight, 0],
220 | }),
221 | },
222 | ],
223 | });
224 | forceInset.top = 'always';
225 | }
226 |
227 | const { container } = this.getCustomStyles();
228 | containerStyles.push(container);
229 |
230 | return (
231 |
232 |
233 |
234 | {this.renderBody()}
235 |
236 |
237 |
238 | );
239 | }
240 |
241 | render() {
242 | const { visible, display } = this.state;
243 |
244 | return (
245 |
246 | {this.props.children}
247 | {visible && display === 'modal' && this.renderModal()}
248 | {visible && display !== 'modal' && this.renderNotModal()}
249 |
250 | );
251 | }
252 | }
253 |
--------------------------------------------------------------------------------
/tutorials/global-alerts/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "GlobalAlerts",
4 | "description": "This project is really great.",
5 | "slug": "GlobalAlerts",
6 | "privacy": "public",
7 | "sdkVersion": "30.0.0",
8 | "platforms": ["ios", "android"],
9 | "version": "1.0.0",
10 | "orientation": "portrait",
11 | "icon": "./assets/icon.png",
12 | "splash": {
13 | "image": "./assets/splash.png",
14 | "resizeMode": "contain",
15 | "backgroundColor": "#ffffff"
16 | },
17 | "updates": {
18 | "fallbackToCacheTimeout": 0
19 | },
20 | "assetBundlePatterns": [
21 | "**/*"
22 | ],
23 | "ios": {
24 | "supportsTablet": true
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tutorials/global-alerts/assets/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/global-alerts/assets/close.png
--------------------------------------------------------------------------------
/tutorials/global-alerts/assets/close@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/global-alerts/assets/close@2x.png
--------------------------------------------------------------------------------
/tutorials/global-alerts/assets/close@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/global-alerts/assets/close@3x.png
--------------------------------------------------------------------------------
/tutorials/global-alerts/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/global-alerts/assets/icon.png
--------------------------------------------------------------------------------
/tutorials/global-alerts/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/global-alerts/assets/splash.png
--------------------------------------------------------------------------------
/tutorials/global-alerts/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "empty-project-template",
3 | "main": "node_modules/expo/AppEntry.js",
4 | "private": true,
5 | "scripts": {
6 | "start": "expo start",
7 | "android": "expo start --android",
8 | "ios": "expo start --ios",
9 | "eject": "expo eject"
10 | },
11 | "dependencies": {
12 | "expo": "^30.0.1",
13 | "react": "16.3.1",
14 | "react-native": "https://github.com/expo/react-native/archive/sdk-30.0.0.tar.gz",
15 | "react-native-safe-area-view": "^0.11.0"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/tutorials/image-basics/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"],
3 | "env": {
4 | "development": {
5 | "plugins": ["transform-react-jsx-source"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/tutorials/image-basics/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 |
--------------------------------------------------------------------------------
/tutorials/image-basics/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/tutorials/image-basics/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | StyleSheet,
4 | View,
5 | Image,
6 | } from 'react-native';
7 |
8 | const styles = StyleSheet.create({
9 | container: {
10 | flex: 1,
11 | backgroundColor: '#7CA1B4',
12 | alignItems: 'center',
13 | justifyContent: 'center',
14 | },
15 | });
16 |
17 | export default class App extends React.Component {
18 | render() {
19 | return (
20 |
21 |
27 |
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/tutorials/image-basics/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "ImageBasics",
4 | "description": "This project is really great.",
5 | "slug": "ImageBasics",
6 | "privacy": "public",
7 | "sdkVersion": "30.0.0",
8 | "platforms": ["ios", "android"],
9 | "version": "1.0.0",
10 | "orientation": "portrait",
11 | "icon": "./assets/icon.png",
12 | "splash": {
13 | "image": "./assets/splash.png",
14 | "resizeMode": "contain",
15 | "backgroundColor": "#ffffff"
16 | },
17 | "updates": {
18 | "fallbackToCacheTimeout": 0
19 | },
20 | "assetBundlePatterns": [
21 | "**/*"
22 | ],
23 | "ios": {
24 | "supportsTablet": true
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tutorials/image-basics/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/image-basics/assets/icon.png
--------------------------------------------------------------------------------
/tutorials/image-basics/assets/rn-school-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/image-basics/assets/rn-school-logo.png
--------------------------------------------------------------------------------
/tutorials/image-basics/assets/rn-school-logo@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/image-basics/assets/rn-school-logo@2x.png
--------------------------------------------------------------------------------
/tutorials/image-basics/assets/rn-school-logo@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/image-basics/assets/rn-school-logo@3x.png
--------------------------------------------------------------------------------
/tutorials/image-basics/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/image-basics/assets/splash.png
--------------------------------------------------------------------------------
/tutorials/image-basics/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "empty-project-template",
3 | "main": "node_modules/expo/AppEntry.js",
4 | "private": true,
5 | "scripts": {
6 | "start": "expo start",
7 | "android": "expo start --android",
8 | "ios": "expo start --ios",
9 | "eject": "expo eject"
10 | },
11 | "dependencies": {
12 | "expo": "^30.0.1",
13 | "react": "16.3.1",
14 | "react-native": "https://github.com/expo/react-native/archive/sdk-30.0.0.tar.gz"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"],
3 | "env": {
4 | "development": {
5 | "plugins": ["transform-react-jsx-source"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # expo
4 | .expo/
5 |
6 | # dependencies
7 | /node_modules
8 |
9 | # misc
10 | .env.local
11 | .env.development.local
12 | .env.test.local
13 | .env.production.local
14 |
15 | npm-debug.log*
16 | yarn-debug.log*
17 | yarn-error.log*
18 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/App.js:
--------------------------------------------------------------------------------
1 | import App from './src';
2 |
3 | export default App;
4 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 |
4 | import App from './App';
5 |
6 | it('renders without crashing', () => {
7 | const rendered = renderer.create().toJSON();
8 | expect(rendered).toBeTruthy();
9 | });
10 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/README.md:
--------------------------------------------------------------------------------
1 | # Instagram Style Double Tap with React Native
2 |
3 | ## Getting Started
4 |
5 | ```
6 | $ git clone https://github.com/HandlebarLabs/react-native-examples-and-tutorials.git
7 | $ cd react-native-examples-and-tutorials/tutorials/instagram-style-double-tap
8 | $ yarn install
9 | $ yarn run ios
10 | $ yarn run android
11 | ```
12 |
13 | ## Full Tutorial
14 |
15 | You can read a full tutorial explaining the code [here](Tutorial.md).
16 |
17 | ---
18 |
19 | Find a typo? A bug? Feel free to fork, fix, and open a pull request!
20 |
21 | ---
22 |
23 | This project was bootstrapped with [Create React Native App](https://github.com/react-community/create-react-native-app).
24 |
25 | Below you'll find information about performing common tasks. The most recent version of this guide is available [here](https://github.com/react-community/create-react-native-app/blob/master/react-native-scripts/template/README.md).
26 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/Tutorial.md:
--------------------------------------------------------------------------------
1 | # Animated Image Overlay in React Native
2 |
3 | When you like a picture on Instagram a heart quickly scales and fades in over the image you liked - how would we go about accomplishing the same in React Native? In this quick tutorial we’ll cover exactly that.
4 |
5 | 
6 |
7 | ## Getting Started
8 | This tutorial builds on top of a [previous tutorial](https://github.com/HandlebarLabs/react-native-examples-and-tutorials/blob/master/tutorials/instagram-style-double-tap/Tutorial.md) in which we implement a double tap component.
9 |
10 | To get started, create a new project with Create React Native App.
11 |
12 | ```
13 | create-react-native-app AnimatedImageOverlay
14 | cd AnimatedImageOverlay
15 | ```
16 |
17 | Next, download the following zip file, unzip it, and place it in your new React Native project. Make sure to name the directory `src`.
18 |
19 | [Starting zip](tutorial-assets/src.zip)
20 |
21 | Then update `App.js` to be
22 |
23 | ```javascript
24 | import App from './src';
25 |
26 | export default App;
27 | ```
28 |
29 | ## Overlaying Images
30 |
31 | We'll start off by adding the necessary styles to successfully overlay an image on another.
32 |
33 | First we'll create a new method to render the overlayed image.
34 |
35 | ```javascript
36 | export default class App extends React.Component {
37 | // ...
38 |
39 | renderOverlay = () => {
40 | return (
41 |
42 |
46 |
47 | );
48 | }
49 |
50 | // ...
51 | }
52 | ```
53 |
54 | We'll then render the overlay. The `DoubleTap` component only accepts one child so we need to wrap our `Image` component in a `View`. Fortunantely, this helps with layout as well.
55 |
56 | ```javascript
57 | export default class App extends React.Component {
58 | // ...
59 |
60 | render() {
61 | return (
62 |
63 |
64 |
65 |
70 | {this.renderOverlay()}
71 |
72 |
73 | {/* ... */}
74 |
75 | );
76 | }
77 | }
78 | ```
79 |
80 | That leaves us with the following:
81 |
82 | 
83 |
84 | To fix this we'll setup some absolute positioning for the containing view to take up the full area and center its contents. We'll also set the `tintColor` of the image to white.
85 |
86 | ```javascript
87 | const styles = StyleSheet.create({
88 | // ...
89 | overlay: {
90 | position: 'absolute',
91 | alignItems: 'center',
92 | justifyContent: 'center',
93 | left: 0,
94 | right: 0,
95 | top: 0,
96 | bottom: 0,
97 | },
98 | overlayHeart: {
99 | tintColor: '#fff',
100 | },
101 | });
102 | ```
103 |
104 | 
105 |
106 | ## Animating the Image
107 |
108 | First thing we need to do to animate the image: import `Animated` from React Native.
109 |
110 | We'll then set up a new animated value on the component. This will drive both the opacity change and scaling thanks to our ability to interpolate animated values in React Native.
111 |
112 | ```javascript
113 | export default class App extends React.Component {
114 | // ...
115 |
116 | animatedValue = new Animated.Value(0);
117 |
118 | // ...
119 | }
120 | ```
121 |
122 | Next we'll modify the `animatedValue` when we've liked the picture. To do this we'll modify the `toggleLike` method. If the image is liked we'll start an animated sequence that brings `this.animatedValue` to a value of 1 and immediately brings it back to 0. I'm going to use `Animated.spring` because I like the way it looks here.
123 |
124 | ```javascript
125 | export default class App extends React.Component {
126 | // ...
127 |
128 | toggleLike = () => {
129 | this.setState((state) => {
130 | const newLiked = !state.liked;
131 |
132 | if (newLiked) {
133 | Animated.sequence([
134 | Animated.spring(this.animatedValue, { toValue: 1 }),
135 | Animated.spring(this.animatedValue, { toValue: 0 }),
136 | ]).start();
137 | }
138 |
139 | return { liked: newLiked };
140 | });
141 | }
142 |
143 | // ...
144 | }
145 | ```
146 |
147 | Last thing we'll do is modify the `renderOverlay` method to use our animated value to modify the styles. First, replace the `Image` component with an `Animated.Image` so that we can use animated values and the component knows what to do with them.
148 |
149 | We'll then set up an array of of our new image styles. First will be our existing `styles.overlayHeart`, next will be an object with our dynamic styles.
150 |
151 | Opacity is a value from 0 to 1 so we can just pass that directly to `opacity`. We'll then use the `scale` property to start the image slightly smaller and scale larger. We can specify the exact values by using `this.animatedValue.interpolate`. Here's the code.
152 |
153 | ```javascript
154 | export default class App extends React.Component {
155 | // ...
156 |
157 | renderOverlay = () => {
158 | const imageStyles = [
159 | styles.overlayHeart,
160 | {
161 | opacity: this.animatedValue,
162 | transform: [
163 | {
164 | scale: this.animatedValue.interpolate({
165 | inputRange: [0, 1],
166 | outputRange: [0.7, 1.5],
167 | }),
168 | },
169 | ],
170 | },
171 | ];
172 |
173 | return (
174 |
175 |
179 |
180 | );
181 | }
182 |
183 | // ...
184 | }
185 | ```
186 |
187 | That leaves us with the following.
188 |
189 | 
190 |
191 | The final code can be found on [Github](https://github.com/HandlebarLabs/react-native-examples-and-tutorials/tree/master/tutorials/instagram-style-animated-image-overlay)
192 |
193 | --
194 |
195 | > Did you enjoy this tutorial? Check out some of my [other React Native tutorials](https://github.com/HandlebarLabs/react-native-examples-and-tutorials)!
196 |
197 |
198 |
199 |
200 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "sdkVersion": "27.0.0"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "IGStyleDoubleTap",
3 | "version": "0.1.0",
4 | "private": true,
5 | "devDependencies": {
6 | "react-native-scripts": "1.14.0",
7 | "jest-expo": "~27.0.0",
8 | "react-test-renderer": "16.3.1"
9 | },
10 | "main": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
11 | "scripts": {
12 | "start": "react-native-scripts start",
13 | "eject": "react-native-scripts eject",
14 | "android": "react-native-scripts android",
15 | "ios": "react-native-scripts ios",
16 | "test": "jest"
17 | },
18 | "jest": {
19 | "preset": "jest-expo"
20 | },
21 | "dependencies": {
22 | "expo": "^27.0.1",
23 | "react": "16.3.1",
24 | "react-native": "~0.55.2"
25 | }
26 | }
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/src/DoubleTap.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { TouchableWithoutFeedback } from 'react-native';
3 |
4 | export default class DoubleTap extends React.Component {
5 | static defaultProps = {
6 | delay: 300,
7 | onDoubleTap: () => null,
8 | };
9 |
10 | lastTap = null;
11 |
12 | // https://gist.github.com/brunotavares/3c9a373ba5cd1b4ff28b
13 | handleDoubleTap = () => {
14 | const now = Date.now();
15 | if (this.lastTap && (now - this.lastTap) < this.props.delay) {
16 | this.props.onDoubleTap();
17 | } else {
18 | this.lastTap = now;
19 | }
20 | }
21 |
22 | render() {
23 | return (
24 |
25 | {this.props.children}
26 |
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/src/images/heart-outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/instagram-style-animated-image-overlay/src/images/heart-outline.png
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/src/images/heart-outline@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/instagram-style-animated-image-overlay/src/images/heart-outline@2x.png
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/src/images/heart-outline@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/instagram-style-animated-image-overlay/src/images/heart-outline@3x.png
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/src/images/heart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/instagram-style-animated-image-overlay/src/images/heart.png
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/src/images/heart@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/instagram-style-animated-image-overlay/src/images/heart@2x.png
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/src/images/heart@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/instagram-style-animated-image-overlay/src/images/heart@3x.png
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | StyleSheet,
4 | View,
5 | Dimensions,
6 | Image,
7 | TouchableOpacity,
8 | Animated,
9 | } from 'react-native';
10 |
11 | import DoubleTap from './DoubleTap';
12 |
13 | const w = Dimensions.get('window');
14 |
15 | const styles = StyleSheet.create({
16 | container: {
17 | flex: 1,
18 | backgroundColor: '#fff',
19 | alignItems: 'center',
20 | justifyContent: 'center',
21 | },
22 | iconRow: {
23 | flexDirection: 'row',
24 | alignSelf: 'stretch',
25 | marginTop: 10,
26 | paddingVertical: 5,
27 | paddingHorizontal: 15,
28 | },
29 | heartIcon: {
30 | width: 20,
31 | height: 20,
32 | },
33 | overlay: {
34 | position: 'absolute',
35 | alignItems: 'center',
36 | justifyContent: 'center',
37 | left: 0,
38 | right: 0,
39 | top: 0,
40 | bottom: 0,
41 | },
42 | overlayHeart: {
43 | tintColor: '#fff',
44 | },
45 | });
46 |
47 | export default class App extends React.Component {
48 | state = {
49 | liked: false,
50 | };
51 |
52 | animatedValue = new Animated.Value(0);
53 |
54 | toggleLike = () => {
55 | this.setState((state) => {
56 | const newLiked = !state.liked;
57 |
58 | if (newLiked) {
59 | Animated.sequence([
60 | Animated.spring(this.animatedValue, { toValue: 1 }),
61 | Animated.spring(this.animatedValue, { toValue: 0 }),
62 | ]).start();
63 | }
64 |
65 | return { liked: newLiked };
66 | });
67 | }
68 |
69 | renderOverlay = () => {
70 | const imageStyles = [
71 | styles.overlayHeart,
72 | {
73 | opacity: this.animatedValue,
74 | transform: [
75 | {
76 | scale: this.animatedValue.interpolate({
77 | inputRange: [0, 1],
78 | outputRange: [0.7, 1.5],
79 | }),
80 | },
81 | ],
82 | },
83 | ];
84 |
85 | return (
86 |
87 |
91 |
92 | );
93 | }
94 |
95 | render() {
96 | return (
97 |
98 |
99 |
100 |
105 | {this.renderOverlay()}
106 |
107 |
108 |
109 |
110 |
115 |
116 |
117 |
118 | );
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/tutorial-assets/01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/instagram-style-animated-image-overlay/tutorial-assets/01.png
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/tutorial-assets/02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/instagram-style-animated-image-overlay/tutorial-assets/02.png
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/tutorial-assets/03.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/instagram-style-animated-image-overlay/tutorial-assets/03.gif
--------------------------------------------------------------------------------
/tutorials/instagram-style-animated-image-overlay/tutorial-assets/src.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/instagram-style-animated-image-overlay/tutorial-assets/src.zip
--------------------------------------------------------------------------------
/tutorials/instagram-style-double-tap/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"],
3 | "env": {
4 | "development": {
5 | "plugins": ["transform-react-jsx-source"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-double-tap/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # expo
4 | .expo/
5 |
6 | # dependencies
7 | /node_modules
8 |
9 | # misc
10 | .env.local
11 | .env.development.local
12 | .env.test.local
13 | .env.production.local
14 |
15 | npm-debug.log*
16 | yarn-debug.log*
17 | yarn-error.log*
18 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-double-tap/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-double-tap/App.js:
--------------------------------------------------------------------------------
1 | import App from './src';
2 |
3 | export default App;
4 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-double-tap/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 |
4 | import App from './App';
5 |
6 | it('renders without crashing', () => {
7 | const rendered = renderer.create().toJSON();
8 | expect(rendered).toBeTruthy();
9 | });
10 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-double-tap/README.md:
--------------------------------------------------------------------------------
1 | # Instagram Style Double Tap with React Native
2 |
3 | ## Getting Started
4 |
5 | ```
6 | $ git clone https://github.com/HandlebarLabs/react-native-examples-and-tutorials.git
7 | $ cd react-native-examples-and-tutorials/tutorials/instagram-style-double-tap
8 | $ yarn install
9 | $ yarn run ios
10 | $ yarn run android
11 | ```
12 |
13 | ## Full Tutorial
14 |
15 | You can read a full tutorial explaining the code [here](Tutorial.md).
16 |
17 | ---
18 |
19 | Find a typo? A bug? Feel free to fork, fix, and open a pull request!
20 |
21 | ---
22 |
23 | This project was bootstrapped with [Create React Native App](https://github.com/react-community/create-react-native-app).
24 |
25 | Below you'll find information about performing common tasks. The most recent version of this guide is available [here](https://github.com/react-community/create-react-native-app/blob/master/react-native-scripts/template/README.md).
26 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-double-tap/Tutorial.md:
--------------------------------------------------------------------------------
1 | # Instagram Style Double Tap with React Native
2 | There are a host of ways to detect a tap in React Native - but how do you detect a double tap? Detecting double taps has a host of uses - like liking a picture, causing a scroll to top in a tab, etc. Let's set up double tap detection.
3 |
4 | 
5 |
6 |
7 | We’ll start with a basic application that allows us to like a picture.
8 |
9 | First create the app
10 |
11 | ```
12 | create-react-native-app IGStyleDoubleTap
13 | ```
14 |
15 | Then set up our app structure.
16 |
17 | ```
18 | cd IGStyleDoubleTap
19 | mkdir src
20 | mkdir src/images
21 | touch src/index.js
22 | ```
23 |
24 | You’ll then want to download these [heart icons](src/images) and put them in the `src/images` directory.
25 |
26 | Next, paste the following in `src/index.js`.
27 |
28 | ```javascript
29 | import React from 'react';
30 | import { StyleSheet, Text, View, Dimensions, Image, TouchableOpacity } from 'react-native';
31 |
32 | const w = Dimensions.get('window');
33 |
34 | export default class App extends React.Component {
35 | state = {
36 | liked: false,
37 | };
38 |
39 | toggleLike = () => this.setState(state => ({ liked: !state.liked }));
40 |
41 | render() {
42 | return (
43 |
44 |
49 |
50 |
51 |
56 |
57 |
58 |
59 | );
60 | }
61 | }
62 |
63 | const styles = StyleSheet.create({
64 | container: {
65 | flex: 1,
66 | backgroundColor: '#fff',
67 | alignItems: 'center',
68 | justifyContent: 'center',
69 | },
70 | iconRow: {
71 | flexDirection: 'row',
72 | alignSelf: 'stretch',
73 | marginTop: 10,
74 | paddingVertical: 5,
75 | paddingHorizontal: 15,
76 | },
77 | heartIcon: {
78 | width: 20,
79 | height: 20,
80 | },
81 | });
82 | ```
83 |
84 | Finally, replace `App.js` with the following
85 |
86 | ```javascript
87 | import App from './src';
88 |
89 | export default App;
90 | ```
91 |
92 | With that you can `yarn run ios` or `yarn run android` and you can like a picture by pressing the heart icon below the picture.
93 |
94 | With the setup complete we can actually start the implementation.
95 |
96 | ## Detecting a Double Tap
97 | This code is inspired by [this gist](https://gist.github.com/brunotavares/3c9a373ba5cd1b4ff28b) by Github user [brunotavares](https://github.com/brunotavares).
98 |
99 | First, we’ll wrap our `Image` component in a `TouchableWithoutFeedback` component, allowing us to detect the tap(s).
100 |
101 | On our component we’ll track the last tap in a variable named `lastTap`. We’re not using component stated here as it doesn’t affect rendering in any way. It can default to a `null` value.
102 |
103 | ```javascript
104 | export default class App extends React.Component {
105 | // ...
106 | lastTap = null;
107 | // ...
108 | }
109 | ```
110 |
111 | Next, on our component we can set up a `handleDoubleTap` function in which we’ll compare the current tap to the last tap and, if it’s within the double tap threshold, call the appropriate function.
112 |
113 | We’ll use `Date.now()` to compare values. `Date.now()` returns the milliseconds elapsed since January 1, 1970, 00:00:00 UTC which will work perfectly since our delay will be millisecond based.
114 |
115 | Upon a successful double tap we’ll call the `this.toggleLike()` function otherwise we’ll update the `this.lastTap` value to be the result of `Date.now()`.
116 |
117 | ```javascript
118 | export default class App extends React.Component {
119 | // ...
120 | lastTap = null;
121 | handleDoubleTap = () => {
122 | const now = Date.now();
123 | const DOUBLE_PRESS_DELAY = 300;
124 | if (this.lastTap && (now - this.lastTap) < DOUBLE_PRESS_DELAY) {
125 | this.toggleLike();
126 | } else {
127 | this.lastTap = now;
128 | }
129 | }
130 | // ...
131 | }
132 | ```
133 |
134 | We then need to wrap our `Image` component in a `TouchableWithoutFeedback` (make sure to import it from React Native!) to detect the tap(s).
135 |
136 | ```javacript
137 | render() {
138 | return (
139 | {/* ... */}
140 |
141 |
146 |
147 | {/* ... */}
148 | );
149 | }
150 | ```
151 |
152 | This results in the following
153 |
154 | 
155 |
156 |
157 | ## Refactoring the Double Tap Functionality to Its Own Component
158 | What we have now works perfectly fine - but it’s not very reusable. Lets refactor it into its own component so we can reuse it easily.
159 |
160 | First, create a new file for the component.
161 |
162 | ```
163 | touch src/DoubleTap.js
164 | ```
165 |
166 | And paste the following to get started
167 |
168 | ```javascript
169 | import React from 'react';
170 | import { TouchableWithoutFeedback } from 'react-native';
171 |
172 | export default class DoubleTap extends React.Component {
173 | render() {
174 |
175 | }
176 | };
177 | ```
178 |
179 | We can then go ahead and start moving logic over from `src/index.js`. First `render`.
180 |
181 | ```javascript
182 | import React from 'react';
183 | import { TouchableWithoutFeedback } from 'react-native';
184 |
185 | export default class DoubleTap extends React.Component {
186 | render() {
187 | return (
188 |
189 | {this.props.children}
190 |
191 | );
192 | }
193 | };
194 | ```
195 |
196 | The only difference from before is that we’re now rendering `this.props.children` inside of the `TouchableWithoutFeedback` so we can pass any component to it.
197 |
198 | Next up let’s set up `handleDoubleTap`.
199 |
200 | ```javascript
201 | export default class DoubleTap extends React.Component {
202 | lastTap = null;
203 | handleDoubleTap = () => {
204 | const now = Date.now();
205 | if (this.lastTap && (now - this.lastTap) < this.props.delay) {
206 | this.props.onDoubleTap();
207 | } else {
208 | this.lastTap = now;
209 | }
210 | }
211 |
212 | // ...
213 | };
214 | ```
215 |
216 | You can notice two differences here. First, instead of setting a state variable for our delay we’re accessing it via `this.props.delay`. That way the consumer of the component can override it. Second, we’re calling `this.props.onDoubleTap()` so the consumer can pass their own function down to the component.
217 |
218 | Finally, we’ll set some default props so our component works regardless of if the consumer passes props.
219 |
220 | ```javascript
221 | export default class DoubleTap extends React.Component {
222 | static defaultProps = {
223 | delay: 300,
224 | onDoubleTap: () => null,
225 | };
226 |
227 | // ...
228 | };
229 | ```
230 |
231 | Now let’s go ahead and use the new component. In `src/index.js`.
232 |
233 | ```javascript
234 | import DoubleTap from './DoubleTap';
235 |
236 | // ...
237 |
238 | render() {
239 | return (
240 | {/* ... */}
241 |
242 |
247 |
248 | {/* ... */}
249 | );
250 | }
251 | ```
252 |
253 | - - - -
254 |
255 | And there you have it! How to build an Instagram style double tap to like as well as how to refactor you code into reusable components?
256 |
257 | The full source code is available on [Github](https://github.com/HandlebarLabs/react-native-examples-and-tutorials/blob/master/tutorials/instagram-style-double-tap/).
258 |
259 | > Did you enjoy this tutorial? Check out other React Native tutorials [here](https://github.com/HandlebarLabs/react-native-examples-and-tutorials)!
260 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-double-tap/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "sdkVersion": "27.0.0"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-double-tap/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "IGStyleDoubleTap",
3 | "version": "0.1.0",
4 | "private": true,
5 | "devDependencies": {
6 | "react-native-scripts": "1.14.0",
7 | "jest-expo": "~27.0.0",
8 | "react-test-renderer": "16.3.1"
9 | },
10 | "main": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
11 | "scripts": {
12 | "start": "react-native-scripts start",
13 | "eject": "react-native-scripts eject",
14 | "android": "react-native-scripts android",
15 | "ios": "react-native-scripts ios",
16 | "test": "jest"
17 | },
18 | "jest": {
19 | "preset": "jest-expo"
20 | },
21 | "dependencies": {
22 | "expo": "^27.0.1",
23 | "react": "16.3.1",
24 | "react-native": "~0.55.2"
25 | }
26 | }
--------------------------------------------------------------------------------
/tutorials/instagram-style-double-tap/src/DoubleTap.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { TouchableWithoutFeedback } from 'react-native';
3 |
4 | export default class DoubleTap extends React.Component {
5 | static defaultProps = {
6 | delay: 300,
7 | onDoubleTap: () => null,
8 | };
9 |
10 | lastTap = null;
11 |
12 | // https://gist.github.com/brunotavares/3c9a373ba5cd1b4ff28b
13 | handleDoubleTap = () => {
14 | const now = Date.now();
15 | if (this.lastTap && (now - this.lastTap) < this.props.delay) {
16 | this.props.onDoubleTap();
17 | } else {
18 | this.lastTap = now;
19 | }
20 | }
21 |
22 | render() {
23 | return (
24 |
25 | {this.props.children}
26 |
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-double-tap/src/images/heart-outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/instagram-style-double-tap/src/images/heart-outline.png
--------------------------------------------------------------------------------
/tutorials/instagram-style-double-tap/src/images/heart-outline@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/instagram-style-double-tap/src/images/heart-outline@2x.png
--------------------------------------------------------------------------------
/tutorials/instagram-style-double-tap/src/images/heart-outline@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/instagram-style-double-tap/src/images/heart-outline@3x.png
--------------------------------------------------------------------------------
/tutorials/instagram-style-double-tap/src/images/heart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/instagram-style-double-tap/src/images/heart.png
--------------------------------------------------------------------------------
/tutorials/instagram-style-double-tap/src/images/heart@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/instagram-style-double-tap/src/images/heart@2x.png
--------------------------------------------------------------------------------
/tutorials/instagram-style-double-tap/src/images/heart@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/instagram-style-double-tap/src/images/heart@3x.png
--------------------------------------------------------------------------------
/tutorials/instagram-style-double-tap/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | StyleSheet,
4 | View,
5 | Dimensions,
6 | Image,
7 | TouchableOpacity,
8 | } from 'react-native';
9 |
10 | import DoubleTap from './DoubleTap';
11 |
12 | const w = Dimensions.get('window');
13 |
14 | const styles = StyleSheet.create({
15 | container: {
16 | flex: 1,
17 | backgroundColor: '#fff',
18 | alignItems: 'center',
19 | justifyContent: 'center',
20 | },
21 | iconRow: {
22 | flexDirection: 'row',
23 | alignSelf: 'stretch',
24 | marginTop: 10,
25 | paddingVertical: 5,
26 | paddingHorizontal: 15,
27 | },
28 | heartIcon: {
29 | width: 20,
30 | height: 20,
31 | },
32 | });
33 |
34 | export default class App extends React.Component {
35 | state = {
36 | liked: false,
37 | };
38 |
39 | toggleLike = () => this.setState(state => ({ liked: !state.liked }));
40 |
41 | render() {
42 | return (
43 |
44 |
45 |
50 |
51 |
52 |
53 |
58 |
59 |
60 |
61 | );
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/tutorials/instagram-style-double-tap/tutorial-assets/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/instagram-style-double-tap/tutorial-assets/demo.gif
--------------------------------------------------------------------------------
/tutorials/long-press-with-gesture-responder-system/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, TouchableHighlight } from 'react-native';
3 |
4 | const styles = StyleSheet.create({
5 | container: {
6 | flex: 1,
7 | backgroundColor: '#fff',
8 | alignItems: 'center',
9 | justifyContent: 'center',
10 | },
11 | box: {
12 | backgroundColor: '#cc0000',
13 | width: 200,
14 | height: 200,
15 | borderRadius: 5,
16 | },
17 | });
18 |
19 | class MultiTap extends React.Component {
20 | static defaultProps = {
21 | onPress: () => null,
22 | numberOfTouches: 2,
23 | delay: 1000,
24 | }
25 |
26 | startPress = null;
27 |
28 | onStartShouldSetResponder = (evt) => {
29 | if (evt.nativeEvent.touches.length === this.props.numberOfTouches) {
30 | this.startPress = Date.now();
31 | return true;
32 | }
33 |
34 | return false;
35 | }
36 |
37 | onResponderRelease = () => {
38 | const now = Date.now();
39 | if (this.startPress && now - this.startPress > this.props.delay) {
40 | this.props.onPress();
41 | }
42 | this.startPress = null;
43 | }
44 |
45 | render() {
46 | return (
47 |
51 | {this.props.children}
52 |
53 | );
54 | }
55 | }
56 |
57 | export default class App extends React.Component {
58 | render() {
59 | return (
60 |
61 | alert('double tap!')}>
62 | alert('box tapped!')}>
63 |
64 |
65 |
66 |
67 | );
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/tutorials/long-press-with-gesture-responder-system/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "long-press-with-gesture-responder-system",
4 | "description": "No description",
5 | "slug": "snack-SyZLKSAqX",
6 | "privacy": "unlisted",
7 | "sdkVersion": "30.0.0",
8 | "version": "1.0.0",
9 | "orientation": "portrait",
10 | "primaryColor": "#cccccc",
11 | "icon": "https://d1wp6m56sqw74a.cloudfront.net/~assets/c9aa1be8a6a6fe81e20c3ac4106a2ebc",
12 | "loading": {
13 | "icon": "https://d1wp6m56sqw74a.cloudfront.net/~assets/c9aa1be8a6a6fe81e20c3ac4106a2ebc",
14 | "hideExponentText": false
15 | },
16 | "packagerOpts": {
17 | "assetExts": [
18 | "ttf",
19 | "mp4",
20 | "otf",
21 | "xml"
22 | ]
23 | },
24 | "ios": {
25 | "supportsTablet": true
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/tutorials/long-press-with-gesture-responder-system/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "long-press-with-gesture-responder-system",
3 | "version": "0.0.0",
4 | "description": "No description",
5 | "author": null,
6 | "private": true,
7 | "main": "node_modules/expo/AppEntry.js",
8 | "dependencies": {
9 | "expo": "^30.0.0",
10 | "react": "16.3.1",
11 | "react-native": "https://github.com/expo/react-native/archive/sdk-30.0.0.tar.gz",
12 | "react-native-elements": "0.19.1"
13 | }
14 | }
--------------------------------------------------------------------------------
/tutorials/multi-theme-button/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"],
3 | "env": {
4 | "development": {
5 | "plugins": ["transform-react-jsx-source"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/tutorials/multi-theme-button/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # expo
4 | .expo/
5 |
6 | # dependencies
7 | /node_modules
8 |
9 | # misc
10 | .env.local
11 | .env.development.local
12 | .env.test.local
13 | .env.production.local
14 |
15 | npm-debug.log*
16 | yarn-debug.log*
17 | yarn-error.log*
18 |
--------------------------------------------------------------------------------
/tutorials/multi-theme-button/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/tutorials/multi-theme-button/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, SafeAreaView } from 'react-native';
3 |
4 | import Button from './Button';
5 |
6 | const styles = StyleSheet.create({
7 | container: {
8 | backgroundColor: '#fff',
9 | },
10 | });
11 | export default class App extends React.Component {
12 | render() {
13 | return (
14 |
15 | null}
18 | />
19 | null}
23 | />
24 | null}
29 | />
30 | null}
36 | />
37 |
38 |
39 | );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/tutorials/multi-theme-button/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const rendered = renderer.create().toJSON();
7 | expect(rendered).toBeTruthy();
8 | });
9 |
--------------------------------------------------------------------------------
/tutorials/multi-theme-button/Button.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { TouchableOpacity, Text, StyleSheet } from 'react-native';
4 |
5 | const PRIMARY_COLOR = '#007bff';
6 | const SECONDARY_COLOR = '#6c757d';
7 |
8 | const styles = StyleSheet.create({
9 | // Container Styles
10 | containerDefault: {
11 | alignItems: 'center',
12 | paddingVertical: 10,
13 | borderWidth: 1,
14 | borderRadius: 2,
15 | marginHorizontal: 20,
16 | marginVertical: 10,
17 | },
18 | containerPrimary: {
19 | backgroundColor: PRIMARY_COLOR,
20 | borderColor: PRIMARY_COLOR,
21 | },
22 | containerPrimaryOutline: {
23 | backgroundColor: 'transparent',
24 | },
25 | containerSecondary: {
26 | backgroundColor: SECONDARY_COLOR,
27 | borderColor: SECONDARY_COLOR,
28 | },
29 | containerSecondaryOutline: {
30 | backgroundColor: 'transparent',
31 | },
32 | containerLarge: {
33 | paddingVertical: 15,
34 | },
35 | containerSmall: {
36 | paddingVertical: 5,
37 | },
38 | containerDisabled: {
39 | opacity: 0.65,
40 | },
41 |
42 | // TextStyles
43 | textDefault: {
44 | fontSize: 16,
45 | fontWeight: '500',
46 | color: '#fff',
47 | },
48 | textPrimary: {
49 |
50 | },
51 | textPrimaryOutline: {
52 | color: PRIMARY_COLOR,
53 | },
54 | textSecondary: {
55 |
56 | },
57 | textSecondaryOutline: {
58 | color: SECONDARY_COLOR,
59 | },
60 | textLarge: {
61 | fontSize: 20,
62 | },
63 | textSmall: {
64 | fontSize: 14,
65 | },
66 | textDisabled: {
67 |
68 | },
69 | });
70 |
71 | const getStyles = ({
72 | size,
73 | theme,
74 | outline,
75 | disabled,
76 | }) => {
77 | const containerStyles = [styles.containerDefault];
78 | const textStyles = [styles.textDefault];
79 |
80 | if (size === 'large') {
81 | containerStyles.push(styles.containerLarge);
82 | textStyles.push(styles.textLarge);
83 | } else if (size === 'small') {
84 | containerStyles.push(styles.containerSmall);
85 | textStyles.push(styles.textSmall);
86 | }
87 |
88 | if (theme === 'secondary') {
89 | containerStyles.push(styles.containerSecondary);
90 | textStyles.push(styles.textSecondary);
91 |
92 | if (outline) {
93 | containerStyles.push(styles.containerSecondaryOutline);
94 | textStyles.push(styles.textSecondaryOutline);
95 | }
96 | } else {
97 | containerStyles.push(styles.containerPrimary);
98 | textStyles.push(styles.textPrimary);
99 |
100 | if (outline) {
101 | containerStyles.push(styles.containerPrimaryOutline);
102 | textStyles.push(styles.textPrimaryOutline);
103 | }
104 | }
105 |
106 | if (disabled) {
107 | containerStyles.push(styles.containerDisabled);
108 | textStyles.push(styles.textDisabled);
109 | }
110 |
111 | return { textStyles, containerStyles };
112 | };
113 |
114 | class Button extends React.Component {
115 | static propTypes = {
116 | text: PropTypes.string.isRequired,
117 | onPress: PropTypes.func.isRequired,
118 | size: PropTypes.oneOf(['small', 'default', 'large']),
119 | theme: PropTypes.oneOf(['primary', 'secondary']),
120 | outline: PropTypes.bool,
121 | disabled: PropTypes.bool,
122 | };
123 |
124 | static defaultProps = {
125 | size: 'default',
126 | theme: 'primary',
127 | outline: false,
128 | disabled: false,
129 | };
130 |
131 | render() {
132 | const {
133 | onPress,
134 | text,
135 | disabled,
136 | ...rest
137 | } = this.props;
138 | const { textStyles, containerStyles } = getStyles({ disabled, ...rest });
139 |
140 | return (
141 |
142 | {text}
143 |
144 | );
145 | }
146 | }
147 |
148 | export default Button;
149 |
--------------------------------------------------------------------------------
/tutorials/multi-theme-button/README.md:
--------------------------------------------------------------------------------
1 | # Building a React Native Multi-Theme Button
2 |
3 | ## Getting Started
4 |
5 | ```
6 | $ git clone https://github.com/HandlebarLabs/react-native-examples-and-tutorials.git
7 | $ cd react-native-examples-and-tutorials/tutorials/multi-theme-button
8 | $ yarn install
9 | $ yarn run ios
10 | $ yarn run android
11 | ```
12 |
13 | ## Full Tutorial
14 |
15 | You can watch a full tutorial explaining the code [here](https://www.youtube.com/watch?v=UUcaqUUeHWEs).
16 |
17 | ---
18 |
19 | Find a typo? A bug? Feel free to fork, fix, and open a pull request!
20 |
21 | ---
22 |
23 | This project was bootstrapped with [Create React Native App](https://github.com/react-community/create-react-native-app).
24 |
25 | Below you'll find information about performing common tasks. The most recent version of this guide is available [here](https://github.com/react-community/create-react-native-app/blob/master/react-native-scripts/template/README.md).
26 |
--------------------------------------------------------------------------------
/tutorials/multi-theme-button/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "sdkVersion": "27.0.0"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/tutorials/multi-theme-button/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "MultiThemeButton",
3 | "version": "0.1.0",
4 | "private": true,
5 | "devDependencies": {
6 | "jest-expo": "~27.0.0",
7 | "react-native-scripts": "1.14.0",
8 | "react-test-renderer": "16.3.1"
9 | },
10 | "main": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
11 | "scripts": {
12 | "start": "react-native-scripts start",
13 | "eject": "react-native-scripts eject",
14 | "android": "react-native-scripts android",
15 | "ios": "react-native-scripts ios",
16 | "test": "jest"
17 | },
18 | "jest": {
19 | "preset": "jest-expo"
20 | },
21 | "dependencies": {
22 | "expo": "^27.0.1",
23 | "prop-types": "^15.6.2",
24 | "react": "16.3.1",
25 | "react-native": "~0.55.2"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tutorials/progressive-image-loading/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"],
3 | "env": {
4 | "development": {
5 | "plugins": ["transform-react-jsx-source"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/tutorials/progressive-image-loading/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # expo
4 | .expo/
5 |
6 | # dependencies
7 | /node_modules
8 |
9 | # misc
10 | .env.local
11 | .env.development.local
12 | .env.test.local
13 | .env.production.local
14 |
15 | npm-debug.log*
16 | yarn-debug.log*
17 | yarn-error.log*
18 |
--------------------------------------------------------------------------------
/tutorials/progressive-image-loading/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/tutorials/progressive-image-loading/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, Dimensions } from 'react-native';
3 | import ProgressiveImage from './ProgressiveImage';
4 |
5 | const w = Dimensions.get('window');
6 |
7 | const styles = StyleSheet.create({
8 | container: {
9 | flex: 1,
10 | alignItems: 'center',
11 | justifyContent: 'center',
12 | },
13 | });
14 |
15 | export default class App extends React.Component {
16 | render() {
17 | return (
18 |
19 |
25 |
26 | );
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/tutorials/progressive-image-loading/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const rendered = renderer.create().toJSON();
7 | expect(rendered).toBeTruthy();
8 | });
9 |
--------------------------------------------------------------------------------
/tutorials/progressive-image-loading/ProgressiveImage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View, StyleSheet, Animated } from 'react-native';
3 |
4 | const styles = StyleSheet.create({
5 | imageOverlay: {
6 | position: 'absolute',
7 | left: 0,
8 | right: 0,
9 | bottom: 0,
10 | top: 0,
11 | },
12 | container: {
13 | backgroundColor: '#e1e4e8',
14 | },
15 | });
16 |
17 | class ProgressiveImage extends React.Component {
18 | thumbnailAnimated = new Animated.Value(0);
19 |
20 | imageAnimated = new Animated.Value(0);
21 |
22 | handleThumbnailLoad = () => {
23 | Animated.timing(this.thumbnailAnimated, {
24 | toValue: 1,
25 | }).start();
26 | }
27 |
28 | onImageLoad = () => {
29 | Animated.timing(this.imageAnimated, {
30 | toValue: 1,
31 | }).start();
32 | }
33 |
34 | render() {
35 | const {
36 | thumbnailSource,
37 | source,
38 | style,
39 | ...props
40 | } = this.props;
41 |
42 | return (
43 |
44 |
51 |
57 |
58 | );
59 | }
60 | }
61 |
62 | export default ProgressiveImage;
63 |
--------------------------------------------------------------------------------
/tutorials/progressive-image-loading/README.md:
--------------------------------------------------------------------------------
1 | # Progressive Image Loading in React Native
2 |
3 | ## Getting Started
4 |
5 | ```
6 | $ git clone https://github.com/HandlebarLabs/react-native-examples-and-tutorials.git
7 | $ cd react-native-examples-and-tutorials/tutorials/progressive-image-loading
8 | $ yarn install
9 | $ yarn run ios
10 | $ yarn run android
11 | ```
12 |
13 | ## Full Tutorial
14 |
15 | You can read a full tutorial explaining the code [here](Tutorial.md).
16 |
17 | ---
18 |
19 | Find a typo? A bug? Feel free to fork, fix, and open a pull request!
20 |
21 | ---
22 |
23 | This project was bootstrapped with [Create React Native App](https://github.com/react-community/create-react-native-app).
24 |
25 | Below you'll find information about performing common tasks. The most recent version of this guide is available [here](https://github.com/react-community/create-react-native-app/blob/master/react-native-scripts/template/README.md).
26 |
--------------------------------------------------------------------------------
/tutorials/progressive-image-loading/Tutorial.md:
--------------------------------------------------------------------------------
1 | # Progressive Image Loading in React Native
2 |
3 | Internet connection speeds are variable, especially when you're working with mobile devices. As developers we often forget that many of our users are running our apps on less powerful devices and with slower internet connections than we are. Go out into the mountains and try to access your app and see how it performs.
4 |
5 | One of the more expensive things in an app is the remote images you're loading. They're going to take time to load, especially if they're large images.
6 |
7 | Today we'll build a component that allows us to:
8 |
9 | 1. Pass a full size image to display (just like a normal `Image` component)
10 | 2. Pass a thumbnail image to display while we're loading the full size image
11 | 3. Automatically display a placeholder in the soon-to-be-downloaded image's place to indicate that something will be there
12 | 4. Animate between each state as we go.
13 |
14 | ## Getting Started
15 |
16 | To start create a new React Native app (via `react-native init`, `create-react-native-app`, or the `expo` cli) and add the following to `App.js`.
17 |
18 | ```javascript
19 | import React from 'react';
20 | import { StyleSheet, View, Dimensions, Image } from 'react-native';
21 |
22 | const w = Dimensions.get('window');
23 |
24 | const styles = StyleSheet.create({
25 | container: {
26 | flex: 1,
27 | alignItems: 'center',
28 | justifyContent: 'center',
29 | },
30 | });
31 |
32 | export default class App extends React.Component {
33 | render() {
34 | return (
35 |
36 |
41 |
42 | );
43 | }
44 | }
45 | ```
46 |
47 | That block of code simply displays an image. It's requesting an image from [Pexels](https://www.pexels.com/) at 2x the size of the screen (we want big images so they load slowly) and the `buster` query param will help us not cache the image so we can fully see what's going on. You wouldn't want to do this in your actual app.
48 |
49 | ## Feel the Pain
50 |
51 | Like I said before - as a developer you've probably got a pretty decent internet connection.
52 |
53 | Let's change that.
54 |
55 | If you're on a Mac you can install a tool called Network Link Conditioner ([here's how to install it](https://nshipster.com/network-link-conditioner/)). I'm sure there is similar for my Windows and Linux fans (chime in below if you have tool suggestions).
56 |
57 | It will allow you to simulate just about any network condition you need. Just remember to turn it off before you start trying to stream video.
58 |
59 | Enable it and set it to the "3G" profile.
60 |
61 | 
62 |
63 | Here's a comparison of the starter with Network Link Conditioner off vs. on.
64 |
65 | 
66 | 
67 |
68 | ## ProgressiveImage Component
69 |
70 | To replace the `Image` component we'll create a new component called `ProgressiveImage`. The goal with this component is to allow it to work exactly like the normal `Image` component just with some extra features:
71 |
72 | 1. Colored background filling in where the image will be
73 | 2. Ability to pass a thumbnail image
74 |
75 | First let's put the foundation in place:
76 |
77 | ```javascript
78 | // ProgressiveImage.js
79 |
80 | import React from 'react';
81 | import { View, StyleSheet, Image } from 'react-native';
82 |
83 | const styles = StyleSheet.create({
84 |
85 | });
86 |
87 | class ProgressiveImage extends React.Component {
88 | render() {
89 | return
90 | }
91 | }
92 |
93 | export default ProgressiveImage;
94 | ```
95 |
96 | We're using the spread syntax to pass all of `this.props` down to the `Image` component so that everything works as expected without us having to manually define each and every prop.
97 |
98 | Then replace `Image` in `App.js` with the new `ProgressiveImage` component.
99 |
100 | ```javascript
101 | // App.js
102 |
103 | import React from 'react';
104 | import { StyleSheet, View, Dimensions } from 'react-native';
105 | import ProgressiveImage from './ProgressiveImage';
106 |
107 | // ...
108 |
109 | export default class App extends React.Component {
110 | render() {
111 | return (
112 |
113 |
118 |
119 | );
120 | }
121 | }
122 | ```
123 |
124 | Everything should work exactly the same as it did before.
125 |
126 | ## Setting the Background Color
127 |
128 | When you're loading a remote image you need to specify the width and height of the image for it to render. We'll leverage that requirement to allow us to easily set a default background color.
129 |
130 | ```javascript
131 | import React from 'react';
132 | import { View, StyleSheet, Image } from 'react-native';
133 |
134 | const styles = StyleSheet.create({
135 | imageOverlay: {
136 | position: 'absolute',
137 | left: 0,
138 | right: 0,
139 | bottom: 0,
140 | top: 0,
141 | },
142 | container: {
143 | backgroundColor: '#e1e4e8',
144 | },
145 | });
146 |
147 | class ProgressiveImage extends React.Component {
148 | render() {
149 | return (
150 |
151 |
152 |
153 | );
154 | }
155 | }
156 |
157 | export default ProgressiveImage;
158 | ```
159 |
160 | First we create a `container` style with our background color and then wrap the `Image` component in a `View` (with the new style assigned to it).
161 |
162 | This gives us the first phase of our progressive image loading.
163 |
164 | 
165 |
166 | ## Displaying Thumnail Image
167 |
168 | Next we'll work on displaying a thumbnail version of our image. Generating this image is beyond scope of this tutorial so we'll assume you've got the full size image and the thumbnail version.
169 |
170 | First, to our instance of `ProgressiveImage`, we'll add a `thumbnailSource` prop which will take the exact same information as a typical `Image` source prop. In it we'll pass a smaller version of the image (50 in this case, use whatever you want) and our cache-busting query variable (for demo purposes only).
171 |
172 | ```javascript
173 | // App.js
174 |
175 | // ...
176 |
177 | export default class App extends React.Component {
178 | render() {
179 | return (
180 |
181 |
187 |
188 | );
189 | }
190 | }
191 | ```
192 |
193 | Then we'll modify our `ProgressiveImage` component. First add an `imageOverlay` style to your styles object.
194 |
195 | ```javascript
196 | // ProgressiveImage.js
197 |
198 | // ...
199 |
200 | const styles = StyleSheet.create({
201 | imageOverlay: {
202 | position: 'absolute',
203 | left: 0,
204 | right: 0,
205 | bottom: 0,
206 | top: 0,
207 | },
208 | container: {
209 | backgroundColor: '#e1e4e8',
210 | },
211 | });
212 |
213 | // ...
214 | ```
215 |
216 | We'll then render two `Image` components. Before we do that though we're going to use object destructuring to pull a few props off of `this.props` because we'll be overriding/combining them.
217 |
218 | ```javascript
219 | // ProgressiveImage.js
220 |
221 | // ...
222 |
223 | class ProgressiveImage extends React.Component {
224 | render() {
225 | const {
226 | thumbnailSource,
227 | source,
228 | style,
229 | ...props
230 | } = this.props;
231 |
232 | return (
233 |
234 |
239 |
244 |
245 | );
246 | }
247 | }
248 |
249 | export default ProgressiveImage;
250 | ```
251 |
252 | You can see we pull `thumbnailSource`, `source`, and `style` off of props. We then use "rest" syntax to capture the rest of the props. This allows us to forward all general purpose props to both of our images and only forward required props to the right component (like the appropriate source).
253 |
254 | You'll notice that we're combining the passed style and our `styles.imageOverlay` in the full size image. This is so that, via absolute positioning, the image will coverup the thumbnail version.
255 |
256 | Here's the result:
257 |
258 | 
259 |
260 | **Note:** You'll noticed that the thumbnail image is quite pixelated. You can pass a `blurRadius` prop to the thumbnail image to blur it. I took a screenshot so you can see the difference (I'm using a blurRadius of 2 for the example).
261 |
262 | 
263 | 
264 |
265 | You'll also notice that if we don't pass a `thumbnailSource` to the `ProgressiveImage` component everything works fine which means we can use this component for all of remote images even if we don't have a thumbnail image.
266 |
267 | ## Animating the Transition
268 |
269 | The final thing we'll do is smooth out that transition between the background color, thumbnail image, and full size image. To do that we'll use the `Animated` libary from React Native.
270 |
271 | Once you've imported `Animated` you'll then want replace the `Image` components in `ProgressiveImage` with `Animated.Image`.
272 |
273 | You'll also want to create two new animated variables on the component, defaulting them to 0.
274 |
275 | ```javascript
276 | // ProgressiveImage.js
277 |
278 | import React from 'react';
279 | import { View, StyleSheet, Animated } from 'react-native';
280 |
281 | // ...
282 |
283 | class ProgressiveImage extends React.Component {
284 | thumbnailAnimated = new Animated.Value(0);
285 |
286 | imageAnimated = new Animated.Value(0);
287 |
288 | render() {
289 | const {
290 | thumbnailSource,
291 | source,
292 | style,
293 | ...props
294 | } = this.props;
295 |
296 | return (
297 |
298 |
304 |
309 |
310 | );
311 | }
312 | }
313 | ```
314 |
315 | These `Animated.Value`s will be used to drive the opacity of the images. When the thumbnail loads we'll set `thumbnailAnimated` to 1. When the full size image loads we'll set `imageAnimated` to 1.
316 |
317 | ```javascript
318 | // ProgressiveImage.js
319 |
320 | // ...
321 |
322 | class ProgressiveImage extends React.Component {
323 | thumbnailAnimated = new Animated.Value(0);
324 |
325 | imageAnimated = new Animated.Value(0);
326 |
327 | handleThumbnailLoad = () => {
328 | Animated.timing(this.thumbnailAnimated, {
329 | toValue: 1,
330 | }).start();
331 | }
332 |
333 | onImageLoad = () => {
334 | Animated.timing(this.imageAnimated, {
335 | toValue: 1,
336 | }).start();
337 | }
338 |
339 | // ...
340 | }
341 | ```
342 |
343 | These functions will be called via the `onLoad` prop of the `Animated.Image` components.
344 |
345 | ```javascript
346 | // ProgressiveImage.js
347 |
348 | // ...
349 |
350 | class ProgressiveImage extends React.Component {
351 | // ...
352 |
353 | render() {
354 | const {
355 | thumbnailSource,
356 | source,
357 | style,
358 | ...props
359 | } = this.props;
360 |
361 | return (
362 |
363 |
370 |
376 |
377 | );
378 | }
379 | }
380 |
381 | export default ProgressiveImage;
382 | ```
383 |
384 | That results in our final progressive image loading.
385 |
386 | 
387 |
388 | The final code can be found [on Github](https://github.com/HandlebarLabs/react-native-examples-and-tutorials/tutorials/progressive-image-loading).
389 |
390 | ---
391 |
392 | > I hope you found this tutorial helpful! If you're looking for more React Native tutorials and examples check out [this repo](https://github.com/HandlebarLabs/react-native-examples-and-tutorials) and be sure to "clap" for this article so other devs can find and benefit from it!
--------------------------------------------------------------------------------
/tutorials/progressive-image-loading/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "sdkVersion": "27.0.0"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/tutorials/progressive-image-loading/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ProgressiveImageLoading",
3 | "version": "0.1.0",
4 | "private": true,
5 | "devDependencies": {
6 | "jest-expo": "~27.0.0",
7 | "react-native-scripts": "1.14.0",
8 | "react-test-renderer": "16.3.1"
9 | },
10 | "main": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
11 | "scripts": {
12 | "start": "react-native-scripts start",
13 | "eject": "react-native-scripts eject",
14 | "android": "react-native-scripts android",
15 | "ios": "react-native-scripts ios",
16 | "test": "jest"
17 | },
18 | "jest": {
19 | "preset": "jest-expo"
20 | },
21 | "dependencies": {
22 | "expo": "^27.0.1",
23 | "prop-types": "^15.6.2",
24 | "react": "16.3.1",
25 | "react-native": "~0.55.2"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tutorials/progressive-image-loading/tutorial-assets/01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/progressive-image-loading/tutorial-assets/01.png
--------------------------------------------------------------------------------
/tutorials/progressive-image-loading/tutorial-assets/02.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/progressive-image-loading/tutorial-assets/02.gif
--------------------------------------------------------------------------------
/tutorials/progressive-image-loading/tutorial-assets/03.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/progressive-image-loading/tutorial-assets/03.gif
--------------------------------------------------------------------------------
/tutorials/progressive-image-loading/tutorial-assets/04.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/progressive-image-loading/tutorial-assets/04.gif
--------------------------------------------------------------------------------
/tutorials/progressive-image-loading/tutorial-assets/05.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/progressive-image-loading/tutorial-assets/05.gif
--------------------------------------------------------------------------------
/tutorials/progressive-image-loading/tutorial-assets/06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/progressive-image-loading/tutorial-assets/06.png
--------------------------------------------------------------------------------
/tutorials/progressive-image-loading/tutorial-assets/07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/progressive-image-loading/tutorial-assets/07.png
--------------------------------------------------------------------------------
/tutorials/progressive-image-loading/tutorial-assets/08.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/progressive-image-loading/tutorial-assets/08.gif
--------------------------------------------------------------------------------
/tutorials/section-list-basics/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"],
3 | "env": {
4 | "development": {
5 | "plugins": ["transform-react-jsx-source"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/tutorials/section-list-basics/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 |
--------------------------------------------------------------------------------
/tutorials/section-list-basics/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/tutorials/section-list-basics/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | StyleSheet,
4 | Text,
5 | View,
6 | SafeAreaView,
7 | // ScrollView,
8 | SectionList,
9 | } from 'react-native';
10 |
11 | const newTasks = [
12 | { id: 2, title: 'Wax on' },
13 | { id: 3, title: 'Wax off' },
14 | { id: 17, title: 'Wax on' },
15 | { id: 18, title: 'Wax off' },
16 | { id: 19, title: 'Wax on' },
17 | { id: 20, title: 'Wax off' },
18 | { id: 21, title: 'Wax on' },
19 | { id: 22, title: 'Wax off' },
20 | { id: 23, title: 'Wax on' },
21 | { id: 24, title: 'Wax off' },
22 | { id: 25, title: 'Wax on' },
23 | { id: 26, title: 'Wax off' },
24 | { id: 27, title: 'Wax on' },
25 | { id: 28, title: 'Wax off' },
26 | ];
27 |
28 | const completedTasks = [
29 | { id: 1, title: 'Watch Karate Kid' },
30 | { id: 4, title: 'Watch Karate Kid' },
31 | { id: 5, title: 'Watch Karate Kid' },
32 | { id: 6, title: 'Watch Karate Kid' },
33 | { id: 7, title: 'Watch Karate Kid' },
34 | { id: 8, title: 'Watch Karate Kid' },
35 | { id: 9, title: 'Watch Karate Kid' },
36 | { id: 10, title: 'Watch Karate Kid' },
37 | { id: 11, title: 'Watch Karate Kid' },
38 | { id: 12, title: 'Watch Karate Kid' },
39 | { id: 13, title: 'Watch Karate Kid' },
40 | { id: 14, title: 'Watch Karate Kid' },
41 | { id: 15, title: 'Watch Karate Kid' },
42 | { id: 16, title: 'Watch Karate Kid' },
43 | ];
44 |
45 | const styles = StyleSheet.create({
46 | container: {
47 | flex: 1,
48 | backgroundColor: '#fff',
49 | },
50 | row: {
51 | paddingHorizontal: 20,
52 | paddingVertical: 10,
53 | },
54 | sectionHeader: {
55 | backgroundColor: '#efefef',
56 | paddingHorizontal: 20,
57 | paddingVertical: 10,
58 | },
59 | });
60 |
61 | export default class App extends React.Component {
62 | render() {
63 | return (
64 |
65 | (
71 |
72 | {item.title}
73 |
74 | )}
75 | renderSectionHeader={({ section }) => (
76 |
77 | {section.title}
78 |
79 | )}
80 | keyExtractor={item => item.id}
81 | />
82 | {/*
83 |
84 | New Tasks
85 |
86 | {newTasks.map(task => (
87 |
88 | {task.title}
89 |
90 | ))}
91 |
92 | Completed Tasks
93 |
94 | {completedTasks.map(task => (
95 |
96 | {task.title}
97 |
98 | ))}
99 | */}
100 |
101 | );
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/tutorials/section-list-basics/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "SectionListBasics",
4 | "description": "This project is really great.",
5 | "slug": "SectionListBasics",
6 | "privacy": "public",
7 | "sdkVersion": "30.0.0",
8 | "platforms": ["ios", "android"],
9 | "version": "1.0.0",
10 | "orientation": "portrait",
11 | "icon": "./assets/icon.png",
12 | "splash": {
13 | "image": "./assets/splash.png",
14 | "resizeMode": "contain",
15 | "backgroundColor": "#ffffff"
16 | },
17 | "updates": {
18 | "fallbackToCacheTimeout": 0
19 | },
20 | "assetBundlePatterns": [
21 | "**/*"
22 | ],
23 | "ios": {
24 | "supportsTablet": true
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tutorials/section-list-basics/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/section-list-basics/assets/icon.png
--------------------------------------------------------------------------------
/tutorials/section-list-basics/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HandlebarLabs/react-native-examples-and-tutorials/7dba1c1946728566ea6b08b3415dcc09b8f2f303/tutorials/section-list-basics/assets/splash.png
--------------------------------------------------------------------------------
/tutorials/section-list-basics/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "empty-project-template",
3 | "main": "node_modules/expo/AppEntry.js",
4 | "private": true,
5 | "scripts": {
6 | "start": "expo start",
7 | "android": "expo start --android",
8 | "ios": "expo start --ios",
9 | "eject": "expo eject"
10 | },
11 | "dependencies": {
12 | "expo": "^30.0.1",
13 | "react": "16.3.1",
14 | "react-native": "https://github.com/expo/react-native/archive/sdk-30.0.0.tar.gz"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@babel/code-frame@^7.0.0":
6 | version "7.0.0"
7 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8"
8 | dependencies:
9 | "@babel/highlight" "^7.0.0"
10 |
11 | "@babel/generator@^7.0.0":
12 | version "7.0.0"
13 | resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0.tgz#1efd58bffa951dc846449e58ce3a1d7f02d393aa"
14 | dependencies:
15 | "@babel/types" "^7.0.0"
16 | jsesc "^2.5.1"
17 | lodash "^4.17.10"
18 | source-map "^0.5.0"
19 | trim-right "^1.0.1"
20 |
21 | "@babel/helper-function-name@^7.0.0":
22 | version "7.0.0"
23 | resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0.tgz#a68cc8d04420ccc663dd258f9cc41b8261efa2d4"
24 | dependencies:
25 | "@babel/helper-get-function-arity" "^7.0.0"
26 | "@babel/template" "^7.0.0"
27 | "@babel/types" "^7.0.0"
28 |
29 | "@babel/helper-get-function-arity@^7.0.0":
30 | version "7.0.0"
31 | resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3"
32 | dependencies:
33 | "@babel/types" "^7.0.0"
34 |
35 | "@babel/helper-split-export-declaration@^7.0.0":
36 | version "7.0.0"
37 | resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz#3aae285c0311c2ab095d997b8c9a94cad547d813"
38 | dependencies:
39 | "@babel/types" "^7.0.0"
40 |
41 | "@babel/highlight@^7.0.0":
42 | version "7.0.0"
43 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4"
44 | dependencies:
45 | chalk "^2.0.0"
46 | esutils "^2.0.2"
47 | js-tokens "^4.0.0"
48 |
49 | "@babel/parser@^7.0.0":
50 | version "7.0.0"
51 | resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.0.0.tgz#697655183394facffb063437ddf52c0277698775"
52 |
53 | "@babel/template@^7.0.0":
54 | version "7.0.0"
55 | resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0.tgz#c2bc9870405959c89a9c814376a2ecb247838c80"
56 | dependencies:
57 | "@babel/code-frame" "^7.0.0"
58 | "@babel/parser" "^7.0.0"
59 | "@babel/types" "^7.0.0"
60 |
61 | "@babel/traverse@^7.0.0":
62 | version "7.0.0"
63 | resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0.tgz#b1fe9b6567fdf3ab542cfad6f3b31f854d799a61"
64 | dependencies:
65 | "@babel/code-frame" "^7.0.0"
66 | "@babel/generator" "^7.0.0"
67 | "@babel/helper-function-name" "^7.0.0"
68 | "@babel/helper-split-export-declaration" "^7.0.0"
69 | "@babel/parser" "^7.0.0"
70 | "@babel/types" "^7.0.0"
71 | debug "^3.1.0"
72 | globals "^11.1.0"
73 | lodash "^4.17.10"
74 |
75 | "@babel/types@^7.0.0":
76 | version "7.0.0"
77 | resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0.tgz#6e191793d3c854d19c6749989e3bc55f0e962118"
78 | dependencies:
79 | esutils "^2.0.2"
80 | lodash "^4.17.10"
81 | to-fast-properties "^2.0.0"
82 |
83 | acorn-jsx@^4.1.1:
84 | version "4.1.1"
85 | resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-4.1.1.tgz#e8e41e48ea2fe0c896740610ab6a4ffd8add225e"
86 | dependencies:
87 | acorn "^5.0.3"
88 |
89 | acorn@^5.0.3, acorn@^5.6.0:
90 | version "5.7.2"
91 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.2.tgz#91fa871883485d06708800318404e72bfb26dcc5"
92 |
93 | ajv-keywords@^3.0.0:
94 | version "3.2.0"
95 | resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a"
96 |
97 | ajv@^6.0.1, ajv@^6.5.3:
98 | version "6.5.3"
99 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.3.tgz#71a569d189ecf4f4f321224fecb166f071dd90f9"
100 | dependencies:
101 | fast-deep-equal "^2.0.1"
102 | fast-json-stable-stringify "^2.0.0"
103 | json-schema-traverse "^0.4.1"
104 | uri-js "^4.2.2"
105 |
106 | ansi-escapes@^3.0.0:
107 | version "3.1.0"
108 | resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30"
109 |
110 | ansi-regex@^3.0.0:
111 | version "3.0.0"
112 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
113 |
114 | ansi-styles@^3.2.1:
115 | version "3.2.1"
116 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
117 | dependencies:
118 | color-convert "^1.9.0"
119 |
120 | argparse@^1.0.7:
121 | version "1.0.10"
122 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
123 | dependencies:
124 | sprintf-js "~1.0.2"
125 |
126 | aria-query@^3.0.0:
127 | version "3.0.0"
128 | resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc"
129 | dependencies:
130 | ast-types-flow "0.0.7"
131 | commander "^2.11.0"
132 |
133 | array-includes@^3.0.3:
134 | version "3.0.3"
135 | resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d"
136 | dependencies:
137 | define-properties "^1.1.2"
138 | es-abstract "^1.7.0"
139 |
140 | array-union@^1.0.1:
141 | version "1.0.2"
142 | resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
143 | dependencies:
144 | array-uniq "^1.0.1"
145 |
146 | array-uniq@^1.0.1:
147 | version "1.0.3"
148 | resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
149 |
150 | arrify@^1.0.0:
151 | version "1.0.1"
152 | resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
153 |
154 | ast-types-flow@0.0.7, ast-types-flow@^0.0.7:
155 | version "0.0.7"
156 | resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad"
157 |
158 | axobject-query@^2.0.1:
159 | version "2.0.1"
160 | resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.0.1.tgz#05dfa705ada8ad9db993fa6896f22d395b0b0a07"
161 | dependencies:
162 | ast-types-flow "0.0.7"
163 |
164 | babel-eslint@^9.0.0:
165 | version "9.0.0"
166 | resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-9.0.0.tgz#7d9445f81ed9f60aff38115f838970df9f2b6220"
167 | dependencies:
168 | "@babel/code-frame" "^7.0.0"
169 | "@babel/parser" "^7.0.0"
170 | "@babel/traverse" "^7.0.0"
171 | "@babel/types" "^7.0.0"
172 | eslint-scope "3.7.1"
173 | eslint-visitor-keys "^1.0.0"
174 |
175 | balanced-match@^1.0.0:
176 | version "1.0.0"
177 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
178 |
179 | brace-expansion@^1.1.7:
180 | version "1.1.11"
181 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
182 | dependencies:
183 | balanced-match "^1.0.0"
184 | concat-map "0.0.1"
185 |
186 | builtin-modules@^1.0.0:
187 | version "1.1.1"
188 | resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
189 |
190 | caller-path@^0.1.0:
191 | version "0.1.0"
192 | resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f"
193 | dependencies:
194 | callsites "^0.2.0"
195 |
196 | callsites@^0.2.0:
197 | version "0.2.0"
198 | resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca"
199 |
200 | chalk@^2.0.0, chalk@^2.1.0:
201 | version "2.4.1"
202 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e"
203 | dependencies:
204 | ansi-styles "^3.2.1"
205 | escape-string-regexp "^1.0.5"
206 | supports-color "^5.3.0"
207 |
208 | chardet@^0.7.0:
209 | version "0.7.0"
210 | resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
211 |
212 | circular-json@^0.3.1:
213 | version "0.3.3"
214 | resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
215 |
216 | cli-cursor@^2.1.0:
217 | version "2.1.0"
218 | resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
219 | dependencies:
220 | restore-cursor "^2.0.0"
221 |
222 | cli-width@^2.0.0:
223 | version "2.2.0"
224 | resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
225 |
226 | color-convert@^1.9.0:
227 | version "1.9.3"
228 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
229 | dependencies:
230 | color-name "1.1.3"
231 |
232 | color-name@1.1.3:
233 | version "1.1.3"
234 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
235 |
236 | commander@^2.11.0:
237 | version "2.17.1"
238 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
239 |
240 | concat-map@0.0.1:
241 | version "0.0.1"
242 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
243 |
244 | contains-path@^0.1.0:
245 | version "0.1.0"
246 | resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a"
247 |
248 | cross-spawn@^6.0.5:
249 | version "6.0.5"
250 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
251 | dependencies:
252 | nice-try "^1.0.4"
253 | path-key "^2.0.1"
254 | semver "^5.5.0"
255 | shebang-command "^1.2.0"
256 | which "^1.2.9"
257 |
258 | damerau-levenshtein@^1.0.4:
259 | version "1.0.4"
260 | resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz#03191c432cb6eea168bb77f3a55ffdccb8978514"
261 |
262 | debug@^2.6.8, debug@^2.6.9:
263 | version "2.6.9"
264 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
265 | dependencies:
266 | ms "2.0.0"
267 |
268 | debug@^3.1.0:
269 | version "3.1.0"
270 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
271 | dependencies:
272 | ms "2.0.0"
273 |
274 | deep-is@~0.1.3:
275 | version "0.1.3"
276 | resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
277 |
278 | define-properties@^1.1.2:
279 | version "1.1.3"
280 | resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
281 | dependencies:
282 | object-keys "^1.0.12"
283 |
284 | del@^2.0.2:
285 | version "2.2.2"
286 | resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8"
287 | dependencies:
288 | globby "^5.0.0"
289 | is-path-cwd "^1.0.0"
290 | is-path-in-cwd "^1.0.0"
291 | object-assign "^4.0.1"
292 | pify "^2.0.0"
293 | pinkie-promise "^2.0.0"
294 | rimraf "^2.2.8"
295 |
296 | doctrine@1.5.0:
297 | version "1.5.0"
298 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
299 | dependencies:
300 | esutils "^2.0.2"
301 | isarray "^1.0.0"
302 |
303 | doctrine@^2.1.0:
304 | version "2.1.0"
305 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
306 | dependencies:
307 | esutils "^2.0.2"
308 |
309 | emoji-regex@^6.5.1:
310 | version "6.5.1"
311 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.5.1.tgz#9baea929b155565c11ea41c6626eaa65cef992c2"
312 |
313 | error-ex@^1.2.0:
314 | version "1.3.2"
315 | resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
316 | dependencies:
317 | is-arrayish "^0.2.1"
318 |
319 | es-abstract@^1.6.1, es-abstract@^1.7.0:
320 | version "1.12.0"
321 | resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165"
322 | dependencies:
323 | es-to-primitive "^1.1.1"
324 | function-bind "^1.1.1"
325 | has "^1.0.1"
326 | is-callable "^1.1.3"
327 | is-regex "^1.0.4"
328 |
329 | es-to-primitive@^1.1.1:
330 | version "1.1.1"
331 | resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d"
332 | dependencies:
333 | is-callable "^1.1.1"
334 | is-date-object "^1.0.1"
335 | is-symbol "^1.0.1"
336 |
337 | escape-string-regexp@^1.0.5:
338 | version "1.0.5"
339 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
340 |
341 | eslint-config-airbnb-base@^13.1.0:
342 | version "13.1.0"
343 | resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-13.1.0.tgz#b5a1b480b80dfad16433d6c4ad84e6605052c05c"
344 | dependencies:
345 | eslint-restricted-globals "^0.1.1"
346 | object.assign "^4.1.0"
347 | object.entries "^1.0.4"
348 |
349 | eslint-config-airbnb@^17.1.0:
350 | version "17.1.0"
351 | resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-17.1.0.tgz#3964ed4bc198240315ff52030bf8636f42bc4732"
352 | dependencies:
353 | eslint-config-airbnb-base "^13.1.0"
354 | object.assign "^4.1.0"
355 | object.entries "^1.0.4"
356 |
357 | eslint-import-resolver-node@^0.3.1:
358 | version "0.3.2"
359 | resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a"
360 | dependencies:
361 | debug "^2.6.9"
362 | resolve "^1.5.0"
363 |
364 | eslint-module-utils@^2.2.0:
365 | version "2.2.0"
366 | resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz#b270362cd88b1a48ad308976ce7fa54e98411746"
367 | dependencies:
368 | debug "^2.6.8"
369 | pkg-dir "^1.0.0"
370 |
371 | eslint-plugin-import@^2.14.0:
372 | version "2.14.0"
373 | resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.14.0.tgz#6b17626d2e3e6ad52cfce8807a845d15e22111a8"
374 | dependencies:
375 | contains-path "^0.1.0"
376 | debug "^2.6.8"
377 | doctrine "1.5.0"
378 | eslint-import-resolver-node "^0.3.1"
379 | eslint-module-utils "^2.2.0"
380 | has "^1.0.1"
381 | lodash "^4.17.4"
382 | minimatch "^3.0.3"
383 | read-pkg-up "^2.0.0"
384 | resolve "^1.6.0"
385 |
386 | eslint-plugin-jsx-a11y@^6.1.1:
387 | version "6.1.1"
388 | resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.1.1.tgz#7bf56dbe7d47d811d14dbb3ddff644aa656ce8e1"
389 | dependencies:
390 | aria-query "^3.0.0"
391 | array-includes "^3.0.3"
392 | ast-types-flow "^0.0.7"
393 | axobject-query "^2.0.1"
394 | damerau-levenshtein "^1.0.4"
395 | emoji-regex "^6.5.1"
396 | has "^1.0.3"
397 | jsx-ast-utils "^2.0.1"
398 |
399 | eslint-plugin-react@^7.11.1:
400 | version "7.11.1"
401 | resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.11.1.tgz#c01a7af6f17519457d6116aa94fc6d2ccad5443c"
402 | dependencies:
403 | array-includes "^3.0.3"
404 | doctrine "^2.1.0"
405 | has "^1.0.3"
406 | jsx-ast-utils "^2.0.1"
407 | prop-types "^15.6.2"
408 |
409 | eslint-restricted-globals@^0.1.1:
410 | version "0.1.1"
411 | resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7"
412 |
413 | eslint-scope@3.7.1:
414 | version "3.7.1"
415 | resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
416 | dependencies:
417 | esrecurse "^4.1.0"
418 | estraverse "^4.1.1"
419 |
420 | eslint-scope@^4.0.0:
421 | version "4.0.0"
422 | resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172"
423 | dependencies:
424 | esrecurse "^4.1.0"
425 | estraverse "^4.1.1"
426 |
427 | eslint-utils@^1.3.1:
428 | version "1.3.1"
429 | resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.3.1.tgz#9a851ba89ee7c460346f97cf8939c7298827e512"
430 |
431 | eslint-visitor-keys@^1.0.0:
432 | version "1.0.0"
433 | resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d"
434 |
435 | eslint@^5.5.0:
436 | version "5.5.0"
437 | resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.5.0.tgz#8557fcceab5141a8197da9ffd9904f89f64425c6"
438 | dependencies:
439 | "@babel/code-frame" "^7.0.0"
440 | ajv "^6.5.3"
441 | chalk "^2.1.0"
442 | cross-spawn "^6.0.5"
443 | debug "^3.1.0"
444 | doctrine "^2.1.0"
445 | eslint-scope "^4.0.0"
446 | eslint-utils "^1.3.1"
447 | eslint-visitor-keys "^1.0.0"
448 | espree "^4.0.0"
449 | esquery "^1.0.1"
450 | esutils "^2.0.2"
451 | file-entry-cache "^2.0.0"
452 | functional-red-black-tree "^1.0.1"
453 | glob "^7.1.2"
454 | globals "^11.7.0"
455 | ignore "^4.0.6"
456 | imurmurhash "^0.1.4"
457 | inquirer "^6.1.0"
458 | is-resolvable "^1.1.0"
459 | js-yaml "^3.12.0"
460 | json-stable-stringify-without-jsonify "^1.0.1"
461 | levn "^0.3.0"
462 | lodash "^4.17.5"
463 | minimatch "^3.0.4"
464 | mkdirp "^0.5.1"
465 | natural-compare "^1.4.0"
466 | optionator "^0.8.2"
467 | path-is-inside "^1.0.2"
468 | pluralize "^7.0.0"
469 | progress "^2.0.0"
470 | regexpp "^2.0.0"
471 | require-uncached "^1.0.3"
472 | semver "^5.5.1"
473 | strip-ansi "^4.0.0"
474 | strip-json-comments "^2.0.1"
475 | table "^4.0.3"
476 | text-table "^0.2.0"
477 |
478 | espree@^4.0.0:
479 | version "4.0.0"
480 | resolved "https://registry.yarnpkg.com/espree/-/espree-4.0.0.tgz#253998f20a0f82db5d866385799d912a83a36634"
481 | dependencies:
482 | acorn "^5.6.0"
483 | acorn-jsx "^4.1.1"
484 |
485 | esprima@^4.0.0:
486 | version "4.0.1"
487 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
488 |
489 | esquery@^1.0.1:
490 | version "1.0.1"
491 | resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708"
492 | dependencies:
493 | estraverse "^4.0.0"
494 |
495 | esrecurse@^4.1.0:
496 | version "4.2.1"
497 | resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf"
498 | dependencies:
499 | estraverse "^4.1.0"
500 |
501 | estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1:
502 | version "4.2.0"
503 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
504 |
505 | esutils@^2.0.2:
506 | version "2.0.2"
507 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
508 |
509 | external-editor@^3.0.0:
510 | version "3.0.3"
511 | resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.3.tgz#5866db29a97826dbe4bf3afd24070ead9ea43a27"
512 | dependencies:
513 | chardet "^0.7.0"
514 | iconv-lite "^0.4.24"
515 | tmp "^0.0.33"
516 |
517 | fast-deep-equal@^2.0.1:
518 | version "2.0.1"
519 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
520 |
521 | fast-json-stable-stringify@^2.0.0:
522 | version "2.0.0"
523 | resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
524 |
525 | fast-levenshtein@~2.0.4:
526 | version "2.0.6"
527 | resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
528 |
529 | figures@^2.0.0:
530 | version "2.0.0"
531 | resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
532 | dependencies:
533 | escape-string-regexp "^1.0.5"
534 |
535 | file-entry-cache@^2.0.0:
536 | version "2.0.0"
537 | resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361"
538 | dependencies:
539 | flat-cache "^1.2.1"
540 | object-assign "^4.0.1"
541 |
542 | find-up@^1.0.0:
543 | version "1.1.2"
544 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
545 | dependencies:
546 | path-exists "^2.0.0"
547 | pinkie-promise "^2.0.0"
548 |
549 | find-up@^2.0.0:
550 | version "2.1.0"
551 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
552 | dependencies:
553 | locate-path "^2.0.0"
554 |
555 | flat-cache@^1.2.1:
556 | version "1.3.0"
557 | resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481"
558 | dependencies:
559 | circular-json "^0.3.1"
560 | del "^2.0.2"
561 | graceful-fs "^4.1.2"
562 | write "^0.2.1"
563 |
564 | fs.realpath@^1.0.0:
565 | version "1.0.0"
566 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
567 |
568 | function-bind@^1.1.0, function-bind@^1.1.1:
569 | version "1.1.1"
570 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
571 |
572 | functional-red-black-tree@^1.0.1:
573 | version "1.0.1"
574 | resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
575 |
576 | glob@^7.0.3, glob@^7.0.5, glob@^7.1.2:
577 | version "7.1.3"
578 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
579 | dependencies:
580 | fs.realpath "^1.0.0"
581 | inflight "^1.0.4"
582 | inherits "2"
583 | minimatch "^3.0.4"
584 | once "^1.3.0"
585 | path-is-absolute "^1.0.0"
586 |
587 | globals@^11.1.0, globals@^11.7.0:
588 | version "11.7.0"
589 | resolved "https://registry.yarnpkg.com/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673"
590 |
591 | globby@^5.0.0:
592 | version "5.0.0"
593 | resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d"
594 | dependencies:
595 | array-union "^1.0.1"
596 | arrify "^1.0.0"
597 | glob "^7.0.3"
598 | object-assign "^4.0.1"
599 | pify "^2.0.0"
600 | pinkie-promise "^2.0.0"
601 |
602 | graceful-fs@^4.1.2:
603 | version "4.1.11"
604 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
605 |
606 | has-flag@^3.0.0:
607 | version "3.0.0"
608 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
609 |
610 | has-symbols@^1.0.0:
611 | version "1.0.0"
612 | resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44"
613 |
614 | has@^1.0.1, has@^1.0.3:
615 | version "1.0.3"
616 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
617 | dependencies:
618 | function-bind "^1.1.1"
619 |
620 | hosted-git-info@^2.1.4:
621 | version "2.7.1"
622 | resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047"
623 |
624 | iconv-lite@^0.4.24:
625 | version "0.4.24"
626 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
627 | dependencies:
628 | safer-buffer ">= 2.1.2 < 3"
629 |
630 | ignore@^4.0.6:
631 | version "4.0.6"
632 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
633 |
634 | imurmurhash@^0.1.4:
635 | version "0.1.4"
636 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
637 |
638 | inflight@^1.0.4:
639 | version "1.0.6"
640 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
641 | dependencies:
642 | once "^1.3.0"
643 | wrappy "1"
644 |
645 | inherits@2:
646 | version "2.0.3"
647 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
648 |
649 | inquirer@^6.1.0:
650 | version "6.2.0"
651 | resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.0.tgz#51adcd776f661369dc1e894859c2560a224abdd8"
652 | dependencies:
653 | ansi-escapes "^3.0.0"
654 | chalk "^2.0.0"
655 | cli-cursor "^2.1.0"
656 | cli-width "^2.0.0"
657 | external-editor "^3.0.0"
658 | figures "^2.0.0"
659 | lodash "^4.17.10"
660 | mute-stream "0.0.7"
661 | run-async "^2.2.0"
662 | rxjs "^6.1.0"
663 | string-width "^2.1.0"
664 | strip-ansi "^4.0.0"
665 | through "^2.3.6"
666 |
667 | is-arrayish@^0.2.1:
668 | version "0.2.1"
669 | resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
670 |
671 | is-builtin-module@^1.0.0:
672 | version "1.0.0"
673 | resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe"
674 | dependencies:
675 | builtin-modules "^1.0.0"
676 |
677 | is-callable@^1.1.1, is-callable@^1.1.3:
678 | version "1.1.4"
679 | resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
680 |
681 | is-date-object@^1.0.1:
682 | version "1.0.1"
683 | resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16"
684 |
685 | is-fullwidth-code-point@^2.0.0:
686 | version "2.0.0"
687 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
688 |
689 | is-path-cwd@^1.0.0:
690 | version "1.0.0"
691 | resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
692 |
693 | is-path-in-cwd@^1.0.0:
694 | version "1.0.1"
695 | resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52"
696 | dependencies:
697 | is-path-inside "^1.0.0"
698 |
699 | is-path-inside@^1.0.0:
700 | version "1.0.1"
701 | resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036"
702 | dependencies:
703 | path-is-inside "^1.0.1"
704 |
705 | is-promise@^2.1.0:
706 | version "2.1.0"
707 | resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
708 |
709 | is-regex@^1.0.4:
710 | version "1.0.4"
711 | resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
712 | dependencies:
713 | has "^1.0.1"
714 |
715 | is-resolvable@^1.1.0:
716 | version "1.1.0"
717 | resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
718 |
719 | is-symbol@^1.0.1:
720 | version "1.0.1"
721 | resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572"
722 |
723 | isarray@^1.0.0:
724 | version "1.0.0"
725 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
726 |
727 | isexe@^2.0.0:
728 | version "2.0.0"
729 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
730 |
731 | "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
732 | version "4.0.0"
733 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
734 |
735 | js-yaml@^3.12.0:
736 | version "3.12.0"
737 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1"
738 | dependencies:
739 | argparse "^1.0.7"
740 | esprima "^4.0.0"
741 |
742 | jsesc@^2.5.1:
743 | version "2.5.1"
744 | resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.1.tgz#e421a2a8e20d6b0819df28908f782526b96dd1fe"
745 |
746 | json-schema-traverse@^0.4.1:
747 | version "0.4.1"
748 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
749 |
750 | json-stable-stringify-without-jsonify@^1.0.1:
751 | version "1.0.1"
752 | resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
753 |
754 | jsx-ast-utils@^2.0.1:
755 | version "2.0.1"
756 | resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz#e801b1b39985e20fffc87b40e3748080e2dcac7f"
757 | dependencies:
758 | array-includes "^3.0.3"
759 |
760 | levn@^0.3.0, levn@~0.3.0:
761 | version "0.3.0"
762 | resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
763 | dependencies:
764 | prelude-ls "~1.1.2"
765 | type-check "~0.3.2"
766 |
767 | load-json-file@^2.0.0:
768 | version "2.0.0"
769 | resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8"
770 | dependencies:
771 | graceful-fs "^4.1.2"
772 | parse-json "^2.2.0"
773 | pify "^2.0.0"
774 | strip-bom "^3.0.0"
775 |
776 | locate-path@^2.0.0:
777 | version "2.0.0"
778 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
779 | dependencies:
780 | p-locate "^2.0.0"
781 | path-exists "^3.0.0"
782 |
783 | lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5:
784 | version "4.17.10"
785 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
786 |
787 | loose-envify@^1.3.1:
788 | version "1.4.0"
789 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
790 | dependencies:
791 | js-tokens "^3.0.0 || ^4.0.0"
792 |
793 | mimic-fn@^1.0.0:
794 | version "1.2.0"
795 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
796 |
797 | minimatch@^3.0.3, minimatch@^3.0.4:
798 | version "3.0.4"
799 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
800 | dependencies:
801 | brace-expansion "^1.1.7"
802 |
803 | minimist@0.0.8:
804 | version "0.0.8"
805 | resolved "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
806 |
807 | mkdirp@^0.5.1:
808 | version "0.5.1"
809 | resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
810 | dependencies:
811 | minimist "0.0.8"
812 |
813 | ms@2.0.0:
814 | version "2.0.0"
815 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
816 |
817 | mute-stream@0.0.7:
818 | version "0.0.7"
819 | resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
820 |
821 | natural-compare@^1.4.0:
822 | version "1.4.0"
823 | resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
824 |
825 | nice-try@^1.0.4:
826 | version "1.0.5"
827 | resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
828 |
829 | normalize-package-data@^2.3.2:
830 | version "2.4.0"
831 | resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f"
832 | dependencies:
833 | hosted-git-info "^2.1.4"
834 | is-builtin-module "^1.0.0"
835 | semver "2 || 3 || 4 || 5"
836 | validate-npm-package-license "^3.0.1"
837 |
838 | object-assign@^4.0.1, object-assign@^4.1.1:
839 | version "4.1.1"
840 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
841 |
842 | object-keys@^1.0.11, object-keys@^1.0.12:
843 | version "1.0.12"
844 | resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2"
845 |
846 | object.assign@^4.1.0:
847 | version "4.1.0"
848 | resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da"
849 | dependencies:
850 | define-properties "^1.1.2"
851 | function-bind "^1.1.1"
852 | has-symbols "^1.0.0"
853 | object-keys "^1.0.11"
854 |
855 | object.entries@^1.0.4:
856 | version "1.0.4"
857 | resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.0.4.tgz#1bf9a4dd2288f5b33f3a993d257661f05d161a5f"
858 | dependencies:
859 | define-properties "^1.1.2"
860 | es-abstract "^1.6.1"
861 | function-bind "^1.1.0"
862 | has "^1.0.1"
863 |
864 | once@^1.3.0:
865 | version "1.4.0"
866 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
867 | dependencies:
868 | wrappy "1"
869 |
870 | onetime@^2.0.0:
871 | version "2.0.1"
872 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
873 | dependencies:
874 | mimic-fn "^1.0.0"
875 |
876 | optionator@^0.8.2:
877 | version "0.8.2"
878 | resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
879 | dependencies:
880 | deep-is "~0.1.3"
881 | fast-levenshtein "~2.0.4"
882 | levn "~0.3.0"
883 | prelude-ls "~1.1.2"
884 | type-check "~0.3.2"
885 | wordwrap "~1.0.0"
886 |
887 | os-tmpdir@~1.0.2:
888 | version "1.0.2"
889 | resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
890 |
891 | p-limit@^1.1.0:
892 | version "1.3.0"
893 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
894 | dependencies:
895 | p-try "^1.0.0"
896 |
897 | p-locate@^2.0.0:
898 | version "2.0.0"
899 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
900 | dependencies:
901 | p-limit "^1.1.0"
902 |
903 | p-try@^1.0.0:
904 | version "1.0.0"
905 | resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
906 |
907 | parse-json@^2.2.0:
908 | version "2.2.0"
909 | resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
910 | dependencies:
911 | error-ex "^1.2.0"
912 |
913 | path-exists@^2.0.0:
914 | version "2.1.0"
915 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
916 | dependencies:
917 | pinkie-promise "^2.0.0"
918 |
919 | path-exists@^3.0.0:
920 | version "3.0.0"
921 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
922 |
923 | path-is-absolute@^1.0.0:
924 | version "1.0.1"
925 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
926 |
927 | path-is-inside@^1.0.1, path-is-inside@^1.0.2:
928 | version "1.0.2"
929 | resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
930 |
931 | path-key@^2.0.1:
932 | version "2.0.1"
933 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
934 |
935 | path-parse@^1.0.5:
936 | version "1.0.6"
937 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
938 |
939 | path-type@^2.0.0:
940 | version "2.0.0"
941 | resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73"
942 | dependencies:
943 | pify "^2.0.0"
944 |
945 | pify@^2.0.0:
946 | version "2.3.0"
947 | resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
948 |
949 | pinkie-promise@^2.0.0:
950 | version "2.0.1"
951 | resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
952 | dependencies:
953 | pinkie "^2.0.0"
954 |
955 | pinkie@^2.0.0:
956 | version "2.0.4"
957 | resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
958 |
959 | pkg-dir@^1.0.0:
960 | version "1.0.0"
961 | resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4"
962 | dependencies:
963 | find-up "^1.0.0"
964 |
965 | pluralize@^7.0.0:
966 | version "7.0.0"
967 | resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
968 |
969 | prelude-ls@~1.1.2:
970 | version "1.1.2"
971 | resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
972 |
973 | progress@^2.0.0:
974 | version "2.0.0"
975 | resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f"
976 |
977 | prop-types@^15.6.2:
978 | version "15.6.2"
979 | resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
980 | dependencies:
981 | loose-envify "^1.3.1"
982 | object-assign "^4.1.1"
983 |
984 | punycode@^2.1.0:
985 | version "2.1.1"
986 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
987 |
988 | read-pkg-up@^2.0.0:
989 | version "2.0.0"
990 | resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be"
991 | dependencies:
992 | find-up "^2.0.0"
993 | read-pkg "^2.0.0"
994 |
995 | read-pkg@^2.0.0:
996 | version "2.0.0"
997 | resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8"
998 | dependencies:
999 | load-json-file "^2.0.0"
1000 | normalize-package-data "^2.3.2"
1001 | path-type "^2.0.0"
1002 |
1003 | regexpp@^2.0.0:
1004 | version "2.0.0"
1005 | resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.0.tgz#b2a7534a85ca1b033bcf5ce9ff8e56d4e0755365"
1006 |
1007 | require-uncached@^1.0.3:
1008 | version "1.0.3"
1009 | resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3"
1010 | dependencies:
1011 | caller-path "^0.1.0"
1012 | resolve-from "^1.0.0"
1013 |
1014 | resolve-from@^1.0.0:
1015 | version "1.0.1"
1016 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
1017 |
1018 | resolve@^1.5.0, resolve@^1.6.0:
1019 | version "1.8.1"
1020 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26"
1021 | dependencies:
1022 | path-parse "^1.0.5"
1023 |
1024 | restore-cursor@^2.0.0:
1025 | version "2.0.0"
1026 | resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
1027 | dependencies:
1028 | onetime "^2.0.0"
1029 | signal-exit "^3.0.2"
1030 |
1031 | rimraf@^2.2.8:
1032 | version "2.6.2"
1033 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
1034 | dependencies:
1035 | glob "^7.0.5"
1036 |
1037 | run-async@^2.2.0:
1038 | version "2.3.0"
1039 | resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
1040 | dependencies:
1041 | is-promise "^2.1.0"
1042 |
1043 | rxjs@^6.1.0:
1044 | version "6.3.2"
1045 | resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.3.2.tgz#6a688b16c4e6e980e62ea805ec30648e1c60907f"
1046 | dependencies:
1047 | tslib "^1.9.0"
1048 |
1049 | "safer-buffer@>= 2.1.2 < 3":
1050 | version "2.1.2"
1051 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
1052 |
1053 | "semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.5.1:
1054 | version "5.5.1"
1055 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477"
1056 |
1057 | shebang-command@^1.2.0:
1058 | version "1.2.0"
1059 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
1060 | dependencies:
1061 | shebang-regex "^1.0.0"
1062 |
1063 | shebang-regex@^1.0.0:
1064 | version "1.0.0"
1065 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
1066 |
1067 | signal-exit@^3.0.2:
1068 | version "3.0.2"
1069 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
1070 |
1071 | slice-ansi@1.0.0:
1072 | version "1.0.0"
1073 | resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d"
1074 | dependencies:
1075 | is-fullwidth-code-point "^2.0.0"
1076 |
1077 | source-map@^0.5.0:
1078 | version "0.5.7"
1079 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
1080 |
1081 | spdx-correct@^3.0.0:
1082 | version "3.0.0"
1083 | resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82"
1084 | dependencies:
1085 | spdx-expression-parse "^3.0.0"
1086 | spdx-license-ids "^3.0.0"
1087 |
1088 | spdx-exceptions@^2.1.0:
1089 | version "2.1.0"
1090 | resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz#2c7ae61056c714a5b9b9b2b2af7d311ef5c78fe9"
1091 |
1092 | spdx-expression-parse@^3.0.0:
1093 | version "3.0.0"
1094 | resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0"
1095 | dependencies:
1096 | spdx-exceptions "^2.1.0"
1097 | spdx-license-ids "^3.0.0"
1098 |
1099 | spdx-license-ids@^3.0.0:
1100 | version "3.0.0"
1101 | resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87"
1102 |
1103 | sprintf-js@~1.0.2:
1104 | version "1.0.3"
1105 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
1106 |
1107 | string-width@^2.1.0, string-width@^2.1.1:
1108 | version "2.1.1"
1109 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
1110 | dependencies:
1111 | is-fullwidth-code-point "^2.0.0"
1112 | strip-ansi "^4.0.0"
1113 |
1114 | strip-ansi@^4.0.0:
1115 | version "4.0.0"
1116 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
1117 | dependencies:
1118 | ansi-regex "^3.0.0"
1119 |
1120 | strip-bom@^3.0.0:
1121 | version "3.0.0"
1122 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
1123 |
1124 | strip-json-comments@^2.0.1:
1125 | version "2.0.1"
1126 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
1127 |
1128 | supports-color@^5.3.0:
1129 | version "5.5.0"
1130 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
1131 | dependencies:
1132 | has-flag "^3.0.0"
1133 |
1134 | table@^4.0.3:
1135 | version "4.0.3"
1136 | resolved "http://registry.npmjs.org/table/-/table-4.0.3.tgz#00b5e2b602f1794b9acaf9ca908a76386a7813bc"
1137 | dependencies:
1138 | ajv "^6.0.1"
1139 | ajv-keywords "^3.0.0"
1140 | chalk "^2.1.0"
1141 | lodash "^4.17.4"
1142 | slice-ansi "1.0.0"
1143 | string-width "^2.1.1"
1144 |
1145 | text-table@^0.2.0:
1146 | version "0.2.0"
1147 | resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
1148 |
1149 | through@^2.3.6:
1150 | version "2.3.8"
1151 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
1152 |
1153 | tmp@^0.0.33:
1154 | version "0.0.33"
1155 | resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
1156 | dependencies:
1157 | os-tmpdir "~1.0.2"
1158 |
1159 | to-fast-properties@^2.0.0:
1160 | version "2.0.0"
1161 | resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
1162 |
1163 | trim-right@^1.0.1:
1164 | version "1.0.1"
1165 | resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
1166 |
1167 | tslib@^1.9.0:
1168 | version "1.9.3"
1169 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
1170 |
1171 | type-check@~0.3.2:
1172 | version "0.3.2"
1173 | resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
1174 | dependencies:
1175 | prelude-ls "~1.1.2"
1176 |
1177 | uri-js@^4.2.2:
1178 | version "4.2.2"
1179 | resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
1180 | dependencies:
1181 | punycode "^2.1.0"
1182 |
1183 | validate-npm-package-license@^3.0.1:
1184 | version "3.0.4"
1185 | resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
1186 | dependencies:
1187 | spdx-correct "^3.0.0"
1188 | spdx-expression-parse "^3.0.0"
1189 |
1190 | which@^1.2.9:
1191 | version "1.3.1"
1192 | resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
1193 | dependencies:
1194 | isexe "^2.0.0"
1195 |
1196 | wordwrap@~1.0.0:
1197 | version "1.0.0"
1198 | resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
1199 |
1200 | wrappy@1:
1201 | version "1.0.2"
1202 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
1203 |
1204 | write@^0.2.1:
1205 | version "0.2.1"
1206 | resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757"
1207 | dependencies:
1208 | mkdirp "^0.5.1"
1209 |
--------------------------------------------------------------------------------