Compare commits

..

76 Commits

Author SHA1 Message Date
Nicolas Gallagher
5cd533e6cc 0.3.1 2018-01-16 11:15:45 -08:00
Nicolas Gallagher
d5e8d85ce9 Fix example in babel plugin README 2018-01-16 11:11:50 -08:00
Nicolas Gallagher
e234568a34 Fix source links in documentation 2018-01-11 12:59:44 -08:00
Nicolas Gallagher
19cf0711bc [add] StyleSheet support for 'overscrollBehavior'
An experimental CSS property to control the behavior when the scroll
position of a scroll container reaches the edge of the scrollport. This
allows web apps to get closer to native scrolling behaviour and
performance.

https://wicg.github.io/overscroll-behavior/
https://developers.google.com/web/updates/2017/11/overscroll-behavior

Fix #765
2018-01-11 12:58:43 -08:00
Johannes
067e3f346f [fix] KeyboardAvoidingView missing 'this' binding
Close #762
2018-01-11 12:53:54 -08:00
Nicolas Gallagher
2117e44e9d [fix] limit Image loader deferral to 1000ms
This patch introduces a limit on how long image loading is deferred, and
mitigates an issue with lengthy delays to 'requestIdleCallback' in the
Chrome browser.

Fix #759
2018-01-11 12:08:30 -08:00
Nicolas Gallagher
902ba22877 Update to flow-bin@0.63.1 2018-01-09 17:49:52 -08:00
Nicolas Gallagher
60c2cd65df Update to lint-staged@6.0.0 2018-01-09 17:36:19 -08:00
Nicolas Gallagher
fde29326f1 Update to lerna@2.6.0 2018-01-09 17:35:53 -08:00
Nicolas Gallagher
44d795437e Update to storybook@3.3.6 2018-01-09 17:34:28 -08:00
Nicolas Gallagher
03598d869b Update to babel-plugin-tester@5.0.0 2018-01-09 17:31:52 -08:00
Nicolas Gallagher
a3e44a5c60 Update to enzyme@3.3.0 2018-01-09 17:27:38 -08:00
Nicolas Gallagher
02b124eceb Update yarn.lock 2018-01-08 18:53:05 -08:00
Nicolas Gallagher
91472bc3d6 0.3.0 2018-01-08 18:42:16 -08:00
Nicolas Gallagher
7f45c52ce7 Update to inline-style-prefixer@4.0.0 2018-01-08 18:31:36 -08:00
Nicolas Gallagher
b6ef1d3a36 [fix] handle "monospace" font-family on web
This hack corrects the inheritance and scaling of font size when the
font-family is "monospace".
2018-01-08 12:19:24 -08:00
Maxime Thirouin
fd6ccbcfb3 [fix] ignore more native-only View/Text props
Fix #735
Close #753
2018-01-08 12:10:46 -08:00
Maxime Thirouin
17614e348b Use prettier config instead of CLI args
This allows IDE plugins that rely on prettier config (introduced in
[1.6.0](https://github.com/prettier/prettier/pull/2434)) to detect
prettier and run it automatically with the correct config.

Close #757
2018-01-08 12:06:58 -08:00
Maxime Thirouin
c26ef0eb3b Run precommit hook automatically
Help to ensure that code is formatted and linted before commits and PRs.

Fix #755
Close #756
Close #754
2018-01-08 11:59:26 -08:00
Nicolas Gallagher
b78206d2f4 Remove 'transform-runtime' from webpack example 2018-01-08 11:49:28 -08:00
Nicolas Gallagher
69e0396fb1 Minor README edit 2018-01-05 16:35:39 -08:00
Nicolas Gallagher
6d9154196e [fix] StyleSheet.hairlineWidth guard against missing document.body 2018-01-04 14:30:24 -08:00
Nicolas Gallagher
87fdd6c73b [change] 'react-native-web' module organization and exports
The patch reorganizes the top-level module division of the
'react-native-web' project.

Previously, the package's exported modules were found in:

    apis/*/index.js
    components/*/index.js
    components/*/*.js
    modules/*/index.js
    propTypes/*.js

Now, each part of the exported API is found in:

    exports/*/index.js

And anything not directly part of the exported API is found in:

    modules/*/index.js
    vendor/*/index.js

Close #748
2018-01-04 14:30:24 -08:00
Nicolas Gallagher
209bd3aee1 [fix] babel-plugin support for 'react-native-web' module name
Now rewrites import/export/require statements from 'react-native-web'.
Install the plugin in the 'benchmarks' package.
2018-01-01 12:01:22 -08:00
Nicolas Gallagher
46e77d0b00 Change babel presets
Tune the compiled output to reduce file size.
2017-12-31 15:47:16 -08:00
Nicolas Gallagher
6f10f6be9c [fix] AppRegistry.unmountApplicationComponentAtRootTag
Fixes the bad import of a named export rather than the default export.
2017-12-30 19:06:03 -08:00
Nicolas Gallagher
0d0fdc15ac Minor docs fixes 2017-12-30 18:43:05 -08:00
Nicolas Gallagher
bff3f50ae0 Fix release script 2017-12-30 18:42:35 -08:00
Liron Yahdav
85aaa39206 [fix] i18n of styles when using 'setNativeProps'
Close #732
2017-12-30 14:31:22 -08:00
Nicolas Gallagher
b85a7062be Fix formatting of benchmarks table 2017-12-30 14:11:54 -08:00
Nicolas Gallagher
af47d5f414 [fix] React warning when using hitSlop prop
Make sure the hitSlop element has a 'key'.

Fix #743
2017-12-30 14:06:33 -08:00
Nicolas Gallagher
41d90e0238 [fix] ReactDOM hydration warnings in development
Don't try to hydrate from SSR HTML during development.

Fix #745
2017-12-30 14:02:01 -08:00
Nicolas Gallagher
86263a2fa0 Reorganize and add to benchmarks
Rearrange the benchmark code so that each implementation is
self-contained. Adds the SierpinskiTriangle case that 'emotion'
introduced in their fork of the 'react-native-web' benchmarks. And make
it possible to run benchmarks on a per-library basis.
2017-12-30 13:53:04 -08:00
Nicolas Gallagher
f6d1caab9d Document web-only Switch props 2017-12-26 09:19:04 +00:00
Nicolas Gallagher
1776891736 Improve project introduction and guides
Adopt the structure of the React README and improve the contribution
guidelines to include Facebook's CoC. Fix various links following the
move to a monorepo.
2017-12-26 09:12:15 +00:00
Nicolas Gallagher
f52a851972 Use an .eslintignore file 2017-12-24 12:50:44 +00:00
Nicolas Gallagher
3026465ae3 Monorepo
Introduces a monorepo structure, relies on yarn workspaces to share
dependencies, and lerna for syncing versions across the monorepo.

* Create 2 workspaces:
    'packages' and 'website'
* Create 2 public packages:
    'babel-plugin-react-native-web' and 'react-native-web'
* Create 1 private package:
    'benchmarks'

A simple release script runs the tests, builds the package assets,
increments the package version numbers, git commits and tags, publishes
the package to npm, pushes the changes to github, and releases the
website update.

Close #657
2017-12-24 12:33:41 +00:00
Nicolas Gallagher
14d87f4b30 Fix tests not running in CI
Accidentally stopped running tests after this patch:
9a5b932139
2017-12-23 13:16:53 +00:00
Nicolas Gallagher
5881f07323 Fix typo in benchmark results 2017-12-20 23:23:24 +00:00
Nicolas Gallagher
b545fe47a7 0.2.2 2017-12-20 23:07:21 +00:00
Mo Kouli
4da4dd57c4 [fix] AppContainer initial state
Regression introduced in:
217ad97bfd

Close #738
2017-12-20 23:05:00 +00:00
Nicolas Gallagher
3e12ddfb2b 0.2.1 2017-12-20 17:31:25 +00:00
Johannes
3ecf5d2ed2 [fix] corrupt hydrate import 2017-12-20 17:45:45 +01:00
Nicolas Gallagher
0a5acdb996 Update version in benchmark results table 2017-12-20 15:05:58 +00:00
Nicolas Gallagher
a712a58eba 0.2.0 2017-12-20 15:01:12 +00:00
Nicolas Gallagher
6de892c92b [add] CheckBox component
Implements the CheckBox component and adds a web-only 'color' prop to
allow the color of the checkbox to be customized.
2017-12-20 14:51:44 +00:00
Nicolas Gallagher
495defd69b [fix] StyleSheet.hairlineWidth on retina screens 2017-12-20 11:54:13 +00:00
Nicolas Gallagher
1a20fcfce6 [add] StyleSheet.compose
As per the recent addition to React Native.
2017-12-20 11:27:57 +00:00
Nicolas Gallagher
ed1e45a43d Link to yarnpkg website instead of npmjs 2017-12-20 11:13:17 +00:00
Nicolas Gallagher
556dc8926e [fix] ScrollView animated scrollTo
Rely on web's native smooth scrolling mechanism when implemented in the
browser: https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior

Fix #593
2017-12-19 15:52:30 +00:00
Nicolas Gallagher
66cf45b90b Update benchmark libraries and results
* Update all packages.
* Remove 'react-native-stylesheet'; since React 16 it is equivalent in
  performance to using the full 'react-native-web' View implementation.
* Remove 'styled-components/primitives'; it's as slow as
  'styled-components'.
* Record latest benchmark results and hardware.
2017-12-19 15:27:05 +00:00
Nicolas Gallagher
d1e49e06e6 Remove unmaintained starter kit 2017-12-18 17:49:51 +00:00
Nicolas Gallagher
8bf28dbe43 Document more instance methods in Direct Manipulation guide
Fix #704
2017-12-18 17:45:23 +00:00
Nicolas Gallagher
9ae95d0797 Add further details to Getting Started guide
Close #728
2017-12-18 17:33:44 +00:00
Nicolas Gallagher
321051b723 [add] ART export
Improve API compatibility with React Native by exporting 'react-art' as
'ART'.

Fix #602
2017-12-18 17:16:09 +00:00
Nicolas Gallagher
5f68542529 [fix] Touchable use with react-test-renderer
The object returned by 'ReactDOM.findDOMNode' when rendered by
'react-test-renderer' doesn't match the DOM API for an element. Only
attempt to bind the listener if 'addEventListener' is present on the
object.

Fix #720
2017-12-18 16:35:45 +00:00
Nicolas Gallagher
82c044ee33 [fix] use ReactDOM.hydrate in AppRegistry.runApplication
Allows AppRegistry to hydrate server-side rendered apps.

Fix #733
2017-12-18 16:15:47 +00:00
Nicolas Gallagher
9bcc67e73a [fix] top-level API exports
Also fixes importing these APIs from 'react-native' when used with the
Babel plugin.
2017-12-18 16:15:03 +00:00
Nicolas Gallagher
f1ce6c2acb [fix] AppRegistry.getApplication style element keys
Fix #734
2017-12-18 15:26:18 +00:00
Nicolas Gallagher
034108a2a0 [add] SafeAreaView component 2017-12-06 14:34:22 -08:00
Nicolas Gallagher
f96d7b868f [change] update PanResponder implementation
Fix #171
2017-12-06 14:23:35 -08:00
Nicolas Gallagher
0dfe319d41 [change] update the Animated implementation
Replaces the 'animated' package with the latest implementation from
React Native. Requires a few imports to be replaced.

Close #716
Fix #714
Fix #688
2017-12-06 14:01:36 -08:00
Kenneth Kufluk
b7e970f4e6 [add] Picker and Picker.Item components
Close #705
2017-12-04 16:15:23 -08:00
Nicolas Gallagher
02e62ad5d6 Lint fixes 2017-12-02 16:08:56 -08:00
Nicolas Gallagher
541d2458fb [change] Image no longer accepts children
Align with recent changes to the React Native API.
2017-12-02 16:04:27 -08:00
Nicolas Gallagher
b1e860ab40 Add ImageBackground docs 2017-12-02 16:04:27 -08:00
Louis Lagrange
e8eab9b3ec [add] ImageBackground component
Close #696
2017-12-02 16:04:19 -08:00
Nicolas Gallagher
6bc76c3c92 Update yarn script syntax 2017-12-02 15:58:47 -08:00
Nicolas Gallagher
2acd8e477c Update raf and debounce modules 2017-12-02 15:58:47 -08:00
Nicolas Gallagher
ff2b0c9bdc Update to React@16.2 dev dependencies 2017-12-02 15:58:47 -08:00
Nicolas Gallagher
79208720d1 Update webpack tools 2017-12-02 15:58:47 -08:00
Nicolas Gallagher
fca04c4125 Update enzyme 2017-12-02 15:58:47 -08:00
Nicolas Gallagher
5b5b72cc19 Update eslint and prettier 2017-12-02 15:58:47 -08:00
Nicolas Gallagher
217ad97bfd [change] Update Flow and types 2017-12-02 15:58:47 -08:00
Nicolas Gallagher
3e3cfc5325 0.1.16 2017-12-02 14:48:35 -08:00
Nicolas Gallagher
da86ea98fc [fix] NetInfo event listeners and types
* Fix 'addEventListener' handler registration.
* Fix event object provided to handlers.
* Fix event object type - always include 'type' and 'effectiveType'.
* Fix unit test semantics.
* Fix documented NetInfo types.

Close #724
2017-12-02 12:47:12 -08:00
514 changed files with 12802 additions and 4887 deletions

5
.babelrc Normal file
View File

@@ -0,0 +1,5 @@
{
"presets": [
"./scripts/babel/preset"
]
}

4
.eslintignore Normal file
View File

@@ -0,0 +1,4 @@
coverage
dist
node_modules
packages/**/vendor/*

View File

@@ -56,6 +56,7 @@
"no-dupe-class-members": 2,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-duplicate-imports": 2,
"no-empty-character-class": 2,
"no-empty-pattern": 2,
"no-eval": 2,

View File

@@ -1,14 +1,17 @@
[version]
^0.63.0
[ignore]
.*/__tests__/.*
.*/benchmarks/.*
.*/docs/.*
.*/node_modules/animated/*
<PROJECT_ROOT>/.*/__tests__/.*
<PROJECT_ROOT>/packages/benchmarks/.*
<PROJECT_ROOT>/packages/.*/dist/.*
<PROJECT_ROOT>/website/.*
.*/node_modules/babel-plugin-transform-react-remove-prop-types/*
[include]
[libs]
types
<PROJECT_ROOT>/types
[options]
unsafe.enable_getters_and_setters=true

View File

@@ -17,7 +17,7 @@ Fork, then clone the repo:
git clone https://github.com/your-username/react-native-web.git
```
Install dependencies (requires [yarn](https://yarnpkg.com/en/docs/install):
Install dependencies (requires [yarn](https://yarnpkg.com/en/docs/install)):
```
yarn
@@ -25,6 +25,12 @@ yarn
## Automated tests
To run the linter:
```
yarn lint
```
To run flow:
```
@@ -40,16 +46,30 @@ yarn jest
…in watch mode:
```
yarn jest:watch
yarn jest --watch
```
To run all automated tests:
To run all these automated tests:
```
yarn test
```
## Visual tests
## Compile and build
To compile the `react-native-web` source code:
```
yarn compile
```
…in watch mode:
```
yarn compile --watch
```
## Website and visual tests
To run the interactive storybook:
@@ -57,47 +77,20 @@ To run the interactive storybook:
yarn docs:start
```
To generate a static build of the storybook:
When you're also making changes to the 'react-native-web' source files, run this command in another process:
```
yarn docs:build
yarn compile --watch
```
To run the performance benchmarks in a browser (opening `./benchmarks/index.html`):
## Benchmarks
To run the performance benchmarks in a browser (opening `./packages/benchmarks/index.html`):
```
yarn benchmark
```
## Compile and build
To compile the source code to `dist`:
```
yarn compile
```
To create a UMD bundle of the library:
```
yarn build
```
### Pre-commit
To format and lint code before commit:
```
yarn precommit
```
To format and lint the entire project:
```
yarn fmt
yarn lint
```
### New Features
Please open an issue with a proposal for a new feature or refactoring before
@@ -106,13 +99,12 @@ that we won't want to accept.
## Pull requests
**Before submitting a pull request,** please make sure the following is done:
**Before submitting a pull request**, please make sure the following is done:
1. Fork the repository and create your branch from `master`.
2. If you've added code that should be tested, add tests!
3. If you've changed APIs, update the documentation.
4. Ensure the tests pass (`yarn test`).
5. Lint and format your code (`yarn fmt && yarn lint`).
You can now submit a pull request, referencing any issues it addresses.
@@ -123,3 +115,18 @@ After you have submitted your pull request, we'll try to get back to you as
soon as possible. We may suggest some changes or improvements.
Thank you for contributing!
## Releases
To commit, publish, and push a final version:
```
yarn release <version>
```
Release candidates or versions that you'd like to publish to npm, but do not
want to produce a commit and push it to GitHub:
```
yarn release <version> --skip-git
```

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
coverage
node_modules
dist

View File

@@ -1,8 +1,17 @@
language: node_js
node_js:
- "6"
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- "8"
before_install:
# Install Yarn
- curl -o- -L https://yarnpkg.com/install.sh | bash
- export PATH=$HOME/.yarn/bin:$PATH
cache:
yarn: true
directories:
- node_modules
script:
- yarn lint
- yarn test

206
README.md
View File

@@ -1,127 +1,137 @@
# React Native for Web
[![Build Status][travis-image]][travis-url]
[![npm version][npm-image]][npm-url]
[![npm version][package-badge]][package-url] [![Build Status][ci-badge]][ci-url] [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://reactjs.org/docs/how-to-contribute.html#your-first-pull-request)
"React Native for Web" brings the platform-agnostic Components and APIs of
[React Native][react-native-url] to the Web.
Browse the [interactive
documentation](https://necolas.github.io/react-native-web/storybook/) or [try
it out](https://glitch.com/edit/#!/react-native-web-playground) on Glitch.
* **High-quality user interfaces**: React Native for Web makes it easy to
create [fast](packages/benchmarks/README.md), adaptive web UIs in JavaScript.
It provides native-like interactions, support for multiple input modes (touch,
mouse, keyboard), optimized vendor-prefixed styles, built-in support for RTL
layout, built-in accessibility, and integrates with React Dev Tools.
## Features
* **Write once, render anywhere**: React Native for Web interoperates with
existing React DOM components and is compatible with the majority of the
React Native API. You can develop new components for native and web without
rewriting existing code. React Native for Web can also render to HTML and
critical CSS on the server using Node.js.
* Interoperability with ReactDOM components.
* Native-like touch handling.
* Built-in integration with web accessibility APIs.
* Built-in support for LTR and RTL layouts.
* Built-in expressive and reliable subset of CSS.
* Optimized, vendor-prefixed CSS with [good runtime performance](benchmarks/README.md).
* Server-side rendering of HTML and critical CSS.
* Browser support: Chrome, Firefox, Safari >= 7, IE 10, Edge.
Who is using React Native for Web? [Twitter](https://mobile.twitter.com),
[Major League Soccer](https://matchcenter.mlssoccer.com), [The
Times](https://github.com/newsuk/times-components), [React Native's
documentation](http://facebook.github.io/react-native/).
Browser support: Chrome, Firefox, Safari >= 7, IE 10, Edge.
## Quick start
Install in your existing app using `yarn` or `npm`:
The easiest way to get started with React Native for Web is to use this
[ready-to-go project on Glitch](https://glitch.com/edit/#!/react-native-web-playground).
You dont need to install anything to try it out.
```
yarn add react react-dom react-native-web
```
Add the `react-native-web/babel` plugin to your Babel configuration. This will
alias `react-native` to `react-native-web` and exclude any modules not required
by the app.
```json
{
"plugins": [
"react-native-web/babel"
],
"presets": [
"react-native"
]
}
```
(For React/ReactDOM 15.4 15.6 support, install `react-native-web@<0.1.0`)
See the [Getting Started](docs/guides/getting-started.md) guide for more details.
If you are unfamiliar with setting up a React web project, please follow the
recommendations in the [React documentation](https://reactjs.org/).
## Documentation
The [interactive
documentation](https://necolas.github.io/react-native-web/storybook/) shows all
the supported APIs and Components.
You can find the React Native for Web API documentation [on the
website][website-url].
Guides:
Please refer to the [React Native documentation][react-native-url] for more
design details, and for information about the [Gesture Responder
system](https://facebook.github.io/react-native/docs/gesture-responder-system.html)
and [animations](https://facebook.github.io/react-native/docs/animations.html).
* [Getting started](docs/guides/getting-started.md)
* [Style](docs/guides/style.md)
* [Accessibility](docs/guides/accessibility.md)
* [Direct manipulation](docs/guides/direct-manipulation.md)
* [Internationalization](docs/guides/internationalization.md)
* [Advanced use](docs/guides/advanced.md)
* [Known issues](docs/guides/known-issues.md)
### Installation
## Example code
Install using `yarn` or `npm`:
```js
import React from 'react'
import { AppRegistry, Image, StyleSheet, Text, View } from 'react-native'
// Components
const Card = ({ children }) => <View style={styles.card}>{children}</View>
const Title = ({ children }) => <Text style={styles.title}>{children}</Text>
const Photo = ({ uri }) => <Image source={{ uri }} style={styles.image} />
const App = () => (
<Card>
<Title>App Card</Title>
<Photo uri="/some-photo.jpg" />
</Card>
)
// Styles
const styles = StyleSheet.create({
card: {
flexGrow: 1,
justifyContent: 'center'
},
title: {
fontSize: '1.25rem',
fontWeight: 'bold'
},
image: {
height: 40,
marginVertical: 10,
width: 40
}
})
// App registration and rendering
AppRegistry.registerComponent('MyApp', () => App)
AppRegistry.runApplication('MyApp', { rootTag: document.getElementById('react-root') })
```
yarn add react react-dom react-native-web
yarn add --dev babel-plugin-react-native-web
```
## Starter kits
### Guides
* [Glitch](https://glitch.com/edit/#!/react-native-web-playground)
* [create-react-app](https://github.com/facebookincubator/create-react-app)
* [re-start](https://github.com/react-everywhere/re-start)
* [Getting started](website/guides/getting-started.md)
* [Style](website/guides/style.md)
* [Accessibility](website/guides/accessibility.md)
* [Internationalization](website/guides/internationalization.md)
* [Direct manipulation](website/guides/direct-manipulation.md)
* [Advanced use](website/guides/advanced.md)
## Related projects
## Examples
* [react-primitives](https://github.com/lelandrichardson/react-primitives/)
* [react-sketchapp](https://github.com/airbnb/react-sketchapp)
* [reactxp](https://github.com/microsoft/reactxp)
* [react-native-web-player](https://github.com/dabbott/react-native-web-player)
There are several examples [on the website][website-url] and in the [website's
source code](./website). Here is an example to get you started:
```js
import React from 'react';
import { AppRegistry, StyleSheet, Text, View } from 'react-native';
class App extends React.Component {
render() {
return (
<View style={styles.box}>
<Text style={styles.text}>Hello, world!</Text>
</View>
);
}
}
const styles = StyleSheet.create({
box: { padding: 10 },
text: { fontWeight: 'bold' }
});
AppRegistry.registerComponent('App', () => App);
AppRegistry.runApplication('App', { rootTag: document.getElementById('react-root') });
```
This example will render the `App` into a container on the page.
You'll notice that there is no reference to `react-dom`; the `App` component is
defined using the platform-agnostic APIs and Components introduced by React
Native. This allows the app to be rendered to web and native platforms.
## Contributing
The main purpose of this repository is to help evolve React web and native
development towards the platform-agnostic design of React Native, and in the
process make it faster and easier to build high-quality experiences for the web
with React. Development happens in the open on GitHub, and we are grateful for
contributing bugfixes and improvements. Read below to learn how you can take
part in improving React Native for Web.
### Code of conduct
Facebook has adopted a [Code of Conduct][code-of-conduct] that this project
expects all participants to adhere to. Please read the full text so that you
can understand what actions will and will not be tolerated.
### Contributing guide
Read the [contributing guide][contributing-url] to learn about the
development process, how to propose bugfixes and improvements, and how to build
and test your changes to React Native for Web.
### Good first issues
To help you get you familiar with the contribution process, there is a list of
[good first issues][good-first-issue-url] that contain bugs which have a
relatively limited scope. This is a great place to get started.
## License
React Native for Web is [BSD licensed](LICENSE).
React Native for Web is [BSD licensed](./LICENSE).
[npm-image]: https://badge.fury.io/js/react-native-web.svg
[npm-url]: https://npmjs.org/package/react-native-web
[package-badge]: https://img.shields.io/npm/v/react-native-web.svg?style=flat
[package-url]: https://yarnpkg.com/en/package/react-native-web
[ci-badge]: https://travis-ci.org/necolas/react-native-web.svg?branch=master
[ci-url]: https://travis-ci.org/necolas/react-native-web
[website-url]: https://necolas.github.io/react-native-web/storybook/
[react-native-url]: https://facebook.github.io/react-native/
[travis-image]: https://travis-ci.org/necolas/react-native-web.svg?branch=master
[travis-url]: https://travis-ci.org/necolas/react-native-web
[contributing-url]: ./.github/CONTRIBUTING.md
[good-first-issue-url]: https://github.com/necolas/react-native-web/labels/good%20first%20issue
[code-of-conduct]: https://code.facebook.com/codeofconduct

View File

@@ -1,161 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`1. Rewrite react-native paths for react-native-web 1`] = `
"
import { View } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
import View from 'react-native-web/dist/components/View';
"
`;
exports[`2. Rewrite react-native paths for react-native-web 1`] = `
"
import { Switch, Text, View as MyView, ViewPropTypes } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
import Switch from 'react-native-web/dist/components/Switch';
import Text from 'react-native-web/dist/components/Text';
import MyView from 'react-native-web/dist/components/View';
import ViewPropTypes from 'react-native-web/dist/components/View/ViewPropTypes';
"
`;
exports[`3. Rewrite react-native paths for react-native-web 1`] = `
"
import { createElement, Switch, StyleSheet } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
import createElement from 'react-native-web/dist/modules/createElement';
import Switch from 'react-native-web/dist/components/Switch';
import StyleSheet from 'react-native-web/dist/apis/StyleSheet';
"
`;
exports[`4. Rewrite react-native paths for react-native-web 1`] = `
"
import { InvalidThing, TouchableOpacity } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
import { InvalidThing } from 'react-native-web';
import TouchableOpacity from 'react-native-web/dist/components/Touchable/TouchableOpacity';
"
`;
exports[`5. Rewrite react-native paths for react-native-web 1`] = `
"
import * as RNW from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
import * as RNW from 'react-native-web';
"
`;
exports[`6. Rewrite react-native paths for react-native-web 1`] = `
"
const { View } = require('react-native');
↓ ↓ ↓ ↓ ↓ ↓
const View = require('react-native-web/dist/components/View');
"
`;
exports[`7. Rewrite react-native paths for react-native-web 1`] = `
"
let { Switch, Text, View: MyView } = require('react-native');
↓ ↓ ↓ ↓ ↓ ↓
let Switch = require('react-native-web/dist/components/Switch');
let Text = require('react-native-web/dist/components/Text');
let MyView = require('react-native-web/dist/components/View');
"
`;
exports[`8. Rewrite react-native paths for react-native-web 1`] = `
"
var { createElement, Switch, StyleSheet } = require('react-native');
↓ ↓ ↓ ↓ ↓ ↓
var createElement = require('react-native-web/dist/modules/createElement');
var Switch = require('react-native-web/dist/components/Switch');
var StyleSheet = require('react-native-web/dist/apis/StyleSheet');
"
`;
exports[`9. Rewrite react-native paths for react-native-web 1`] = `
"
const { InvalidThing, TouchableOpacity } = require('react-native');
↓ ↓ ↓ ↓ ↓ ↓
const TouchableOpacity = require('react-native-web/dist/components/Touchable/TouchableOpacity');
"
`;
exports[`10. Rewrite react-native paths for react-native-web 1`] = `
"
export { View } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
export { default as View } from 'react-native-web/dist/components/View';
"
`;
exports[`11. Rewrite react-native paths for react-native-web 1`] = `
"
export { Switch, Text, View as MyView, ViewPropTypes } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
export { default as Switch } from 'react-native-web/dist/components/Switch';
export { default as Text } from 'react-native-web/dist/components/Text';
export { default as MyView } from 'react-native-web/dist/components/View';
export { default as ViewPropTypes } from 'react-native-web/dist/components/View/ViewPropTypes';
"
`;
exports[`12. Rewrite react-native paths for react-native-web 1`] = `
"
export { createElement, Switch, StyleSheet } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
export { default as createElement } from 'react-native-web/dist/modules/createElement';
export { default as Switch } from 'react-native-web/dist/components/Switch';
export { default as StyleSheet } from 'react-native-web/dist/apis/StyleSheet';
"
`;
exports[`13. Rewrite react-native paths for react-native-web 1`] = `
"
export { InvalidThing, TouchableOpacity } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
export { InvalidThing } from 'react-native-web';
export { default as TouchableOpacity } from 'react-native-web/dist/components/Touchable/TouchableOpacity';
"
`;
exports[`14. Rewrite react-native paths for react-native-web 1`] = `
"
export { default as RNW } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
export { default as RNW } from 'react-native-web';
"
`;

View File

@@ -1,46 +0,0 @@
const plugin = require('..');
const pluginTester = require('babel-plugin-tester');
pluginTester({
plugin,
snapshot: true,
tests: [
// import react-native
"import { View } from 'react-native';",
"import { Switch, Text, View as MyView, ViewPropTypes } from 'react-native';",
"import { createElement, Switch, StyleSheet } from 'react-native';",
"import { InvalidThing, TouchableOpacity } from 'react-native';",
"import * as RNW from 'react-native';",
// import react-native-web
// "import { View } from 'react-native-web';",
// "import { Switch, Text, View as MyView } from 'react-native-web';",
// "import { createElement, Switch, StyleSheet } from 'react-native-web';",
// "import { InvalidThing, TouchableOpacity } from 'react-native-web';",
// "import * as RNW from 'react-native-web';",
// require react-native
"const { View } = require('react-native');",
"let { Switch, Text, View: MyView } = require('react-native');",
"var { createElement, Switch, StyleSheet } = require('react-native');",
"const { InvalidThing, TouchableOpacity } = require('react-native');",
// require react-native-web
// "const { View } = require('react-native-web');",
// "let { Switch, Text, View: MyView } = require('react-native-web');",
// "var { createElement, Switch, StyleSheet } = require('react-native-web');",
// "const { InvalidThing, TouchableOpacity } = require('react-native-web');",
// export react-native
"export { View } from 'react-native';",
"export { Switch, Text, View as MyView, ViewPropTypes } from 'react-native';",
"export { createElement, Switch, StyleSheet } from 'react-native';",
"export { InvalidThing, TouchableOpacity } from 'react-native';",
"export { default as RNW } from 'react-native';",
{
code: "const RNW = require('react-native');",
output: "const RNW = require('react-native');",
snapshot: false
}
]
});

View File

@@ -1,181 +0,0 @@
const getDistLocation = importName => {
const root = 'react-native-web/dist';
switch (importName) {
// apis
case 'Animated':
case 'AppRegistry':
case 'AppState':
case 'AsyncStorage':
case 'BackHandler':
case 'Clipboard':
case 'Dimensions':
case 'Easing':
case 'I18nManager':
case 'InteractionManager':
case 'Keyboard':
case 'Linking':
case 'NetInfo':
case 'PanResponder':
case 'PixelRatio':
case 'Platform':
case 'StyleSheet':
case 'UIManager':
case 'Vibration': {
return `${root}/apis/${importName}`;
}
// components
case 'ActivityIndicator':
case 'Button':
case 'FlatList':
case 'Image':
case 'KeyboardAvoidingView':
case 'ListView':
case 'Modal':
case 'Picker':
case 'ProgressBar':
case 'RefreshControl':
case 'ScrollView':
case 'SectionList':
case 'Slider':
case 'StatusBar':
case 'Switch':
case 'Text':
case 'TextInput':
case 'View':
case 'VirtualizedList': {
return `${root}/components/${importName}`;
}
case 'Touchable':
case 'TouchableHighlight':
case 'TouchableNativeFeedback':
case 'TouchableOpacity':
case 'TouchableWithoutFeedback': {
return `${root}/components/Touchable/${importName}`;
}
// modules
case 'createElement':
case 'findNodeHandle':
case 'NativeModules':
case 'processColor':
case 'render':
case 'unmountComponentAtNode': {
return `${root}/modules/${importName}`;
}
// propTypes
case 'ColorPropType':
case 'EdgeInsetsPropType':
case 'PointPropType': {
return `${root}/propTypes/${importName}`;
}
case 'TextPropTypes': {
return `${root}/components/Text/${importName}`;
}
case 'ViewPropTypes': {
return `${root}/components/View/${importName}`;
}
default:
return;
}
};
const isReactNativeRequire = (t, node) => {
const { declarations } = node;
if (declarations.length > 1) {
return false;
}
const { id, init } = declarations[0];
return (
t.isObjectPattern(id) &&
t.isCallExpression(init) &&
t.isIdentifier(init.callee) &&
init.callee.name === 'require' &&
init.arguments.length === 1 &&
init.arguments[0].value === 'react-native'
);
};
module.exports = function({ types: t }) {
return {
name: 'Rewrite react-native paths for react-native-web',
visitor: {
ImportDeclaration(path) {
const { source, specifiers } = path.node;
if (source && source.value === 'react-native' && specifiers.length) {
const imports = specifiers
.map(specifier => {
if (t.isImportSpecifier(specifier)) {
const importName = specifier.imported.name;
const distLocation = getDistLocation(importName);
if (distLocation) {
return t.importDeclaration(
[t.importDefaultSpecifier(t.identifier(specifier.local.name))],
t.stringLiteral(distLocation)
);
}
}
return t.importDeclaration([specifier], t.stringLiteral('react-native-web'));
})
.filter(Boolean);
path.replaceWithMultiple(imports);
}
},
ExportNamedDeclaration(path) {
const { source, specifiers } = path.node;
if (source && source.value === 'react-native' && specifiers.length) {
const exports = specifiers
.map(specifier => {
if (t.isExportSpecifier(specifier)) {
const exportName = specifier.exported.name;
const localName = specifier.local.name;
const distLocation = getDistLocation(localName);
if (distLocation) {
return t.exportNamedDeclaration(
null,
[t.exportSpecifier(t.identifier('default'), t.identifier(exportName))],
t.stringLiteral(distLocation)
);
}
return t.exportNamedDeclaration(
null,
[specifier],
t.stringLiteral('react-native-web')
);
}
})
.filter(Boolean);
path.replaceWithMultiple(exports);
}
},
VariableDeclaration(path) {
if (isReactNativeRequire(t, path.node)) {
const { id } = path.node.declarations[0];
const imports = id.properties
.map(identifier => {
const distLocation = getDistLocation(identifier.key.name);
if (distLocation) {
return t.variableDeclaration(path.node.kind, [
t.variableDeclarator(
t.identifier(identifier.value.name),
t.callExpression(t.identifier('require'), [t.stringLiteral(distLocation)])
)
]);
}
})
.filter(Boolean);
path.replaceWithMultiple(imports);
}
}
}
};
};

View File

@@ -1,52 +0,0 @@
# Performance
To run these benchmarks from the root of the project:
```
yarn benchmark
open ./benchmarks/index.html
```
Append `?fastest` to the URL to include the fastest "other libraries", and
`?all` to include all the "other libraries".
## Notes
The components used in the render benchmarks are simple enough to be
implemented by multiple UI or style libraries. The implementations are not
equivalent in functionality. For example, React Native for Web's stylesheet is
unique in that it also converts React Native styles to DOM styles, has
deterministic resolution, and supports RTL layout.
`react-native-web/stylesheet` is a comparative baseline that implements a
simple `View` without much of React Native's functionality.
## Benchmark results
Typical render timings*: mean ± two standard deviations.
| Implementation | Deep tree (ms) | Wide tree (ms) | Tweets (ms) |
| :--- | ---: | ---: | ---: |
| `css-modules` | `88.83` `±18.63` | `198.79` `±22.98` | |
| `react-native-web/stylesheet@0.0.121` | `91.17` `±19.29` | `209.67` `±32.38` | |
| `react-native-web@0.0.121` | `124.21` `±16.84` | `264.55` `±38.75` | `16.90` `±7.30ms` |
Other libraries
| Implementation | Deep tree (ms) | Wide tree (ms) |
| :--- | ---: | ---: |
| `aphrodite@1.2.3` | `91.73` `±41.63` | `197.72` `±44.90` |
| `styletron@2.5.1` | `94.73` `±37.58` | `201.81` `±57.93` |
| `glamor@2.20.40` | `146.60` `±26.73` | `277.46` `±29.17` |
| `emotion@7.2.2` | `150.79` `±38.29` | `282.18` `±41.79` |
| `react-jss@7.1.0` | `201.83` `±34.65` | `428.61` `±47.8` |
| `reactxp@0.42.1` | `262.69` `±24.14` | `595.20` `±66.17` |
| `styled-components@2.1.2` | `280.59` `±31.77` | `599.00` `±62.99` |
| `styled-components/primitives@2.1.2` | `291.74` `±48.96` | `606.57` `±78.18` |
| `radium@0.19.4` | `563.94` `±69.91` | `1139.18` `±152.59` |
These results indicate that style render performance is not a significant
differentiating factor between `aphrodite`, `css-modules`, `react-native-web`,
and `styletron`.
*MacBook Pro (13-inch, Early 2015); 3.1 GHz Intel Core i7; 16 GB 1867 MHz DDR3. Google Chrome 58 (2x CPU slowdown).

View File

@@ -1,66 +0,0 @@
import aphrodite from './src/aphrodite';
import cssModules from './src/css-modules';
import emotion from './src/emotion';
import glamor from './src/glamor';
import jss from './src/jss';
import radium from './src/radium';
import reactNative from './src/react-native';
import reactNativeStyleSheet from './src/react-native-stylesheet';
import styledComponents from './src/styled-components';
import styletron from './src/styletron';
import xp from './src/reactxp';
import renderDeepTree from './tests/renderDeepTree';
import renderTweet from './tests/renderTweet';
import renderWideTree from './tests/renderWideTree';
const testAll = window.location.search === '?all';
const testFastest = window.location.search === '?fastest';
const coreTests = [
() => renderTweet('react-native-web', reactNative),
() => renderDeepTree('css-modules', cssModules),
() => renderWideTree('css-modules', cssModules),
() => renderDeepTree('react-native-web/stylesheet', reactNativeStyleSheet),
() => renderWideTree('react-native-web/stylesheet', reactNativeStyleSheet),
() => renderDeepTree('react-native-web', reactNative),
() => renderWideTree('react-native-web', reactNative)
];
const fastestTests = [
() => renderDeepTree('aphrodite', aphrodite),
() => renderWideTree('aphrodite', aphrodite),
() => renderDeepTree('styletron', styletron),
() => renderWideTree('styletron', styletron)
];
/**
* Optionally run tests using other libraries
*/
const restTests = [
() => renderDeepTree('emotion', emotion),
() => renderWideTree('emotion', emotion),
() => renderDeepTree('glamor', glamor),
() => renderWideTree('glamor', glamor),
() => renderDeepTree('radium', radium),
() => renderWideTree('radium', radium),
() => renderDeepTree('reactxp', xp),
() => renderWideTree('reactxp', xp),
() => renderDeepTree('react-jss', jss),
() => renderWideTree('react-jss', jss),
() => renderDeepTree('styled-components', styledComponents),
() => renderWideTree('styled-components', styledComponents)
];
const tests = [...coreTests];
if (testFastest) {
tests.push(...fastestTests);
}
if (testAll) {
tests.push(...fastestTests);
tests.push(...restTests);
}
// run benchmarks
tests.reduce((promise, test) => promise.then(test()), Promise.resolve());

View File

@@ -1,22 +0,0 @@
{
"name": "benchmarks",
"private": true,
"dependencies": {
"aphrodite": "^1.2.5",
"classnames": "^2.2.5",
"emotion": "^8.0.9",
"glamor": "^2.20.40",
"marky": "^1.2.0",
"radium": "^0.19.6",
"react-jss": "^7.2.0",
"react-primitives": "^0.4.3",
"reactxp": "^0.42.11",
"styled-components": "^2.2.3",
"styletron-client": "^2.5.7",
"styletron-utils": "^2.5.4"
},
"devDependencies": {
"css-loader": "^0.28.7",
"style-loader": "^0.19.0"
}
}

View File

@@ -1,7 +0,0 @@
import Box from './components/Box/aphrodite';
import View from './components/View/aphrodite';
export default {
Box,
View
};

View File

@@ -1,49 +0,0 @@
/* eslint-disable react/prop-types */
import React from 'react';
import StyleSheet from 'react-native/apis/StyleSheet';
import View from '../View/react-native-stylesheet';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
<View
{...other}
style={[
styles[`color${color}`],
fixed && styles.fixed,
layout === 'row' && styles.row,
outer && styles.outer
]}
/>
);
const styles = StyleSheet.create({
outer: {
padding: 4
},
row: {
flexDirection: 'row'
},
color0: {
backgroundColor: '#222'
},
color1: {
backgroundColor: '#666'
},
color2: {
backgroundColor: '#999'
},
color3: {
backgroundColor: 'blue'
},
color4: {
backgroundColor: 'orange'
},
color5: {
backgroundColor: 'red'
},
fixed: {
width: 20,
height: 20
}
});
export default Box;

View File

@@ -1,30 +0,0 @@
import styled from 'styled-components/primitives';
const getColor = color => {
switch (color) {
case 0:
return '#222';
case 1:
return '#666';
case 2:
return '#999';
case 3:
return 'blue';
case 4:
return 'orange';
case 5:
return 'red';
default:
return 'transparent';
}
};
const Box = styled.View`
flex-direction: ${props => (props.layout === 'column' ? 'column' : 'row')};
padding: ${props => (props.outer ? '4px' : '0')};
height: ${props => (props.fixed ? '20px' : 'auto')};
width: ${props => (props.fixed ? '20px' : 'auto')};
background-color: ${props => getColor(props.color)};
`;
export default Box;

View File

@@ -1,35 +0,0 @@
/* eslint-disable react/prop-types */
import React from 'react';
import StyleSheet from 'react-native/apis/StyleSheet';
import registry from 'react-native/apis/StyleSheet/registry';
const emptyObject = {};
class View extends React.Component {
render() {
const { style, ...other } = this.props;
const styleProps = registry.resolve([styles.root, style]) || emptyObject;
return <div {...other} {...styleProps} />;
}
}
const styles = StyleSheet.create({
root: {
alignItems: 'stretch',
borderWidth: 0,
borderStyle: 'solid',
boxSizing: 'border-box',
display: 'flex',
flexBasis: 'auto',
flexDirection: 'column',
flexShrink: 0,
margin: 0,
padding: 0,
position: 'relative',
// fix flexbox bugs
minHeight: 0,
minWidth: 0
}
});
export default View;

View File

@@ -1,9 +0,0 @@
import Box from './components/Box/css-modules';
import View from './components/View/css-modules';
const api = {
Box,
View
};
export default api;

View File

@@ -1,7 +0,0 @@
import Box from './components/Box/emotion';
import View from './components/View/emotion';
export default {
Box,
View
};

View File

@@ -1,7 +0,0 @@
import Box from './components/Box/glamor';
import View from './components/View/glamor';
export default {
Box,
View
};

View File

@@ -1,9 +0,0 @@
import Box from './components/Box/jss';
import View from './components/View/jss';
const api = {
Box,
View
};
export default api;

View File

@@ -1,9 +0,0 @@
import Box from './components/Box/radium';
import View from './components/View/radium';
const api = {
Box,
View
};
export default api;

View File

@@ -1,9 +0,0 @@
import Box from './components/Box/react-native-stylesheet';
import View from './components/View/react-native-stylesheet';
const api = {
Box,
View
};
export default api;

View File

@@ -1,9 +0,0 @@
import Box from './components/Box/react-native';
import Tweet from './components/Tweet';
import { View } from 'react-native';
export default {
Box,
Tweet,
View
};

View File

@@ -1,7 +0,0 @@
import Box from './components/Box/styled-components';
import styled from 'styled-components/primitives';
export default {
Box,
View: styled.View
};

View File

@@ -1,7 +0,0 @@
import Box from './components/Box/styled-components';
import View from './components/View/styled-components';
export default {
Box,
View
};

View File

@@ -1,7 +0,0 @@
import Box from './components/Box/styletron';
import View from './components/View/styletron';
export default {
Box,
View
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,31 +0,0 @@
# Known issues
## Safari flexbox performance
Safari version prior to 10.1 can suffer from extremely [poor flexbox
performance](https://bugs.webkit.org/show_bug.cgi?id=150445). The recommended
way to work around this issue (as used on mobile.twitter.com) is to set
`display:block` on Views in your element hierarchy that you know don't need
flexbox layout.
## Missing modules and components
Not all of the views present on iOS/Android are currently available on Web. We
are very much interested in the community's feedback on the next set of modules
and views.
Not all the modules or views for iOS/Android can be implemented on Web. In some
cases it will be necessary to use a Web counterpart or to guard the use of a
module with `Platform.OS` (e.g. `NativeModules`)
## Missing component props
There are properties that do not work across all platforms. All web-specific
props are annotated with `(web)` in the documentation.
## Platform parity
There are some known issues in React Native where APIs could be made more
consistent between platforms. For example, React Native for Web includes
`ActivityIndicator` and a horizontal `ProgressBar` for Web use, in anticipation
of the convergence between the iOS and Android components in React Native.

View File

@@ -1,11 +0,0 @@
{
"scripts": {
"build": "yarn && build-storybook -o ./dist -c ./storybook/.storybook",
"start": "start-storybook -p 9001 -c ./storybook/.storybook",
"release": "yarn build && git checkout gh-pages && rm -rf ../storybook && mv dist ../storybook && git add -A && git commit -m \"Storybook deploy\" && git push origin gh-pages && git checkout -"
},
"dependencies": {
"@storybook/addon-options": "^3.1.6",
"@storybook/react": "^3.1.9"
}
}

10
lerna.json Normal file
View File

@@ -0,0 +1,10 @@
{
"lerna": "2.5.1",
"version": "0.3.1",
"npmClient": "yarn",
"useWorkspaces": true,
"packages": [
"packages/*",
"website"
]
}

View File

@@ -1,126 +1,71 @@
{
"name": "react-native-web",
"version": "0.1.15",
"description": "React Native for Web",
"main": "dist/index.js",
"files": [
"babel",
"dist",
"jest",
"src",
"!**/__tests__"
],
"private": true,
"name": "react-native-web-monorepo",
"scripts": {
"benchmark": "cd benchmarks && yarn && webpack && open index.html",
"build": "yarn clean-dist && yarn compile && webpack --config webpack.config.js --sort-assets-by --progress",
"clean-dist": "del ./dist && mkdir dist",
"compile": "babel src -d dist --ignore *-test.js",
"docs:build": "cd docs && yarn build",
"docs:start": "cd docs && yarn && yarn start",
"docs:release": "cd docs && yarn release",
"benchmark": "cd packages/benchmarks && yarn benchmark",
"clean": "del ./packages/*/dist",
"compile": "yarn clean && cd packages/react-native-web && babel src --out-dir dist --ignore \"**/__tests__\"",
"docs:start": "cd website && yarn start",
"docs:release": "cd website && yarn release",
"flow": "flow",
"fmt": "find babel benchmarks docs jest src -name '*.js' | grep -v -E '(node_modules|dist)' | xargs yarn fmt:cmd",
"fmt:cmd": "prettier --print-width=100 --single-quote --write",
"jest": "jest",
"jest:watch": "yarn test -- --watch",
"lint": "yarn lint:cmd -- babel benchmarks docs jest src",
"lint:cmd": "eslint --ignore-path .gitignore --fix",
"fmt": "find packages scripts types website -name '*.js' | grep -v -E '(node_modules|dist|vendor)' | xargs yarn fmt:cmd",
"fmt:cmd": "prettier --write",
"jest": "jest --config ./scripts/jest/config.js",
"lint": "yarn lint:check --fix",
"lint:check": "eslint packages scripts website",
"precommit": "lint-staged",
"release": "yarn lint && yarn test && yarn build && npm publish",
"test": "flow && jest"
},
"babel": {
"presets": [
"react-native"
],
"plugins": [
[
"transform-react-remove-prop-types",
{
"mode": "wrap"
}
]
]
},
"jest": {
"testEnvironment": "jsdom",
"timers": "fake",
"setupFiles": [
"raf/polyfill"
],
"setupTestFrameworkScriptFile": "<rootDir>/jest-setup-framework.js",
"snapshotSerializers": [
"enzyme-to-json/serializer"
]
},
"lint-staged": {
"**/*.js": [
"fmt:cmd",
"git update-index --again",
"lint:cmd"
]
},
"dependencies": {
"animated": "^0.2.0",
"array-find-index": "^1.0.2",
"babel-runtime": "^6.26.0",
"create-react-class": "^15.6.2",
"debounce": "1.0.2",
"deep-assign": "^2.0.0",
"fbjs": "^0.8.16",
"hyphenate-style-name": "^1.0.2",
"inline-style-prefixer": "^3.0.8",
"normalize-css-color": "^1.0.2",
"prop-types": "^15.6.0",
"react-timer-mixin": "^0.13.3"
"prerelease": "yarn test && yarn compile",
"release": "node ./scripts/release/publish.js",
"postrelease": "yarn docs:release",
"test": "yarn flow && yarn lint:check && yarn jest"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-eslint": "^7.2.3",
"babel-eslint": "^8.0.3",
"babel-loader": "^7.1.2",
"babel-plugin-tester": "^4.0.0",
"babel-plugin-transform-react-remove-prop-types": "^0.4.9",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-plugin-transform-react-remove-prop-types": "^0.4.10",
"babel-preset-env": "^1.6.1",
"babel-preset-flow": "^6.23.0",
"babel-preset-react": "^6.24.1",
"babel-preset-react-native": "^4.0.0",
"caniuse-api": "^2.0.0",
"del-cli": "^1.1.0",
"enzyme": "^3.1.0",
"enzyme-adapter-react-16": "^1.0.2",
"enzyme-to-json": "^3.1.4",
"eslint": "^4.6.1",
"eslint-config-prettier": "^2.6.0",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-react": "^7.4.0",
"file-loader": "^1.1.4",
"flow-bin": "^0.49.1",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.0",
"enzyme-to-json": "^3.2.2",
"eslint": "^4.12.1",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-promise": "^3.6.0",
"eslint-plugin-react": "^7.5.1",
"flow-bin": "^0.63.1",
"husky": "^0.14.3",
"jest": "^21.2.1",
"lint-staged": "^4.1.3",
"prettier": "^1.7.3",
"raf": "^3.3.2",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"react-test-renderer": "^16.0.0",
"url-loader": "^0.5.9",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^2.9.0"
"lerna": "^2.6.0",
"lint-staged": "^6.0.0",
"prettier": "^1.8.2",
"raf": "^3.4.0",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-test-renderer": "^16.2.0"
},
"peerDependencies": {
"react": "16.x.x",
"react-dom": "16.x.x"
"workspaces": [
"packages/*",
"website"
],
"lint-staged": {
"**/*.js": [
"fmt:cmd",
"git update-index --again",
"eslint"
]
},
"prettier": {
"printWidth": 100,
"singleQuote": true
},
"author": "Nicolas Gallagher",
"license": "BSD-3-Clause",
"repository": {
"type": "git",
"url": "git://github.com/necolas/react-native-web.git"
},
"tags": [
"react"
],
"keywords": [
"react",
"react-component",
"react-native",
"web"
]
"license": "BSD-3-Clause"
}

View File

@@ -0,0 +1,39 @@
# babel-plugin-react-native-web
A Babel plugin that will alias `react-native` to `react-native-web` and exclude
any modules not required by your app (keeping bundle size down).
## Installation
```
yarn add --dev babel-plugin-react-native-web
```
## Usage
**.babelrc**
```
{
"plugins": ["react-native-web"]
}
```
## Example
NOTE: `react-native-web` internal paths are _not stable_ and you must not rely
on them. Always use the Babel plugin to optimize your build. What follows is an
example of the rewrite performed by the plugin.
**Before**
```js
import { StyleSheet, View } from 'react-native';
```
**After**
```js
import StyleSheet from 'react-native-web/dist/exports/StyleSheet';
import View from 'react-native-web/dist/exports/View';
```

View File

@@ -0,0 +1 @@
module.exports = require('./src');

View File

@@ -0,0 +1,15 @@
{
"name": "babel-plugin-react-native-web",
"version": "0.3.1",
"description": "Babel plugin for React Native for Web",
"main": "index.js",
"devDependencies": {
"babel-plugin-tester": "^5.0.0"
},
"author": "Nicolas Gallagher",
"license": "MIT",
"repository": {
"type": "git",
"url": "git://github.com/necolas/react-native-web.git"
}
}

View File

@@ -0,0 +1,91 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`export from "react-native" 1`] = `
"
export { View } from 'react-native';
export { ColorPropType, StyleSheet, Text, createElement } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
export { default as View } from 'react-native-web/dist/exports/View';
export { default as ColorPropType } from 'react-native-web/dist/exports/ColorPropType';
export { default as StyleSheet } from 'react-native-web/dist/exports/StyleSheet';
export { default as Text } from 'react-native-web/dist/exports/Text';
export { default as createElement } from 'react-native-web/dist/exports/createElement';
"
`;
exports[`export from "react-native-web" 1`] = `
"
export { View } from 'react-native-web';
export { ColorPropType, StyleSheet, Text, createElement } from 'react-native-web';
↓ ↓ ↓ ↓ ↓ ↓
export { default as View } from 'react-native-web/dist/exports/View';
export { default as ColorPropType } from 'react-native-web/dist/exports/ColorPropType';
export { default as StyleSheet } from 'react-native-web/dist/exports/StyleSheet';
export { default as Text } from 'react-native-web/dist/exports/Text';
export { default as createElement } from 'react-native-web/dist/exports/createElement';
"
`;
exports[`import from "native-native" 1`] = `
"
import ReactNative from 'react-native';
import { View } from 'react-native';
import { Invalid, View as MyView, ViewPropTypes } from 'react-native';
import * as ReactNativeModules from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
import ReactNative from 'react-native-web/dist/index';
import View from 'react-native-web/dist/exports/View';
import Invalid from 'react-native-web/dist/exports/Invalid';
import MyView from 'react-native-web/dist/exports/View';
import ViewPropTypes from 'react-native-web/dist/exports/ViewPropTypes';
import * as ReactNativeModules from 'react-native-web/dist/index';
"
`;
exports[`import from "react-native-web" 1`] = `
"
import { createElement } from 'react-native-web';
import { ColorPropType, StyleSheet, View, TouchableOpacity, processColor } from 'react-native-web';
import * as ReactNativeModules from 'react-native-web';
↓ ↓ ↓ ↓ ↓ ↓
import createElement from 'react-native-web/dist/exports/createElement';
import ColorPropType from 'react-native-web/dist/exports/ColorPropType';
import StyleSheet from 'react-native-web/dist/exports/StyleSheet';
import View from 'react-native-web/dist/exports/View';
import TouchableOpacity from 'react-native-web/dist/exports/TouchableOpacity';
import processColor from 'react-native-web/dist/exports/processColor';
import * as ReactNativeModules from 'react-native-web/dist/index';
"
`;
exports[`require "react-native-web" 1`] = `
"
const ReactNative = require('react-native-web');
const { createElement } = require('react-native-web');
const { ColorPropType, StyleSheet, View, TouchableOpacity, processColor } = require('react-native-web');
↓ ↓ ↓ ↓ ↓ ↓
const ReactNative = require('react-native-web');
const createElement = require('react-native-web/dist/exports/createElement');
const ColorPropType = require('react-native-web/dist/exports/ColorPropType');
const StyleSheet = require('react-native-web/dist/exports/StyleSheet');
const View = require('react-native-web/dist/exports/View');
const TouchableOpacity = require('react-native-web/dist/exports/TouchableOpacity');
const processColor = require('react-native-web/dist/exports/processColor');
"
`;

View File

@@ -0,0 +1,45 @@
const plugin = require('..');
const pluginTester = require('babel-plugin-tester');
const tests = [
// import react-native
{
title: 'import from "native-native"',
code: `import ReactNative from 'react-native';
import { View } from 'react-native';
import { Invalid, View as MyView, ViewPropTypes } from 'react-native';
import * as ReactNativeModules from 'react-native';`,
snapshot: true
},
{
title: 'import from "react-native-web"',
code: `import { createElement } from 'react-native-web';
import { ColorPropType, StyleSheet, View, TouchableOpacity, processColor } from 'react-native-web';
import * as ReactNativeModules from 'react-native-web';`,
snapshot: true
},
{
title: 'export from "react-native"',
code: `export { View } from 'react-native';
export { ColorPropType, StyleSheet, Text, createElement } from 'react-native';`,
snapshot: true
},
{
title: 'export from "react-native-web"',
code: `export { View } from 'react-native-web';
export { ColorPropType, StyleSheet, Text, createElement } from 'react-native-web';`,
snapshot: true
},
{
title: 'require "react-native-web"',
code: `const ReactNative = require('react-native-web');
const { createElement } = require('react-native-web');
const { ColorPropType, StyleSheet, View, TouchableOpacity, processColor } = require('react-native-web');`,
snapshot: true
}
];
pluginTester({
plugin,
tests
});

View File

@@ -0,0 +1,106 @@
const getDistLocation = importName =>
importName ? `react-native-web/dist/exports/${importName}` : undefined;
const isReactNativeRequire = (t, node) => {
const { declarations } = node;
if (declarations.length > 1) {
return false;
}
const { id, init } = declarations[0];
return (
t.isObjectPattern(id) &&
t.isCallExpression(init) &&
t.isIdentifier(init.callee) &&
init.callee.name === 'require' &&
init.arguments.length === 1 &&
(init.arguments[0].value === 'react-native' || init.arguments[0].value === 'react-native-web')
);
};
const isReactNativeModule = ({ source, specifiers }) =>
source &&
(source.value === 'react-native' || source.value === 'react-native-web') &&
specifiers.length;
module.exports = function({ types: t }) {
return {
name: 'Rewrite react-native to react-native-web',
visitor: {
ImportDeclaration(path, state) {
const { specifiers } = path.node;
if (isReactNativeModule(path.node)) {
const imports = specifiers
.map(specifier => {
if (t.isImportSpecifier(specifier)) {
const importName = specifier.imported.name;
const distLocation = getDistLocation(importName);
if (distLocation) {
return t.importDeclaration(
[t.importDefaultSpecifier(t.identifier(specifier.local.name))],
t.stringLiteral(distLocation)
);
}
}
return t.importDeclaration(
[specifier],
t.stringLiteral('react-native-web/dist/index')
);
})
.filter(Boolean);
path.replaceWithMultiple(imports);
}
},
ExportNamedDeclaration(path, state) {
const { specifiers } = path.node;
if (isReactNativeModule(path.node)) {
const exports = specifiers
.map(specifier => {
if (t.isExportSpecifier(specifier)) {
const exportName = specifier.exported.name;
const localName = specifier.local.name;
const distLocation = getDistLocation(localName);
if (distLocation) {
return t.exportNamedDeclaration(
null,
[t.exportSpecifier(t.identifier('default'), t.identifier(exportName))],
t.stringLiteral(distLocation)
);
}
}
return t.exportNamedDeclaration(
null,
[specifier],
t.stringLiteral('react-native-web/dist/index')
);
})
.filter(Boolean);
path.replaceWithMultiple(exports);
}
},
VariableDeclaration(path, state) {
if (isReactNativeRequire(t, path.node)) {
const { id } = path.node.declarations[0];
const imports = id.properties
.map(identifier => {
const distLocation = getDistLocation(identifier.key.name);
if (distLocation) {
return t.variableDeclaration(path.node.kind, [
t.variableDeclarator(
t.identifier(identifier.value.name),
t.callExpression(t.identifier('require'), [t.stringLiteral(distLocation)])
)
]);
}
})
.filter(Boolean);
path.replaceWithMultiple(imports);
}
}
}
};
};

View File

@@ -0,0 +1,56 @@
# benchmarks
To run these benchmarks:
```
yarn benchmark
```
To run benchmarks for individual implementations append `?<name>,<name>` to the
URL, e.g., `?css-modules,react-native-web`.
## Notes
These benchmarks are crude approximations of extreme cases that libraries may
encounter. The deep and wide tree cases look at the performance of mounting and
rendering large trees of styled elements. The Triangle case looks at the
performance of repeated style updates to a large mounted tree. Some libraries
must inject new styles for each "dynamic style", whereas others may not.
Libraries without support for dynamic styles (i.e., they rely on user-authored
inline styles) do not include the `SierpinskiTriangle` benchmark.
The components used in the render benchmarks are simple enough to be
implemented by multiple UI or style libraries. The benchmark implementations
and the features of the style libraries are _only approximately equivalent in
functionality_.
## Results
Typical render timings*: mean ± two standard deviations.
| Implementation | Deep tree (ms) | Wide tree (ms) | Triangle (ms) |
| :--- | ---: | ---: | ---: |
| `react-native-web@0.2.2` | `89.67` `±28.51` | `167.46` `±27.03` | `65.40` `±19.50` |
| `css-modules` | `77.42` `±45.50` | `141.44` `±33.96` | - |
| `inline-styles` | `236.25` `±95.57` | `477.01` `±88.30` | `40.95` `±23.53` |
Other libraries
| Implementation | Deep tree (ms) | Wide tree (ms) | Triangle (ms) |
| :--- | ---: | ---: | ---: |
| `styletron@3.0.0-rc.5` | `83.53` `±33.55` | `153.12` `±39.13` | `56.47` `±24.22` |
| `aphrodite@1.2.5` | `88.23` `±31.22` | `164.03` `±34.70` | - |
| `glamor@2.20.40` | `110.09` `±34.20` | `182.06` `±50.39` | ‡ |
| `emotion@8.0.12` | `103.44` `±32.12` | `204.45` `±41.00` | `110.28` `±26.94` |
| `react-jss@8.2.0` | `136.17` `±59.23` | `270.51` `±69.20` | - |
| `styled-components@2.3.2` | `217.57` `±51.90` | `437.57` `±65.74` | `76.99` `±41.79` |
| `reactxp@0.46.6` | `240.88` `±79.82` | `467.32` `±74.42` | `70.95` `±32.90`|
| `radium@0.19.6` | `400.19` `±94.58` | `816.59` `±91.10` | `71.13` `±27.22` |
These results indicate that render times when using `react-native-web`,
`css-modules`, `aphrodite`, and `styletron` are roughly equivalent and
significantly faster than alternatives.
*MacBook Pro (13-inch, Early 2011); 2.3 GHz Intel Core i5; 8 GB 1333 MHz DDR3. Google Chrome 62.
‡Glamor essentially crashes the browser tab.

View File

@@ -0,0 +1,33 @@
{
"private": true,
"name": "benchmarks",
"version": "0.3.1",
"scripts": {
"benchmark": "webpack --config ./webpack.config.js && open index.html"
},
"dependencies": {
"aphrodite": "^1.2.5",
"babel-polyfill": "^6.26.0",
"classnames": "^2.2.5",
"d3-scale-chromatic": "^1.1.1",
"emotion": "^8.0.12",
"glamor": "^2.20.40",
"marky": "^1.2.0",
"radium": "^0.19.6",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-jss": "^8.2.0",
"react-native-web": "^0.3.1",
"reactxp": "^0.46.6",
"styled-components": "^2.3.2",
"styletron-client": "^3.0.0-rc.5",
"styletron-utils": "^3.0.0-rc.3"
},
"devDependencies": {
"babel-plugin-react-native-web": "^0.3.1",
"css-loader": "^0.28.7",
"style-loader": "^0.19.1",
"webpack": "^3.10.0",
"webpack-bundle-analyzer": "^2.9.1"
}
}

View File

@@ -38,30 +38,31 @@ const standardDeviation = values => {
return Math.sqrt(meanSquareDiff);
};
export const log = (name, description, durations) => {
const stdDev = standardDeviation(durations);
const formattedMean = fmt(mean(durations));
const formattedMedian = fmt(median(durations));
const formattedStdDev = fmt(stdDev);
console.groupCollapsed(`${name}\n${formattedMean} ±${fmt(2 * stdDev)}`);
description && console.log(description);
console.log(`Median: ${formattedMedian}`);
console.log(`Mean: ${formattedMean}`);
console.log(`Standard deviation: ${formattedStdDev}`);
console.log(durations);
console.groupEnd();
};
const benchmark = ({ name, description, setup, teardown, task, runs }) => {
return new Promise(resolve => {
const durations = [];
let i = 0;
setup();
const first = measure('first', task);
teardown();
const done = () => {
const stdDev = standardDeviation(durations);
const formattedFirst = fmt(first);
const formattedMean = fmt(mean(durations));
const formattedMedian = fmt(median(durations));
const formattedStdDev = fmt(stdDev);
console.groupCollapsed(`${name}\n${formattedMean} ±${fmt(2 * stdDev)}`);
description && console.log(description);
console.log(`First: ${formattedFirst}`);
console.log(`Median: ${formattedMedian}`);
console.log(`Mean: ${formattedMean}`);
console.log(`Standard deviation: ${formattedStdDev}`);
console.log(durations);
console.groupEnd();
log(name, description, durations);
resolve();
};

View File

@@ -0,0 +1,84 @@
import PropTypes from 'prop-types';
import React from 'react';
import { interpolatePurples, interpolateBuPu, interpolateRdPu } from 'd3-scale-chromatic';
const targetSize = 25;
class SierpinskiTriangle extends React.Component {
static propTypes = {
Dot: PropTypes.node,
depth: PropTypes.number,
renderCount: PropTypes.number,
s: PropTypes.number,
x: PropTypes.number,
y: PropTypes.number
};
static defaultProps = {
depth: 0,
renderCount: 0
};
render() {
const { x, y, depth, renderCount, Dot } = this.props;
let { s } = this.props;
if (s <= targetSize) {
let fn;
switch (depth) {
case 1:
fn = interpolatePurples;
break;
case 2:
fn = interpolateBuPu;
break;
case 3:
default:
fn = interpolateRdPu;
}
return (
<Dot
color={fn(renderCount / 20)}
size={targetSize}
x={x - targetSize / 2}
y={y - targetSize / 2}
/>
);
}
s /= 2;
return [
<SierpinskiTriangle
Dot={Dot}
depth={1}
key={1}
renderCount={renderCount}
s={s}
x={x}
y={y - s / 2}
/>,
<SierpinskiTriangle
Dot={Dot}
depth={2}
key={2}
renderCount={renderCount}
s={s}
x={x - s}
y={y + s / 2}
/>,
<SierpinskiTriangle
Dot={Dot}
depth={3}
key={3}
renderCount={renderCount}
s={s}
x={x + s}
y={y + s / 2}
/>
];
}
}
export default SierpinskiTriangle;

View File

@@ -1,10 +1,10 @@
import createRenderBenchmark from '../createRenderBenchmark';
import NestedTree from '../src/components/NestedTree';
import NestedTree from './NestedTree';
import React from 'react';
const renderDeepTree = (label, components) =>
createRenderBenchmark({
name: `Deep tree [${label}]`,
name: `[${label}] Deep tree`,
runs: 20,
getElement() {
return <NestedTree breadth={3} components={components} depth={6} id={0} wrap={1} />;

View File

@@ -0,0 +1,112 @@
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import SierpinskiTriangle from './SierpinskiTriangle';
import { log } from '../benchmark';
const node = document.querySelector('.root');
let runs = 20;
class Speedometer extends React.Component {
/* necessary for reactxp to work without errors */
static childContextTypes = {
focusManager: PropTypes.object
};
getChildContext() {
return {
focusManager: {
addFocusableComponent() {},
removeFocusableComponent() {},
restrictFocusWithin() {},
removeFocusRestriction() {},
limitFocusWithin() {},
removeFocusLimitation() {}
}
};
}
static propTypes = {
Dot: PropTypes.node.isRequired,
description: PropTypes.string,
name: PropTypes.string.isRequired,
onComplete: PropTypes.node.isRequired
};
state = { renderCount: -1 };
async componentDidMount() {
const durations = [];
while ((runs -= 1)) {
const prev = window.performance.now();
await new Promise(resolve => {
this.raf = window.requestAnimationFrame(() => {
this.setState({ renderCount: this.state.renderCount + 1 }, () => {
const now = window.performance.now();
durations.push(now - prev);
resolve();
});
});
});
}
const { description, name } = this.props;
log(name, description, durations);
runs = 20;
this.props.onComplete();
}
componentWillUnmount() {
window.cancelAnimationFrame(this.raf);
}
render() {
return (
<div style={styles.wrapper}>
<SierpinskiTriangle
Dot={this.props.Dot}
renderCount={this.state.renderCount}
s={1000}
x={0}
y={0}
/>
</div>
);
}
}
const styles = {
wrapper: {
position: 'absolute',
transformOrigin: '0 0',
left: '50%',
top: '50%',
width: '10px',
height: '10px',
backgroundColor: '#eee',
transform: 'scale(0.33)'
}
};
const renderSierpinskiTriangle = (name, { Dot }) => () => {
return new Promise(resolve => {
/* eslint-disable react/jsx-no-bind */
ReactDOM.render(
<Speedometer
Dot={Dot}
description="Dynamic styles"
name={`[${name}] Triangle`}
onComplete={() => {
ReactDOM.unmountComponentAtNode(node);
resolve();
}}
/>,
node
);
/* eslint-enable react/jsx-no-bind */
});
};
export default renderSierpinskiTriangle;

View File

@@ -1,5 +1,4 @@
import createRenderBenchmark from '../createRenderBenchmark';
import Tweet from '../src/components/Tweet';
import React from 'react';
const tweet1 = {
@@ -96,9 +95,9 @@ const tweet2 = {
}
};
const renderTweet = label =>
const renderTweet = (label, { Tweet }) =>
createRenderBenchmark({
name: `Tweet [${label}]`,
name: `[${label}] Tweet`,
runs: 10,
getElement() {
return (

View File

@@ -1,10 +1,10 @@
import createRenderBenchmark from '../createRenderBenchmark';
import NestedTree from '../src/components/NestedTree';
import NestedTree from './NestedTree';
import React from 'react';
const renderWideTree = (label, components) =>
createRenderBenchmark({
name: `Wide tree [${label}]`,
name: `[${label}] Wide tree`,
runs: 20,
getElement() {
return <NestedTree breadth={10} components={components} depth={3} id={0} wrap={4} />;

View File

@@ -1,6 +1,6 @@
/* eslint-disable react/prop-types */
import React from 'react';
import View from '../View/aphrodite';
import View from './View';
import { StyleSheet } from 'aphrodite';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (

View File

@@ -0,0 +1,7 @@
import Box from './Box';
import View from './View';
export default {
Box,
View
};

View File

@@ -1,8 +1,8 @@
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import React from 'react';
import View from '../View/css-modules';
import styles from './styles.css';
import View from './View';
import styles from './box-styles.css';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
<View

View File

@@ -1,7 +1,7 @@
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import React from 'react';
import styles from './styles.css';
import styles from './view-styles.css';
class View extends React.Component {
render() {

View File

@@ -0,0 +1,7 @@
import Box from './Box';
import View from './View';
export default {
Box,
View
};

View File

@@ -1,6 +1,6 @@
/* eslint-disable react/prop-types */
import React from 'react';
import View from '../View/glamor';
import View from './View';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
<View

View File

@@ -0,0 +1,32 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { css } from 'emotion';
const Dot = ({ size, x, y, children, color }) => (
<div
className={css(styles.root, {
borderBottomColor: color,
borderRightWidth: `${size / 2}px`,
borderBottomWidth: `${size / 2}px`,
borderLeftWidth: `${size / 2}px`,
left: `${x}px`,
top: `${y}px`
})}
>
{children}
</div>
);
const styles = {
root: {
position: 'absolute',
cursor: 'pointer',
width: 0,
height: 0,
borderColor: 'transparent',
borderStyle: 'solid',
borderTopWidth: 0
}
};
export default Dot;

View File

@@ -5,7 +5,7 @@ import React from 'react';
class View extends React.Component {
render() {
const { style, ...other } = this.props;
return <div {...other} className={css([viewStyle, ...style])} />;
return <div {...other} className={css(viewStyle, ...style)} />;
}
}

View File

@@ -0,0 +1,9 @@
import Box from './Box';
import Dot from './Dot';
import View from './View';
export default {
Box,
Dot,
View
};

View File

@@ -1,6 +1,6 @@
/* eslint-disable react/prop-types */
import React from 'react';
import View from '../View/emotion';
import View from './View';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
<View

View File

@@ -0,0 +1,32 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { css } from 'glamor';
const Dot = ({ size, x, y, children, color }) => (
<div
className={css(styles.root, {
borderBottomColor: color,
borderRightWidth: `${size / 2}px`,
borderBottomWidth: `${size / 2}px`,
borderLeftWidth: `${size / 2}px`,
left: `${x}px`,
top: `${y}px`
})}
>
{children}
</div>
);
const styles = {
root: {
position: 'absolute',
cursor: 'pointer',
width: 0,
height: 0,
borderColor: 'transparent',
borderStyle: 'solid',
borderTopWidth: 0
}
};
export default Dot;

View File

@@ -0,0 +1,9 @@
import Box from './Box';
import Dot from './Dot';
import View from './View';
export default {
Box,
Dot,
View
};

View File

@@ -0,0 +1,48 @@
/* eslint-disable react/prop-types */
import React from 'react';
import View from './View';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
<View
{...other}
style={{
...styles[`color${color}`],
...(fixed && styles.fixed),
...(layout === 'row' && styles.row),
...(outer && styles.outer)
}}
/>
);
const styles = {
outer: {
padding: 4
},
row: {
flexDirection: 'row'
},
color0: {
backgroundColor: '#222'
},
color1: {
backgroundColor: '#666'
},
color2: {
backgroundColor: '#999'
},
color3: {
backgroundColor: 'blue'
},
color4: {
backgroundColor: 'orange'
},
color5: {
backgroundColor: 'red'
},
fixed: {
width: 20,
height: 20
}
};
export default Box;

View File

@@ -0,0 +1,34 @@
/* eslint-disable react/prop-types */
import React from 'react';
const Dot = ({ size, x, y, children, color }) => (
<div
style={{
...styles.root,
...{
borderBottomColor: color,
borderRightWidth: `${size / 2}px`,
borderBottomWidth: `${size / 2}px`,
borderLeftWidth: `${size / 2}px`,
left: `${x}px`,
top: `${y}px`
}
}}
>
{children}
</div>
);
const styles = {
root: {
position: 'absolute',
cursor: 'pointer',
width: 0,
height: 0,
borderColor: 'transparent',
borderStyle: 'solid',
borderTopWidth: 0
}
};
export default Dot;

View File

@@ -0,0 +1,36 @@
/* eslint-disable react/prop-types */
import React from 'react';
const compose = (s1, s2) => {
if (s1 && s2) {
return { ...s1, ...s2 };
} else {
return s1 || s2;
}
};
class View extends React.Component {
render() {
const { style, ...other } = this.props;
return <div {...other} style={compose(viewStyle, style)} />;
}
}
const viewStyle = {
alignItems: 'stretch',
borderWidth: 0,
borderStyle: 'solid',
boxSizing: 'border-box',
display: 'flex',
flexBasis: 'auto',
flexDirection: 'column',
flexShrink: 0,
margin: 0,
padding: 0,
position: 'relative',
// fix flexbox bugs
minHeight: 0,
minWidth: 0
};
export default View;

View File

@@ -0,0 +1,9 @@
import Box from './Box';
import Dot from './Dot';
import View from './View';
export default {
Box,
Dot,
View
};

View File

@@ -2,7 +2,7 @@
import classnames from 'classnames';
import injectSheet from 'react-jss';
import React from 'react';
import View from '../View/jss';
import View from './View';
const Box = ({ classes, color, fixed = false, layout = 'column', outer = false, ...other }) => (
<View

View File

@@ -0,0 +1,7 @@
import Box from './Box';
import View from './View';
export default {
Box,
View
};

View File

@@ -1,7 +1,7 @@
/* eslint-disable react/prop-types */
import Radium from 'radium';
import React from 'react';
import View from '../View/radium';
import View from './View';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
<View

View File

@@ -0,0 +1,35 @@
/* eslint-disable react/prop-types */
import Radium from 'radium';
import React from 'react';
const Dot = ({ size, x, y, children, color }) => (
<div
style={[
styles.root,
{
borderBottomColor: color,
borderRightWidth: `${size / 2}px`,
borderBottomWidth: `${size / 2}px`,
borderLeftWidth: `${size / 2}px`,
left: `${x}px`,
top: `${y}px`
}
]}
>
{children}
</div>
);
const styles = {
root: {
position: 'absolute',
cursor: 'pointer',
width: 0,
height: 0,
borderColor: 'transparent',
borderStyle: 'solid',
borderTopWidth: 0
}
};
export default Radium(Dot);

View File

@@ -0,0 +1,9 @@
import Box from './Box';
import Dot from './Dot';
import View from './View';
export default {
Box,
Dot,
View
};

View File

@@ -0,0 +1,32 @@
/* eslint-disable react/prop-types */
import { createElement, StyleSheet } from 'react-native';
const Dot = ({ size, x, y, children, color }) =>
createElement('div', {
children,
style: [
styles.root,
{
borderBottomColor: color,
borderRightWidth: size / 2,
borderBottomWidth: size / 2,
borderLeftWidth: size / 2,
left: x,
top: y
}
]
});
const styles = StyleSheet.create({
root: {
position: 'absolute',
cursor: 'pointer',
width: 0,
height: 0,
borderColor: 'transparent',
borderStyle: 'solid',
borderTopWidth: 0
}
});
export default Dot;

View File

@@ -1,5 +1,5 @@
import PropTypes from 'prop-types';
import theme from '../theme';
import theme from './theme';
import React, { PureComponent } from 'react';
import { StyleSheet, Text } from 'react-native';

View File

@@ -1,6 +1,6 @@
import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';
import { StyleSheet, View } from 'react-native';
import { StyleSheet, View, ViewPropTypes } from 'react-native';
class AspectRatio extends PureComponent {
static displayName = 'AspectRatio';
@@ -8,7 +8,7 @@ class AspectRatio extends PureComponent {
static propTypes = {
children: PropTypes.any,
ratio: PropTypes.number,
style: PropTypes.object
style: ViewPropTypes.style
};
static defaultProps = {

View File

@@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import { StyleSheet, View } from 'react-native';
import { StyleSheet, View, ViewPropTypes } from 'react-native';
import React, { Component } from 'react';
import theme from '../theme';
import theme from './theme';
class GridView extends Component {
static displayName = 'GridView';
@@ -9,7 +9,7 @@ class GridView extends Component {
static propTypes = {
children: PropTypes.node,
hasGap: PropTypes.bool,
style: PropTypes.object
style: ViewPropTypes.style
};
render() {

View File

@@ -1,11 +1,11 @@
import IconReply from '../Icons/Reply';
import IconHeart from '../Icons/Heart';
import IconRetweet from '../Icons/Retweet';
import IconDirectMessage from '../Icons/DirectMessage';
import IconReply from './IconReply';
import IconHeart from './IconHeart';
import IconRetweet from './IconRetweet';
import IconDirectMessage from './IconDirectMessage';
import PropTypes from 'prop-types';
import React from 'react';
import theme from '../theme';
import { Text, View, StyleSheet } from 'react-native';
import theme from './theme';
import { Text, View, ViewPropTypes, StyleSheet } from 'react-native';
const getIcon = (icon, highlighted) => {
switch (icon) {
@@ -30,7 +30,7 @@ export default class TweetAction extends React.Component {
displayMode: PropTypes.oneOf(['like', 'reply', 'retweet', 'directMessage']),
highlighted: PropTypes.bool,
onPress: PropTypes.func,
style: PropTypes.object
style: ViewPropTypes.style
};
render() {

View File

@@ -1,6 +1,6 @@
import PropTypes from 'prop-types';
import TweetAction from '../TweetAction';
import { View, StyleSheet } from 'react-native';
import TweetAction from './TweetAction';
import { View, ViewPropTypes, StyleSheet } from 'react-native';
import React, { PureComponent } from 'react';
const actionNames = ['reply', 'retweet', 'like', 'directMessage'];
@@ -16,7 +16,7 @@ export default class TweetActionsBar extends PureComponent {
onPress: PropTypes.func
})
),
style: PropTypes.object
style: ViewPropTypes.style
};
render() {

View File

@@ -1,6 +1,6 @@
import AppText from '../AppText';
import AppText from './AppText';
import React from 'react';
import TweetTextPart from '../TweetTextPart';
import TweetTextPart from './TweetTextPart';
import { array, number, string } from 'prop-types';
class TweetText extends React.Component {

View File

@@ -2,7 +2,7 @@
import { Image, StyleSheet, Text } from 'react-native';
import PropTypes from 'prop-types';
import React from 'react';
import theme from '../theme';
import theme from './theme';
const createTextEntity = ({ part }) => <Text>{`${part.prefix}${part.text}`}</Text>;
@@ -106,7 +106,7 @@ const styles = StyleSheet.create({
width: '1.25em',
paddingRight: '0.05em',
paddingLeft: '0.1em',
verticalAlign: '-0.2em'
textAlignVertical: '-0.2em'
}
});

View File

@@ -1,8 +1,8 @@
import AspectRatio from '../AspectRatio';
import AspectRatio from './AspectRatio';
import PropTypes from 'prop-types';
import { Image, StyleSheet } from 'react-native';
import { Image, StyleSheet, ViewPropTypes } from 'react-native';
import React, { PureComponent } from 'react';
import theme from '../theme';
import theme from './theme';
class UserAvatar extends PureComponent {
static displayName = 'UserAvatar';
@@ -10,7 +10,7 @@ class UserAvatar extends PureComponent {
static propTypes = {
accessibilityLabel: PropTypes.string,
circle: PropTypes.bool,
style: PropTypes.object,
style: ViewPropTypes.style,
uri: PropTypes.string
};

View File

@@ -1,6 +1,6 @@
import AppText from '../AppText';
import AppText from './AppText';
import PropTypes from 'prop-types';
import { StyleSheet } from 'react-native';
import { StyleSheet, ViewPropTypes } from 'react-native';
import React, { PureComponent } from 'react';
class UserNames extends PureComponent {
@@ -11,7 +11,7 @@ class UserNames extends PureComponent {
layout: PropTypes.oneOf(['nowrap', 'stack']),
onPress: PropTypes.func,
screenName: PropTypes.string,
style: PropTypes.object
style: ViewPropTypes.style
};
static defaultProps = {

View File

@@ -1,13 +1,13 @@
import AspectRatio from '../AspectRatio';
import GridView from '../GridView';
import AspectRatio from './AspectRatio';
import GridView from './GridView';
import PropTypes from 'prop-types';
import TweetActionsBar from '../TweetActionsBar';
import TweetText from '../TweetText';
import UserAvatar from '../UserAvatar';
import UserNames from '../UserNames';
import TweetActionsBar from './TweetActionsBar';
import TweetText from './TweetText';
import UserAvatar from './UserAvatar';
import UserNames from './UserNames';
import { Image, StyleSheet, Text, View } from 'react-native';
import React, { Component } from 'react';
import theme from '../theme';
import theme from './theme';
export class Tweet extends Component {
static displayName = 'Tweet';

View File

@@ -8,7 +8,7 @@ const styles = StyleSheet.create({
maxWidth: '100%',
position: 'relative',
userSelect: 'none',
verticalAlign: 'text-bottom'
textAlignVertical: 'text-bottom'
}
});

View File

@@ -0,0 +1,9 @@
import Box from './Box';
import Dot from './Dot';
import { View } from 'react-native';
export default {
Box,
Dot,
View
};

View File

@@ -0,0 +1,34 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { Styles, View } from 'reactxp';
const Dot = ({ size, x, y, children, color }) => (
<View
children={children}
style={[
styles.root,
{
borderBottomColor: color,
borderRightWidth: `${size / 2}px`,
borderBottomWidth: `${size / 2}px`,
borderLeftWidth: `${size / 2}px`,
left: `${x}px`,
top: `${y}px`
}
]}
/>
);
const styles = {
root: Styles.createViewStyle({
position: 'absolute',
cursor: 'pointer',
width: 0,
height: 0,
borderColor: 'transparent',
borderStyle: 'solid',
borderTopWidth: 0
})
};
export default Dot;

View File

@@ -1,7 +1,9 @@
import Box from './components/Box/reactxp';
import Box from './Box';
import Dot from './Dot';
import { View } from 'reactxp';
export default {
Box,
Dot,
View
};

View File

@@ -1,5 +1,5 @@
import styled from 'styled-components';
import View from '../View/styled-components';
import View from './View';
const getColor = color => {
switch (color) {

Some files were not shown because too many files have changed in this diff Show More