├── .babelrc ├── .gitignore ├── .npmignore ├── .prettierrc ├── LICENSE ├── README.md ├── examples ├── todos │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── apollo.config.js │ ├── gatsby-config.js │ ├── gatsby-node.js │ ├── graphql.config.json │ ├── package.json │ ├── src │ │ ├── AppApolloClient.tsx │ │ ├── __apollo_codegen__ │ │ │ ├── GET_TODOS.ts │ │ │ ├── TODO_STATS.ts │ │ │ ├── UPDATE_TODO.ts │ │ │ └── globalTypes.ts │ │ ├── components │ │ │ └── appLink.tsx │ │ ├── declarations.d.ts │ │ ├── layouts │ │ │ ├── MainLayout.css │ │ │ └── MainLayout.tsx │ │ ├── pages │ │ │ ├── 404.tsx │ │ │ ├── createTodo.tsx │ │ │ ├── index.tsx │ │ │ └── todos.tsx │ │ └── utils │ │ │ ├── typography.d.ts │ │ │ └── typography.js │ ├── tsconfig.json │ ├── tslint.json │ └── yarn.lock └── uikits │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── babel.config.js │ ├── gatsby-browser.js │ ├── gatsby-config.js │ ├── gatsby-ssr.js │ ├── package.json │ ├── src │ ├── assets │ │ └── retro-regular.ttf │ ├── components │ │ ├── JSONView.js │ │ ├── example.js │ │ ├── expo │ │ │ ├── ExpoBatteryExample.js │ │ │ ├── ExpoBlurExample.js │ │ │ ├── ExpoCameraExample.js │ │ │ ├── ExpoConstantsExample.js │ │ │ ├── ExpoFontExample.js │ │ │ ├── ExpoGesturesExample.js │ │ │ ├── ExpoLocationExample.js │ │ │ └── ExpoPermissionsExample.js │ │ ├── externalLink.js │ │ ├── kittens │ │ │ ├── KittensDatepickerExample.js │ │ │ └── KittensSelectExample.js │ │ ├── layout.css │ │ └── layout.js │ └── pages │ │ ├── elements.js │ │ ├── expo.js │ │ ├── index.js │ │ ├── kittens.js │ │ ├── nachos.js │ │ ├── nativebase.js │ │ └── paper.js │ └── yarn.lock ├── index.js ├── package.json ├── recipe.mdx ├── recipePage.js ├── src ├── gatsby-browser.js ├── gatsby-node.js └── gatsby-ssr.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["babel-preset-gatsby-package"], 3 | "plugins": ["babel-plugin-react-native-web"] 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | .idea 61 | 62 | /gatsby-browser.js 63 | /gatsby-ssr.js 64 | /gatsby-node.js 65 | 66 | *.iml -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | *.un~ 29 | yarn.lock 30 | src 31 | flow-typed 32 | coverage 33 | decls 34 | examples 35 | .github/ -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "semi": false, 4 | "singleQuote": true, 5 | "tabWidth": 2 6 | } 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Sébastien Lorber 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gatsby plugin for React-Native-Web / Expo 2 | 3 | [![NPM](https://img.shields.io/npm/dm/gatsby-plugin-react-native-web.svg)](https://www.npmjs.com/package/gatsby-plugin-react-native-web) 4 | [![Build Status](https://travis-ci.com/slorber/gatsby-plugin-react-native-web.svg?branch=master)](https://travis-ci.com/slorber/gatsby-plugin-react-native-web) 5 | 6 | Adds [React-Native-Web](https://github.com/necolas/react-native-web) and [Expo](https://docs.expo.io/) support to a Gatsby site. 7 | 8 | Example "production" usage on my blog: [sebastienlorber.com/using-expo-in-gatsby](https://sebastienlorber.com/using-expo-in-gatsby) 9 | 10 | --- 11 | 12 | # Sponsor 13 | 14 | **[ThisWeekInReact.com](https://thisweekinreact.com)**: the best newsletter to stay up-to-date with the React ecosystem: 15 | 16 | [![ThisWeekInReact.com banner](https://user-images.githubusercontent.com/749374/136185889-ebdb67cd-ec78-4655-b88b-79a6c134acd2.png)](https://thisweekinreact.com) 17 | 18 | --- 19 | 20 | # Why 21 | 22 | Main reasons: 23 | 24 | - sharing components between your mobile app and your static website. 25 | - using [atomic CSS-in-JS](https://sebastienlorber.com/atomic-css-in-js/) with React-Native-Web 26 | 27 | Cross-platform code is finally taking off, and it's time to share more code between web and mobile. 28 | There's already Expo web, but it is not suited for a marketing website that needs JAMStack / SEO / CMS integration / Performance / Gatsby-image ... 29 | This project aims to "merge" Expo for web with Gatsby, so that you can build useful things like: 30 | 31 | - Share a universal cross-platform design system between your mobile app and your marketing website 32 | - Blog about ReactNative, and include runnable RN demos directly in your MDX 33 | - Document your ReactNative components with Docz 34 | - Use ReactNativeWeb as a performant CSS-in-JS lib, like Twitter does (see [atomic CSS-in-JS](https://sebastienlorber.com/atomic-css-in-js/)). 35 | 36 | --- 37 | 38 | # Supported features 39 | 40 | This plugin uses the same setup as Expo for web, thanks to [@expo/webpack-config](https://www.npmjs.com/package/@expo/webpack-config) 41 | 42 | Support includes: 43 | - Primitive components from ReactNative (check RNW [support](https://github.com/necolas/react-native-web#compatibility-with-react-native)) 44 | - Expo unimodules with web support like `expo-camera` (check Expo doc for support) 45 | - .web.js extension handling 46 | - Universal ReactNative design system libraries, like `react-native-paper`, `react-native-ui-kitten`... 47 | - Universal gesture systems with Animated, `react-native-gesture-handler` or Reanimated. 48 | - Universal SVG components using `react-native-svg` 49 | - Automatic transpilation of third party react-native libs 50 | - Works in MDX 51 | - Works in Docz (Gatsby-based) 52 | 53 | 54 | # Recipe 55 | 56 | Use the new Gatsby Recipe feature to get started fast: 57 | 58 | ``` 59 | gatsby recipes https://raw.githubusercontent.com/slorber/gatsby-plugin-react-native-web/master/recipe.mdx 60 | ``` 61 | 62 | 63 | # Manual setup 64 | 65 | 66 | **1. Install required dependencies** 67 | 68 | ``` 69 | yarn add react-native react-native-web@~0.11.7 gatsby-plugin-react-native-web expo 70 | ``` 71 | 72 | 73 | **2. Create a `gatsby-config.js` and use the plugin:** 74 | 75 | ```js 76 | module.exports = { 77 | plugins: [ 78 | `gatsby-plugin-react-native-web`, 79 | ], 80 | } 81 | ``` 82 | 83 | **3. Install additional cross-platform libraries / unimodules (optional)** 84 | 85 | Add expo audio/video components: 86 | 87 | ``` 88 | yarn add expo-av 89 | ``` 90 | 91 | **4. Create a test pages** 92 | 93 | Create an example page like [this one](./recipePage.js) in your `./pages` folder, or try importing React-Native / Expo components into an existing one. 94 | 95 | --- 96 | 97 | # Demo 98 | 99 | The `examples` folder have runnable Gatsby site demos. They are also hosted: 100 | 101 | - [sebastienlorber.com](https://sebastienlorber.com): an [open-source](https://github.com/slorber/sebastienlorber.com) Gatsby/Novela blog. All MDX embedded components are using React-Native ([example](https://sebastienlorber.com/using-expo-in-gatsby)). 102 | - [Todos example](https://gatsby-rnw-todos.netlify.com): a "todo backoffice" built with Gatsby + RNW + Apollo + TS + react-native-paper: I like fancy stacks. 103 | 104 | # Example usage 105 | 106 | Very basic example: 107 | 108 | ```js 109 | import React from 'react' 110 | import Link from 'gatsby-link' 111 | import { StyleSheet, TouchableOpacity, Text, View } from 'react-native' 112 | 113 | const styles = StyleSheet.create({ 114 | box: { padding: 10, margin: 10, borderWidth: 1, borderColor: 'black' }, 115 | text: { fontWeight: 'bold', color: 'red' }, 116 | button: { 117 | marginVertical: 40, 118 | paddingVertical: 20, 119 | paddingHorizontal: 10, 120 | borderWidth: 1, 121 | borderColor: 'black', 122 | backgroundColor: 'lightgrey', 123 | alignItems: 'center', 124 | }, 125 | buttonText: { fontWeight: 'bold', color: 'black' }, 126 | }) 127 | 128 | const IndexPage = () => ( 129 | 130 | 131 | Hi this is React-Native-Web rendered by Gatsby 132 | 133 | alert('it works')}> 134 | Button 135 | 136 | Go to page 2 137 | 138 | ) 139 | 140 | export default IndexPage 141 | ``` 142 | 143 | ![image](https://camo.githubusercontent.com/58ec39b3966cdefb241b90fb4643ad8aa7b971b2/68747470733a2f2f7062732e7477696d672e636f6d2f6d656469612f445844575f715058304149534148532e6a70673a6c61726765) 144 | 145 | # How does it work 146 | 147 | - Adds `babel-preset-expo` which manages tree-shaking of `react-native-web` packages. 148 | - Implements module resolution for files with platform extensions like `.web.js`, `.web.tsx`... 149 | - Uses `@expo/webpack-config` which creates aliases for various React Native asset features and ensures that all React Native packages, and Unimodules are loaded with Babel. 150 | - Creates support for Gatsby SSR with `react-native-web` 151 | - Extracts critical CSS with the `StyleSheet` api of `react-native-web` and adds it to the static page. 152 | 153 | # FAQ 154 | 155 | 156 | ### Expo already has web support, why do I need this? 157 | 158 | Expo web support is more like Create-React-App, it only outputs a single html file and does client side routing. It works fine for apps, but miss the various benefits of Gatsby, including performance, SEO, CMS integration, Gatsby-image... 159 | 160 | Actually, this plugin uses the same webpack config as Expo web support, and Expo (Evan Bacon) contributed to this project. You'll also find support for Next if you need a static/SSR hybrid. 161 | 162 | 163 | ### How to share code for navigation/routing? 164 | 165 | This is not easy, because navigation patterns are different between web and mobile. 166 | 167 | ReactNavigation may have web support, but Gatsby can't use ReactNavigation config easily to construct static pages. 168 | 169 | You'd rather keep using platform-specific navigation trees (pages for Gatsby, and stacks/tabs for ReactNavigation). 170 | 171 | Eventually you could build your own cross-platform `navigate()` function, and your own cross-platform `Link` component (take a look at [expo-gatsby-navigation](https://github.com/nandorojo/expo-gatsby-navigation)). 172 | 173 | 174 | ### Can I share the same repo to build a mobile app and a Gatsby site with shared components? 175 | 176 | The most simple way to share code between an Expo app and a Gatsby site is currently to use a single folder for both the Expo app and the Gatsby app. 177 | 178 | Otherwise you can try to setup a monorepo, but keep in mind this requires more complex configuration, and Metro does not follow symlinks. 179 | 180 | You can also read the Expo doc about [adding Gatsby support to an existing app](https://docs.expo.io/versions/latest/guides/using-gatsby/). 181 | 182 | 183 | ### How can I publish an universal cross-platform component that works on web and mobile? 184 | 185 | You can take a loot at this example: [expo-dark-mode-switch](https://github.com/EvanBacon/expo-dark-mode-switch). It uses [expo-module-scripts](https://www.npmjs.com/package/expo-module-scripts). 186 | 187 | You can also check [react-native-community/bob](https://github.com/react-native-community/bob) 188 | 189 | 190 | # Hire a freelance expert 191 | 192 | Looking for a React/ReactNative freelance expert with more than 5 years production experience? 193 | Contact me from my [website](https://sebastienlorber.com/) or with [Twitter](https://twitter.com/sebastienlorber). 194 | -------------------------------------------------------------------------------- /examples/todos/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/osx,vim,node,windows,visualstudiocode 3 | 4 | ### Node ### 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | 24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 25 | .grunt 26 | 27 | # Bower dependency directory (https://bower.io/) 28 | bower_components 29 | 30 | # node-waf configuration 31 | .lock-wscript 32 | 33 | # Compiled binary addons (http://nodejs.org/api/addons.html) 34 | build/Release 35 | 36 | # Dependency directories 37 | node_modules/ 38 | jspm_packages/ 39 | 40 | # Typescript v1 declaration files 41 | typings/ 42 | 43 | # Optional npm cache directory 44 | .npm 45 | 46 | # Optional eslint cache 47 | .eslintcache 48 | 49 | # Optional REPL history 50 | .node_repl_history 51 | 52 | # Output of 'npm pack' 53 | *.tgz 54 | 55 | # Yarn Integrity file 56 | .yarn-integrity 57 | 58 | # dotenv environment variables file 59 | .env 60 | 61 | 62 | ### OSX ### 63 | *.DS_Store 64 | .AppleDouble 65 | .LSOverride 66 | 67 | # Icon must end with two \r 68 | Icon 69 | 70 | 71 | # Thumbnails 72 | ._* 73 | 74 | # Files that might appear in the root of a volume 75 | .DocumentRevisions-V100 76 | .fseventsd 77 | .Spotlight-V100 78 | .TemporaryItems 79 | .Trashes 80 | .VolumeIcon.icns 81 | .com.apple.timemachine.donotpresent 82 | 83 | # Directories potentially created on remote AFP share 84 | .AppleDB 85 | .AppleDesktop 86 | Network Trash Folder 87 | Temporary Items 88 | .apdisk 89 | 90 | ### Vim ### 91 | # swap 92 | [._]*.s[a-v][a-z] 93 | [._]*.sw[a-p] 94 | [._]s[a-v][a-z] 95 | [._]sw[a-p] 96 | # session 97 | Session.vim 98 | # temporary 99 | .netrwhist 100 | *~ 101 | # auto-generated tag files 102 | tags 103 | 104 | ### VisualStudioCode ### 105 | .vscode/* 106 | !.vscode/settings.json 107 | !.vscode/tasks.json 108 | !.vscode/launch.json 109 | !.vscode/extensions.json 110 | 111 | ### Windows ### 112 | # Windows thumbnail cache files 113 | Thumbs.db 114 | ehthumbs.db 115 | ehthumbs_vista.db 116 | 117 | # Folder config file 118 | Desktop.ini 119 | 120 | # Recycle Bin used on file shares 121 | $RECYCLE.BIN/ 122 | 123 | # Windows Installer files 124 | *.cab 125 | *.msi 126 | *.msm 127 | *.msp 128 | 129 | # Windows shortcuts 130 | *.lnk 131 | 132 | # End of https://www.gitignore.io/api/osx,vim,node,windows,visualstudiocode 133 | 134 | ### Gatsby ### 135 | public 136 | .cache 137 | -------------------------------------------------------------------------------- /examples/todos/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "semi": false, 4 | "singleQuote": true, 5 | "tabWidth": 2 6 | } 7 | -------------------------------------------------------------------------------- /examples/todos/README.md: -------------------------------------------------------------------------------- 1 | # Todo list example 2 | 3 | Using Gatsby, React-Native-Web, Apollo and Typescript and displaying highlighly dynamic data (like a backoffice) 4 | 5 | The GraphQL server is hosted on a [CodeSandbox](https://codesandbox.io/s/34p241l2r1) 6 | 7 | The Gatsby site is live at [https://gatsby-rnw-todos.netlify.com/](https://gatsby-rnw-todos.netlify.com/) 8 | -------------------------------------------------------------------------------- /examples/todos/apollo.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | client: { 3 | name: 'Gatsby RNW Todo client', 4 | service: { 5 | name: 'Gatsby RNW Todo service', 6 | url: 'https://34p241l2r1.sse.codesandbox.io/', 7 | }, 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /examples/todos/gatsby-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | siteMetadata: { 3 | siteName: `Using Typescript Example`, 4 | }, 5 | plugins: [ 6 | `gatsby-plugin-typescript`, 7 | `gatsby-plugin-react-native-web`, 8 | { 9 | resolve: `gatsby-plugin-typography`, 10 | options: { 11 | pathToConfigModule: `src/utils/typography.js`, 12 | omitGoogleFont: true, 13 | }, 14 | }, 15 | ], 16 | } 17 | -------------------------------------------------------------------------------- /examples/todos/gatsby-node.js: -------------------------------------------------------------------------------- 1 | 2 | // See https://callstack.github.io/react-native-paper/using-on-the-web.html 3 | exports.onCreateWebpackConfig = ({ actions, loaders, getConfig }) => { 4 | const config = getConfig() 5 | config.module.rules.push({ 6 | test: /\.js$/, 7 | include: /node_modules/, 8 | exclude: /node_modules[/\\](?!react-native-paper|react-native-vector-icons|react-native-safe-area-view)/, 9 | use: { 10 | loader: 'babel-loader', 11 | options: { 12 | // Disable reading babel configuration 13 | babelrc: false, 14 | configFile: false, 15 | 16 | // The configration for compilation 17 | presets: [ 18 | ['@babel/preset-env', { useBuiltIns: 'usage' }], 19 | '@babel/preset-react', 20 | '@babel/preset-flow', 21 | ], 22 | plugins: [ 23 | '@babel/plugin-proposal-class-properties', 24 | '@babel/plugin-proposal-object-rest-spread', 25 | ], 26 | }, 27 | }, 28 | }) 29 | actions.replaceWebpackConfig(config) 30 | } 31 | -------------------------------------------------------------------------------- /examples/todos/graphql.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": { 3 | "request": { 4 | "url": "https://34p241l2r1.sse.codesandbox.io/", 5 | "method": "POST", 6 | "postIntrospectionQuery": true 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/todos/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "using-typescript", 3 | "version": "1.0.0", 4 | "description": "Gatsby example site demonstrating how to use Typescript with Gatsby", 5 | "author": "fabien0102 ", 6 | "license": "MIT", 7 | "scripts": { 8 | "start": "concurrently \"yarn start:codegen\" \"yarn start:gatsby\" \"yarn start:typecheck\"", 9 | "start:gatsby": "gatsby develop", 10 | "start:typecheck": "yarn typecheck --watch", 11 | "start:codegen": "yarn apollo:codegen --watch", 12 | "develop": "gatsby develop", 13 | "build": "gatsby build", 14 | "typecheck": "tsc --noEmit", 15 | "lint": "tslint --project .", 16 | "apollo:codegen": "apollo client:codegen --target=typescript --addTypename --outputFlat src/__apollo_codegen__" 17 | }, 18 | "dependencies": { 19 | "@types/isomorphic-fetch": "^0.0.35", 20 | "@types/node": "^11.11.3", 21 | "@types/react": "16.8.8", 22 | "@types/react-dom": "16.8.2", 23 | "@types/react-native": "^0.57.40", 24 | "apollo": "^2.6.1", 25 | "apollo-boost": "^0.3.1", 26 | "gatsby": "^2.1.32", 27 | "gatsby-plugin-react-native-web": "^2.0.0-beta.0", 28 | "gatsby-plugin-typescript": "^2.0.11", 29 | "gatsby-plugin-typography": "^2.2.10", 30 | "isomorphic-fetch": "^2.2.1", 31 | "query-string": "^6.3.0", 32 | "react": "^16.4.0", 33 | "react-apollo": "^2.5.2", 34 | "react-dom": "^16.4.0", 35 | "react-native": "^0.55", 36 | "react-native-paper": "^2.13.0", 37 | "react-native-vector-icons": "^6.4.1", 38 | "react-native-web": "^0.11.1", 39 | "react-typography": "^0.16.13", 40 | "typography": "^0.16.17", 41 | "typography-theme-bootstrap": "^0.16.7" 42 | }, 43 | "devDependencies": { 44 | "@babel/preset-flow": "^7.0.0", 45 | "babel-plugin-react-native-web": "^0.11.1", 46 | "concurrently": "^4.1.0", 47 | "prettier": "^1.16.4", 48 | "tslint": "^5.14.0", 49 | "tslint-config-prettier": "^1.13.0", 50 | "tslint-react": "^3.6.0", 51 | "typescript": "^3.3.3333" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/todos/src/AppApolloClient.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from 'react' 2 | import { ApolloProvider } from 'react-apollo' 3 | import ApolloClient from 'apollo-boost' 4 | import fetch from 'isomorphic-fetch' 5 | 6 | const AppApolloClient = new ApolloClient({ 7 | uri: 'https://34p241l2r1.sse.codesandbox.io/', 8 | fetch, 9 | }) 10 | 11 | export default AppApolloClient 12 | 13 | export const AppApolloProvider = ({ children }: { children: ReactNode }) => ( 14 | {children} 15 | ) 16 | -------------------------------------------------------------------------------- /examples/todos/src/__apollo_codegen__/GET_TODOS.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | import { TodosFilter, Pagination } from "./globalTypes"; 6 | 7 | // ==================================================== 8 | // GraphQL query operation: GET_TODOS 9 | // ==================================================== 10 | 11 | export interface GET_TODOS_todos { 12 | __typename: "Todo"; 13 | id: string; 14 | text: string; 15 | checked: boolean; 16 | } 17 | 18 | export interface GET_TODOS { 19 | todos: GET_TODOS_todos[]; 20 | todosCount: number; 21 | } 22 | 23 | export interface GET_TODOSVariables { 24 | filter: TodosFilter; 25 | pagination: Pagination; 26 | } 27 | -------------------------------------------------------------------------------- /examples/todos/src/__apollo_codegen__/TODO_STATS.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL query operation: TODO_STATS 7 | // ==================================================== 8 | 9 | export interface TODO_STATS { 10 | allTodos: number; 11 | checkedTodos: number; 12 | uncheckedTodos: number; 13 | } 14 | -------------------------------------------------------------------------------- /examples/todos/src/__apollo_codegen__/UPDATE_TODO.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | // ==================================================== 6 | // GraphQL mutation operation: UPDATE_TODO 7 | // ==================================================== 8 | 9 | export interface UPDATE_TODO_updateTodo { 10 | __typename: "Todo"; 11 | id: string; 12 | text: string; 13 | checked: boolean; 14 | } 15 | 16 | export interface UPDATE_TODO { 17 | updateTodo: UPDATE_TODO_updateTodo; 18 | } 19 | 20 | export interface UPDATE_TODOVariables { 21 | id: string; 22 | text: string; 23 | checked: boolean; 24 | } 25 | -------------------------------------------------------------------------------- /examples/todos/src/__apollo_codegen__/globalTypes.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | // This file was automatically generated and should not be edited. 4 | 5 | //============================================================== 6 | // START Enums and Input Objects 7 | //============================================================== 8 | 9 | export interface Pagination { 10 | skip: number; 11 | limit: number; 12 | } 13 | 14 | export interface TodosFilter { 15 | checked?: boolean | null; 16 | } 17 | 18 | //============================================================== 19 | // END Enums and Input Objects 20 | //============================================================== 21 | -------------------------------------------------------------------------------- /examples/todos/src/components/appLink.tsx: -------------------------------------------------------------------------------- 1 | import React, { CSSProperties, ReactNode } from 'react' 2 | import { Link } from 'gatsby' 3 | 4 | const AppLink = (props: { 5 | to: string 6 | children?: ReactNode 7 | style?: CSSProperties 8 | }) => 9 | 10 | export default AppLink 11 | -------------------------------------------------------------------------------- /examples/todos/src/declarations.d.ts: -------------------------------------------------------------------------------- 1 | interface TodosQueryString { 2 | checked: boolean | null 3 | page: number 4 | } 5 | -------------------------------------------------------------------------------- /examples/todos/src/layouts/MainLayout.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'MaterialIcons'; 3 | src: url('../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf') format('truetype'); 4 | } 5 | -------------------------------------------------------------------------------- /examples/todos/src/layouts/MainLayout.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View } from 'react-native' 3 | import { AppApolloProvider } from '../AppApolloClient' 4 | import { Appbar, Provider as PaperProvider } from 'react-native-paper' 5 | import AppLink from '../components/appLink' 6 | import './MainLayout.css' 7 | 8 | const Layout: React.FC = ({ children }) => ( 9 | 15 | 23 | 24 | { 27 | // TODO 28 | }} 29 | /> 30 | 31 | 32 | Home 33 | 34 | 35 | 36 | { 39 | // TODO 40 | }} 41 | /> 42 | 43 | 44 | Todos 45 | 46 | 47 | 48 | { 51 | // TODO 52 | }} 53 | /> 54 | 55 | 59 | Create 60 | 61 | 62 | 63 | 64 | 70 | 77 | {children} 78 | 79 | 80 | 81 | ) 82 | 83 | const MainLayout: React.FC = ({ children }) => ( 84 | 85 | 86 | {children} 87 | 88 | 89 | ) 90 | 91 | export default MainLayout 92 | -------------------------------------------------------------------------------- /examples/todos/src/pages/404.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import {Text} from "react-native" 3 | import MainLayout from "../layouts/MainLayout" 4 | 5 | export default () => ( 6 | 7 | 404 - Not found 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /examples/todos/src/pages/createTodo.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Text, View } from 'react-native' 3 | import MainLayout from '../layouts/MainLayout' 4 | import { Button, TextInput } from 'react-native-paper' 5 | 6 | export default class CreateTodo extends React.Component<{}, 7 | { newTodo: string }> { 8 | state = { newTodo: '' } 9 | 10 | render() { 11 | return ( 12 | 13 | Create todo 14 | this.setState({ newTodo })} 19 | /> 20 | 29 | 30 | ) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/todos/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { gql } from 'apollo-boost' 2 | import React, { ComponentType } from 'react' 3 | import { Query } from 'react-apollo' 4 | import { ActivityIndicator, Linking, Text, View } from 'react-native' 5 | import { TODO_STATS } from '../__apollo_codegen__/TODO_STATS' 6 | import AppLink from '../components/appLink' 7 | import MainLayout from '../layouts/MainLayout' 8 | import { Avatar, Card, Title, Paragraph, Surface } from 'react-native-paper' 9 | import { stringify } from 'query-string' 10 | 11 | const TodoStats = gql` 12 | query TODO_STATS { 13 | allTodos: todosCount(filter: { checked: null }) 14 | checkedTodos: todosCount(filter: { checked: true }) 15 | uncheckedTodos: todosCount(filter: { checked: false }) 16 | } 17 | ` 18 | 19 | const TodoStatCard = ({ 20 | label, 21 | count, 22 | icon, 23 | queryString, 24 | }: { 25 | label: string 26 | count: number 27 | icon: string 28 | queryString?: Partial 29 | }) => { 30 | const linkUrl = `/todos${queryString ? '?' + stringify(queryString) : ''}` 31 | return ( 32 | 33 | 34 | 35 | 36 | } 40 | /> 41 | 42 | 43 | 44 | 45 | 46 | ) 47 | } 48 | 49 | const TodoStatsQuery = () => ( 50 | query={TodoStats}> 51 | {({ loading, error, data }) => { 52 | if (loading) { 53 | return ( 54 | 55 | 56 | 57 | ) 58 | } 59 | if (error) { 60 | return ( 61 | 62 | Error :( 63 | 64 | Maybe the CodeSandbox GraphQL server is offline. 65 | 66 | 69 | Linking.openURL('https://codesandbox.io/s/34p241l2r1') 70 | } 71 | > 72 | Open it to reactivate it 73 | 74 | 75 | ) 76 | } 77 | return ( 78 | 86 | 91 | 97 | 103 | 104 | ) 105 | }} 106 | 107 | ) 108 | 109 | type TextProps = React.ComponentProps 110 | type WebTextProps = TextProps & { 111 | href?: string 112 | } 113 | const FixedText = Text as ComponentType 114 | 115 | export default class IndexPage extends React.Component { 116 | render() { 117 | return ( 118 | 119 | 127 | This is an example of Gatsby using react-native-web 128 | 129 | 134 | Check https://github.com/slorber/gatsby-plugin-react-native-web 135 | 136 | 137 | 138 | ) 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /examples/todos/src/pages/todos.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ActivityIndicator, Linking, Text, View } from 'react-native' 3 | import { UPDATE_TODO, UPDATE_TODOVariables } from '../__apollo_codegen__/UPDATE_TODO' 4 | import MainLayout from '../layouts/MainLayout' 5 | 6 | import { gql } from 'apollo-boost' 7 | import { Query, Mutation } from 'react-apollo' 8 | import { 9 | GET_TODOS, 10 | GET_TODOS_todos, 11 | GET_TODOSVariables, 12 | } from '../__apollo_codegen__/GET_TODOS' 13 | import { navigate } from 'gatsby' 14 | import { parse, stringify } from 'query-string' 15 | import { Checkbox, DataTable } from 'react-native-paper' 16 | 17 | interface HasQueryString { 18 | queryString: TodosQueryString 19 | } 20 | 21 | const PageSize = 10 22 | 23 | const getQueryString = (search: string): TodosQueryString => { 24 | const parsed = parse(search) 25 | return { 26 | checked: 27 | parsed.checked === 'true' 28 | ? true 29 | : parsed.checked === 'false' 30 | ? false 31 | : null, 32 | page: parsed.page ? parseInt(parsed.page as any) : 1, 33 | } 34 | } 35 | 36 | const updateQueryString = (queryString: TodosQueryString): void => { 37 | const obj: any = {} 38 | if (queryString.checked !== null) { 39 | obj.checked = queryString.checked 40 | } 41 | if (queryString.page !== PageSize) { 42 | obj.page = queryString.page 43 | } 44 | const search = stringify(obj) 45 | navigate('/todos?' + search) 46 | } 47 | 48 | const GetTodos = gql` 49 | query GET_TODOS($filter: TodosFilter!, $pagination: Pagination!) { 50 | todos(filter: $filter, pagination: $pagination) { 51 | id 52 | text 53 | checked 54 | } 55 | todosCount(filter: $filter) 56 | } 57 | ` 58 | 59 | const UpdateTodo = gql` 60 | mutation UPDATE_TODO($id: String!,$text: String!, $checked: Boolean!) { 61 | updateTodo(id: $id,text: $text, checked: $checked) { 62 | id 63 | text 64 | checked 65 | } 66 | } 67 | ` 68 | 69 | 70 | const TodosTable = ({ 71 | todos, 72 | todosCount, 73 | queryString, 74 | }: GET_TODOS & HasQueryString) => { 75 | const numberOfPages = Math.ceil(todosCount / PageSize) 76 | const pageFirst = ((queryString.page - 1) * PageSize) + 1 77 | const pageLast = Math.min(todosCount, pageFirst + PageSize) 78 | const pageLabel = `${pageFirst}-${pageLast} of ${todosCount}` 79 | return ( 80 | 81 | mutation={UpdateTodo} 82 | > 83 | {(updateTodo, { data }) => ( 84 | 85 | 86 | Id 87 | Name 88 | Status 89 | 90 | 91 | {todos.map(todo => ( 92 | 93 | {todo.id} 94 | {todo.text} 95 | 96 | { 99 | updateTodo({ 100 | variables: { 101 | ...todo, 102 | checked: !todo.checked, 103 | }, 104 | optimisticResponse: { 105 | updateTodo: { 106 | __typename: 'Todo', 107 | ...todo, 108 | checked: !todo.checked, 109 | }, 110 | }, 111 | }) 112 | }} 113 | /> 114 | 115 | 116 | ))} 117 | 118 | { 122 | updateQueryString({ 123 | ...queryString, 124 | page: page + 1, 125 | }) 126 | }} 127 | label={pageLabel} 128 | /> 129 | 130 | )} 131 | 132 | ) 133 | } 134 | 135 | const TodosQuery = ({ queryString }: HasQueryString) => { 136 | return ( 137 | 145 | 146 | fetchPolicy="cache-and-network" 147 | query={GetTodos} 148 | variables={{ 149 | filter: { checked: queryString.checked }, 150 | pagination: { 151 | skip: (queryString.page - 1) * PageSize, 152 | limit: PageSize, 153 | }, 154 | }} 155 | > 156 | {({ loading, error, data }) => { 157 | if (loading) { 158 | return ( 159 | 160 | 161 | 162 | ) 163 | } 164 | if (error) { 165 | return ( 166 | 167 | Error :( 168 | 169 | Maybe the CodeSandbox GraphQL server is offline. 170 | 171 | 174 | Linking.openURL('https://codesandbox.io/s/34p241l2r1') 175 | } 176 | > 177 | Open it to reactivate it 178 | 179 | 180 | ) 181 | } 182 | return 183 | }} 184 | 185 | 186 | ) 187 | } 188 | 189 | const TodosFilter = ({ queryString }: HasQueryString) => ( 190 | 191 | 197 | updateQueryString({ 198 | ...queryString, 199 | page: 1, 200 | checked: null, 201 | }) 202 | } 203 | > 204 | All 205 | 206 | 212 | updateQueryString({ 213 | ...queryString, 214 | page: 1, 215 | checked: true, 216 | }) 217 | } 218 | > 219 | Only checked 220 | 221 | 227 | updateQueryString({ 228 | ...queryString, 229 | page: 1, 230 | checked: false, 231 | }) 232 | } 233 | > 234 | Only unchecked 235 | 236 | 237 | ) 238 | 239 | export default class TodosPage extends React.Component { 240 | render() { 241 | const queryString = getQueryString(this.props.location.search as string) 242 | return ( 243 | 244 | 245 | Todos page 246 | 247 | 248 | 249 | 250 | 251 | 252 | ) 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /examples/todos/src/utils/typography.d.ts: -------------------------------------------------------------------------------- 1 | export declare const rhythm: (a: number) => number; 2 | -------------------------------------------------------------------------------- /examples/todos/src/utils/typography.js: -------------------------------------------------------------------------------- 1 | import Typography from "typography" 2 | import theme from "typography-theme-bootstrap" 3 | 4 | theme.overrideThemeStyles = ({ rhythm, scale }, options) => { 5 | return { 6 | "h1, h2, h3": { 7 | ...scale(1 / 6), 8 | fontWeight: `normal`, 9 | color: `#999`, 10 | lineHeight: `1.2`, 11 | }, 12 | "code, kbd, pre, samp": { 13 | fontFamily: `Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace`, 14 | }, 15 | code: { 16 | fontSize: `90%`, 17 | }, 18 | "h1 code, h2 code": { 19 | color: `#112`, 20 | fontWeight: `bold`, 21 | }, 22 | "p > code": { 23 | background: `#fcf1e8`, 24 | color: `#452108`, 25 | padding: `.1rem .3rem`, 26 | }, 27 | a: { 28 | color: `inherit`, 29 | textDecoration: `none`, 30 | transition: `background 0.2s ease-out`, 31 | boxShadow: `inset 0 -2px 0px 0px #cdecf9`, 32 | textShadow: `0.03em 0 #fff, -0.03em 0 #fff, 0 0.03em #fff, 0 -0.03em #fff, 0.06em 0 #fff, -0.06em 0 #fff, 0.09em 0 #fff, -0.09em 0 #fff, 0.12em 0 #fff, -0.12em 0 #fff, 0.15em 0 #fff, -0.15em 0 #fff`, 33 | }, 34 | "a:hover": { 35 | background: `#cdecf9`, 36 | textShadow: `none`, 37 | }, 38 | "a > code": { 39 | fontWeight: `bold`, 40 | }, 41 | img: { 42 | borderRadius: `2px`, 43 | margin: `0`, 44 | }, 45 | // borrowed from https://github.com/comfusion/after-dark/ 46 | // @see https://github.com/comfusion/after-dark/blob/8fdbe2f480ac40315cf0e01cece785d2b5c4b0c3/layouts/partials/critical-theme.css#L36-L39 47 | "a[href*='//']:after": { 48 | content: `" " url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20class='i-external'%20viewBox='0%200%2032%2032'%20width='14'%20height='14'%20fill='none'%20stroke='%23cdecf9'%20stroke-linecap='round'%20stroke-linejoin='round'%20stroke-width='9.38%'%3E%3Cpath%20d='M14%209%20L3%209%203%2029%2023%2029%2023%2018%20M18%204%20L28%204%2028%2014%20M28%204%20L14%2018'/%3E%3C/svg%3E")`, 49 | }, 50 | "a[href*='//']:hover:after": { 51 | content: `" " url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20class='i-external'%20viewBox='0%200%2032%2032'%20width='14'%20height='14'%20fill='none'%20stroke='%23000000'%20stroke-linecap='round'%20stroke-linejoin='round'%20stroke-width='9.38%'%3E%3Cpath%20d='M14%209%20L3%209%203%2029%2023%2029%2023%2018%20M18%204%20L28%204%2028%2014%20M28%204%20L14%2018'/%3E%3C/svg%3E")`, 52 | }, 53 | } 54 | } 55 | 56 | const typography = new Typography(theme) 57 | 58 | // Back out the below once Typography is upgraded for es6 59 | export default typography 60 | 61 | export const rhythm = typography.rhythm 62 | export const scale = typography.scale 63 | export const options = typography.options 64 | -------------------------------------------------------------------------------- /examples/todos/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["./src/**/*"], 3 | "compilerOptions": { 4 | "target": "esnext", 5 | "module": "commonjs", 6 | "lib": ["dom", "es2017"], 7 | // "allowJs": true, 8 | // "checkJs": true, 9 | "jsx": "react", 10 | "strict": true, 11 | "esModuleInterop": true, 12 | "experimentalDecorators": true, 13 | "emitDecoratorMetadata": true, 14 | "noEmit": true, 15 | "skipLibCheck": true 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/todos/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": ["tslint:latest", "tslint-react", "tslint-config-prettier"], 4 | "jsRules": {}, 5 | "rules": { 6 | "interface-name": false, 7 | "member-access": [false], 8 | "jsx-no-lambda": [false], 9 | "ordered-imports": [false], 10 | "object-literal-sort-keys": [false], 11 | "no-console": [false], 12 | "no-var-requires": true, 13 | "radix": false 14 | }, 15 | "rulesDirectory": [] 16 | } 17 | -------------------------------------------------------------------------------- /examples/uikits/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # dotenv environment variables file 55 | .env 56 | 57 | # gatsby files 58 | .cache/ 59 | public 60 | 61 | # Mac files 62 | .DS_Store 63 | 64 | # Yarn 65 | yarn-error.log 66 | .pnp/ 67 | .pnp.js 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | report.html 72 | 73 | # Expo 74 | /.expo -------------------------------------------------------------------------------- /examples/uikits/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "semi": false, 4 | "singleQuote": true, 5 | "tabWidth": 2 6 | } 7 | -------------------------------------------------------------------------------- /examples/uikits/README.md: -------------------------------------------------------------------------------- 1 | Showcase how this plugin can be used alongside RNW compatible UIKits/libs like: 2 | 3 | - expo 4 | - react-native-paper 5 | - nativebase 6 | - reactnativeelements 7 | 8 | Example is published automatically [here](https://gatsby-rnw-uikits.netlify.com/) 9 | -------------------------------------------------------------------------------- /examples/uikits/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { presets: ['babel-preset-expo'] } 2 | -------------------------------------------------------------------------------- /examples/uikits/gatsby-browser.js: -------------------------------------------------------------------------------- 1 | import { light as lightTheme, mapping } from '@eva-design/eva' 2 | import React from 'react' 3 | import { Provider as PaperProvider } from 'react-native-paper' 4 | import { ApplicationProvider } from 'react-native-ui-kitten' 5 | 6 | export const wrapRootElement = ({ element }) => { 7 | return ( 8 | 9 | {element} 10 | 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /examples/uikits/gatsby-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | `gatsby-plugin-react-native-web`, 4 | { 5 | resolve: 'gatsby-plugin-webpack-bundle-analyzer', 6 | options: { 7 | production: true, 8 | openAnalyzer: false, 9 | analyzerMode: 'static', 10 | }, 11 | }, 12 | ], 13 | } 14 | -------------------------------------------------------------------------------- /examples/uikits/gatsby-ssr.js: -------------------------------------------------------------------------------- 1 | import { light as lightTheme, mapping } from '@eva-design/eva' 2 | import React from 'react' 3 | import { Provider as PaperProvider } from 'react-native-paper' 4 | import { ApplicationProvider } from 'react-native-ui-kitten' 5 | 6 | export const wrapRootElement = ({ element }) => { 7 | return ( 8 | 9 | {element} 10 | 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /examples/uikits/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-starter-default", 3 | "private": true, 4 | "description": "A simple starter to get up and developing quickly with Gatsby", 5 | "version": "0.1.0", 6 | "author": "Kyle Mathews ", 7 | "dependencies": { 8 | "@eva-design/eva": "^1.2.0", 9 | "@unimodules/core": "^4.0.0", 10 | "@unimodules/react-native-adapter": "^4.0.0", 11 | "expo-asset": "^7.0.0", 12 | "expo-battery": "^1.0.1", 13 | "expo-blur": "^7.0.0", 14 | "expo-camera": "^7.0.0", 15 | "expo-constants": "^7.0.1", 16 | "expo-file-system": "^7.0.0", 17 | "expo-font": "^7.0.0", 18 | "expo-location": "^7.0.0", 19 | "expo-permissions": "^7.0.0", 20 | "gatsby": "^2.3.3", 21 | "gatsby-plugin-react-native-web": "^3.0.0-beta.0", 22 | "gatsby-plugin-webpack-bundle-analyzer": "^1.0.4", 23 | "nachos-ui": "^0.2.0-beta.1", 24 | "native-base": "^2.12.1", 25 | "prop-types": "^15.7.2", 26 | "react": "^16.8.6", 27 | "react-dom": "^16.8.6", 28 | "react-helmet": "^5.2.0", 29 | "react-native": "^0.55", 30 | "react-native-elements": "^1.1.0", 31 | "react-native-gesture-handler": "^1.4.1", 32 | "react-native-paper": "^2.13.0", 33 | "react-native-ui-kitten": "^4.2.0", 34 | "react-native-vector-icons": "^6.4.2", 35 | "react-native-web": "^0.11.1", 36 | "react-native-web-hooks": "^1.0.2" 37 | }, 38 | "devDependencies": { 39 | "babel-preset-expo": "^7.1.0", 40 | "prettier": "^1.16.4" 41 | }, 42 | "keywords": [ 43 | "gatsby" 44 | ], 45 | "license": "MIT", 46 | "scripts": { 47 | "build": "gatsby build", 48 | "develop": "gatsby develop", 49 | "format": "prettier --write src/**/*.{js,jsx}", 50 | "start": "npm run develop", 51 | "serve": "gatsby serve", 52 | "test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\"" 53 | }, 54 | "repository": { 55 | "type": "git", 56 | "url": "https://github.com/gatsbyjs/gatsby-starter-default" 57 | }, 58 | "bugs": { 59 | "url": "https://github.com/gatsbyjs/gatsby/issues" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /examples/uikits/src/assets/retro-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slorber/gatsby-plugin-react-native-web/c8271534c7c64a6ef4c547f1fd7a3cb99a49f232/examples/uikits/src/assets/retro-regular.ttf -------------------------------------------------------------------------------- /examples/uikits/src/components/JSONView.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { ScrollView, Text } from 'react-native' 3 | 4 | export default function JSONView({ json }) { 5 | return ( 6 | 7 | 14 | {JSON.stringify(json, null, 2)} 15 | 16 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /examples/uikits/src/components/example.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './layout.css' 3 | import { Text, View } from 'react-native' 4 | 5 | const Example = ({ title, children, style, row = false }) => ( 6 | 16 | {title} 17 | 27 | {children} 28 | 29 | 30 | ) 31 | 32 | export default Example 33 | -------------------------------------------------------------------------------- /examples/uikits/src/components/expo/ExpoBatteryExample.js: -------------------------------------------------------------------------------- 1 | import * as Battery from 'expo-battery' 2 | import * as React from 'react' 3 | import { ScrollView, Text } from 'react-native' 4 | 5 | import Example from '../example' 6 | 7 | export default function ExpoBatteryExample() { 8 | if (!Battery.isSupported) { 9 | return Battery API is not supported on this device 10 | } 11 | return ( 12 | 13 | 14 | 15 | ) 16 | } 17 | 18 | function BatteryExample() { 19 | const [batteryLevel, setBatteryLevel] = React.useState(-1) 20 | const [batteryState, setBatteryState] = React.useState( 21 | Battery.BatteryState.UNKNOWN 22 | ) 23 | const [lowPowerMode, setLowPowerMode] = React.useState(false) 24 | 25 | async function getInitialBatteryStateAsync() { 26 | const [batteryLevel, batteryState, lowPowerMode] = await Promise.all([ 27 | Battery.getBatteryLevelAsync(), 28 | Battery.getBatteryStateAsync(), 29 | Battery.isLowPowerModeEnabledAsync(), 30 | ]) 31 | 32 | setBatteryLevel(batteryLevel) 33 | setBatteryState(batteryState) 34 | setLowPowerMode(lowPowerMode) 35 | } 36 | 37 | React.useEffect(() => { 38 | getInitialBatteryStateAsync() 39 | 40 | const batteryLevelListener = Battery.addBatteryLevelListener( 41 | ({ batteryLevel }) => setBatteryLevel(batteryLevel) 42 | ) 43 | const batteryStateListener = Battery.addBatteryStateListener( 44 | ({ batteryState }) => setBatteryState(batteryState) 45 | ) 46 | const lowPowerModeListener = Battery.addLowPowerModeListener( 47 | ({ lowPowerMode }) => setLowPowerMode(lowPowerMode) 48 | ) 49 | return () => { 50 | batteryLevelListener && batteryLevelListener.remove() 51 | batteryStateListener && batteryStateListener.remove() 52 | lowPowerModeListener && lowPowerModeListener.remove() 53 | } 54 | }, []) 55 | 56 | return ( 57 | 58 | 59 | {JSON.stringify( 60 | { 61 | batteryLevel, 62 | batteryState: getBatteryStateString(batteryState), 63 | lowPowerMode, 64 | }, 65 | null, 66 | 2 67 | )} 68 | 69 | 70 | ) 71 | } 72 | 73 | function getBatteryStateString(batteryState) { 74 | switch (batteryState) { 75 | case Battery.BatteryState.UNPLUGGED: 76 | return 'UNPLUGGED' 77 | case Battery.BatteryState.CHARGING: 78 | return 'CHARGING' 79 | case Battery.BatteryState.FULL: 80 | return 'FULL' 81 | case Battery.BatteryState.UNKNOWN: 82 | default: 83 | return 'UNKNOWN' 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /examples/uikits/src/components/expo/ExpoBlurExample.js: -------------------------------------------------------------------------------- 1 | import { BlurView } from 'expo-blur' 2 | import React, { useEffect, useState } from 'react' 3 | import { Image, StyleSheet, Text } from 'react-native' 4 | 5 | import Example from '../example' 6 | 7 | const IMAGE = { uri: 'https://i.ytimg.com/vi/y588qNiCZZo/maxresdefault.jpg' } 8 | 9 | export default function ExpoBlurExample() { 10 | return ( 11 | 12 | 13 | 14 | Blur View 15 | 16 | 17 | ) 18 | } 19 | 20 | const styles = StyleSheet.create({ 21 | image: { 22 | flex: 1, 23 | height: 300, 24 | }, 25 | blur: { 26 | position: 'absolute', 27 | top: '50%', 28 | left: 0, 29 | bottom: 0, 30 | right: 0, 31 | padding: 15, 32 | alignItems: 'center', 33 | justifyContent: 'center', 34 | borderRadius: 5, 35 | }, 36 | text: { 37 | backgroundColor: 'transparent', 38 | fontSize: 15, 39 | color: '#fff', 40 | }, 41 | }) 42 | -------------------------------------------------------------------------------- /examples/uikits/src/components/expo/ExpoCameraExample.js: -------------------------------------------------------------------------------- 1 | import { Camera } from 'expo-camera' 2 | import React, { useEffect, useState } from 'react' 3 | 4 | import Example from '../example' 5 | 6 | export default function ExpoCameraExample() { 7 | return ( 8 | 9 | 12 | 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /examples/uikits/src/components/expo/ExpoConstantsExample.js: -------------------------------------------------------------------------------- 1 | import Constants from 'expo-constants' 2 | import React, { useEffect, useState } from 'react' 3 | import { Text } from 'react-native' 4 | 5 | import Example from '../example' 6 | 7 | export default function ExpoConstantsExample() { 8 | return ( 9 | 10 | 17 | {JSON.stringify(Constants, null, 2)} 18 | 19 | 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /examples/uikits/src/components/expo/ExpoFontExample.js: -------------------------------------------------------------------------------- 1 | import * as Font from 'expo-font' 2 | import React, { useEffect, useState } from 'react' 3 | import { Text } from 'react-native' 4 | 5 | import Example from '../example' 6 | 7 | function ExpoFontExample() { 8 | const [loaded, setLoaded] = useState(false) 9 | 10 | async function loadFontAsync() { 11 | await Font.loadAsync({ 12 | 'retro-regular': require('../../assets/retro-regular.ttf'), 13 | }) 14 | setLoaded(true) 15 | } 16 | 17 | useEffect(() => { 18 | loadFontAsync() 19 | }, []) 20 | 21 | return ( 22 | 23 | {loaded && ( 24 | 32 | Cool new font 33 | 34 | )} 35 | 36 | ) 37 | } 38 | 39 | export default ExpoFontExample 40 | -------------------------------------------------------------------------------- /examples/uikits/src/components/expo/ExpoGesturesExample.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { Animated, StyleSheet, View } from 'react-native' 3 | import { PanGestureHandler, State } from 'react-native-gesture-handler' 4 | 5 | import Example from '../example' 6 | 7 | const USE_NATIVE_DRIVER = false 8 | 9 | const START_X = 0 10 | const START_Y = 0 11 | 12 | class Tracking extends Component { 13 | constructor(props) { 14 | super(props) 15 | 16 | this.state = { width: 0, height: 0 } 17 | 18 | const tension = 0.8 19 | const friction = 3 20 | 21 | this._dragX = new Animated.Value(START_X) 22 | this._transX = new Animated.Value(START_X) 23 | this._follow1x = new Animated.Value(START_X) 24 | this._follow2x = new Animated.Value(START_X) 25 | Animated.spring(this._transX, { 26 | toValue: this._dragX, 27 | tension, 28 | friction, 29 | }).start() 30 | Animated.spring(this._follow1x, { 31 | toValue: this._transX, 32 | tension, 33 | friction, 34 | }).start() 35 | Animated.spring(this._follow2x, { 36 | toValue: this._follow1x, 37 | tension, 38 | friction, 39 | }).start() 40 | 41 | this._dragY = new Animated.Value(START_Y) 42 | this._transY = new Animated.Value(START_Y) 43 | this._follow1y = new Animated.Value(START_Y) 44 | this._follow2y = new Animated.Value(START_Y) 45 | Animated.spring(this._transY, { 46 | toValue: this._dragY, 47 | tension, 48 | friction, 49 | }).start() 50 | Animated.spring(this._follow1y, { 51 | toValue: this._transY, 52 | tension, 53 | friction, 54 | }).start() 55 | Animated.spring(this._follow2y, { 56 | toValue: this._follow1y, 57 | tension, 58 | friction, 59 | }).start() 60 | 61 | this._onGestureEvent = Animated.event( 62 | [ 63 | { 64 | nativeEvent: { translationX: this._dragX, translationY: this._dragY }, 65 | }, 66 | ], 67 | { useNativeDriver: USE_NATIVE_DRIVER } 68 | ) 69 | 70 | this._lastOffset = { x: START_X, y: START_Y } 71 | } 72 | _onHandlerStateChange = event => { 73 | if (event.nativeEvent.oldState === State.ACTIVE) { 74 | const { height, width } = this.state 75 | 76 | const posX = this._lastOffset.x + event.nativeEvent.translationX 77 | const posY = this._lastOffset.y + event.nativeEvent.translationY 78 | 79 | const distFromTop = posY 80 | const distFromBottom = height - posY - BOX_SIZE 81 | const distFromLeft = posX 82 | const distFromRight = width - posX - BOX_SIZE 83 | 84 | this._lastOffset = { x: posX, y: posY } 85 | 86 | this._dragX.flattenOffset() 87 | this._dragY.flattenOffset() 88 | 89 | const minDist = Math.min( 90 | distFromTop, 91 | distFromBottom, 92 | distFromLeft, 93 | distFromRight 94 | ) 95 | if (distFromTop === minDist) { 96 | this._dragY.setValue(-BOX_SIZE / 4) 97 | this._lastOffset.y = -BOX_SIZE / 4 98 | } else if (distFromBottom === minDist) { 99 | this._dragY.setValue(height - BOX_SIZE / 2) 100 | this._lastOffset.y = height - BOX_SIZE / 2 101 | } else if (distFromLeft === minDist) { 102 | this._dragX.setValue(-BOX_SIZE / 2) 103 | this._lastOffset.x = -BOX_SIZE / 2 104 | } else if (distFromRight === minDist) { 105 | this._dragX.setValue(width - BOX_SIZE / 2) 106 | this._lastOffset.x = width - BOX_SIZE / 2 107 | } 108 | 109 | this._dragX.extractOffset() 110 | this._dragY.extractOffset() 111 | } 112 | } 113 | _onLayout = ({ nativeEvent }) => { 114 | const { width, height } = nativeEvent.layout 115 | this.setState({ width, height }) 116 | } 117 | render() { 118 | return ( 119 | 120 | 135 | 150 | 151 | 156 | 170 | 171 | 172 | ) 173 | } 174 | } 175 | 176 | export default function ExpoGesturesExample() { 177 | return ( 178 | 179 | 180 | 181 | ) 182 | } 183 | 184 | const BOX_SIZE = 80 185 | 186 | const styles = StyleSheet.create({ 187 | container: { 188 | flex: 1, 189 | }, 190 | box: { 191 | position: 'absolute', 192 | width: BOX_SIZE, 193 | height: BOX_SIZE, 194 | borderColor: '#F5FCFF', 195 | backgroundColor: 'plum', 196 | borderRadius: BOX_SIZE / 2, 197 | }, 198 | }) 199 | -------------------------------------------------------------------------------- /examples/uikits/src/components/expo/ExpoLocationExample.js: -------------------------------------------------------------------------------- 1 | import * as Location from 'expo-location' 2 | import React, { useEffect, useState } from 'react' 3 | import { Button } from 'react-native' 4 | 5 | import Example from '../example' 6 | import JSONView from '../JSONView' 7 | 8 | export default function ExpoLocationExample() { 9 | const [item, setItem] = useState(null) 10 | 11 | return ( 12 | 13 |