Compare commits

..

1 Commits

Author SHA1 Message Date
Nicolas Gallagher
cc46c1d1a8 [change] Introduce static CSS base rules for core primitives
This patch addresses 2 related issues:

1) Browser layout times in Chrome increase when elements use a lot of CSS class
names. This begins to add up for larger trees.

2) React Native supports passing 'null' values for styles. This can remove base
styles defined using 'StyleSheet' in the implementation of components like View
and Text.

Both of these issues can be avoided, and some runtime logic and computation
removed, by moving the base styles to static CSS rules. Comparisons of the
"benchmark" UI tests (which only render View) indicate that total times in
Chrome are reduced by ~20% with almost all of those savings coming from a
~33% reduction in layout-related timings.

Ref #1136
2019-01-01 13:50:58 -08:00
899 changed files with 53284 additions and 40329 deletions

5
.babelrc Normal file
View File

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

View File

@@ -1,11 +1,4 @@
{
"settings": {
"react": {
"pragma": "React",
"version": "16.6",
"flowVersion": "0.109.0" // Flow version
}
},
// babel parser to support ES6/7 features
"parser": "babel-eslint",
"parserOptions": {
@@ -17,24 +10,31 @@
"sourceType": "module"
},
"extends": [
"plugin:flowtype/recommended",
"prettier",
"prettier/react"
],
"plugins": [
"flowtype",
"promise",
"react",
"react-hooks"
"react"
],
"env": {
"browser": true,
"es6": true,
"jest": true,
"node": true
},
"globals": {
"document": false,
"navigator": false,
"window": false,
// Flow global types,
"$Enum": false,
"HTMLInputElement": false,
"ReactClass": false,
"ReactComponent": false,
"ReactElement": false,
"ReactPropsChainableTypeChecker": false,
"ReactPropsCheckType": false,
"ReactPropTypes": false,
"SyntheticEvent": false
},
"rules": {
"camelcase": 0,
@@ -57,6 +57,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,
@@ -124,16 +125,12 @@
"valid-typeof": 2,
"yoda": [2, "never"],
// flow
"flowtype/generic-spacing": 0,
"flowtype/space-after-type-colon": 0,
// promise
"promise/param-names": 2,
// react
"react/display-name": 0,
"react/jsx-no-bind": 0,
"react/jsx-no-bind": 2,
"react/jsx-no-duplicate-props": 2,
"react/jsx-no-undef": 2,
"react/jsx-pascal-case": 2,
@@ -147,15 +144,11 @@
"react/no-string-refs": 2,
"react/no-unknown-property": 2,
"react/prefer-es6-class": 2,
"react/prop-types": 0,
"react/prop-types": 2,
"react/react-in-jsx-scope": 0,
"react/self-closing-comp": 2,
"react/sort-comp": 0,
"react/sort-prop-types": 2,
"react/wrap-multilines": 0,
// react-hooks
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
"react/wrap-multilines": 0
}
}

View File

@@ -1,10 +1,11 @@
[version]
^0.109.0
^0.63.0
[ignore]
<PROJECT_ROOT>/.*/__tests__/.*
<PROJECT_ROOT>/packages/.*/dist/.*
<PROJECT_ROOT>/packages/docs/.*
<PROJECT_ROOT>/packages/examples/.*
<PROJECT_ROOT>/packages/website/.*
.*/node_modules/babel-plugin-transform-react-remove-prop-types/*
[include]
@@ -14,7 +15,3 @@
[options]
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState

View File

@@ -70,16 +70,15 @@ yarn compile
yarn compile --watch
```
## Documentation and visual tests
## Website and visual tests
To run the interactive storybook:
```
yarn docs
yarn website
```
When you're also making changes to the 'react-native-web' source files, run this
command in another process:
When you're also making changes to the 'react-native-web' source files, run this command in another process:
```
yarn compile --watch

View File

@@ -1,13 +0,0 @@
name: compressed-size
on: [pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: necolas/compressed-size-action@master
with:
build-script: "compile"
exclude: "./packages/react-native-web/dist/cjs/{index.js,**/*.js}"
pattern: "./packages/react-native-web/dist/{index.js,**/*.js}"
repo-token: "${{ secrets.GITHUB_TOKEN }}"

View File

@@ -1,7 +1,7 @@
language: node_js
node_js:
- "10"
- "8"
before_install:
# Install Yarn

View File

@@ -1 +0,0 @@
{}

102
README.md
View File

@@ -2,10 +2,12 @@
[![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)
**Compatibility: React Native >= 0.63**.
**Compatibility: React Native 0.55**.
"React Native for Web" makes it possible to run [React
Native][react-native-url] components and APIs on the web using React DOM.
Native][react-native-url] components and APIs on the web using React DOM. Check
out the live demo of the [React Native examples][examples-url] running on the
web.
* **High-quality web interfaces**: makes it easy to
create [fast](https://github.com/necolas/react-native-web/blob/master/packages/benchmarks/README.md),
@@ -20,59 +22,73 @@ 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.
Who is using React Native for Web in production?
Who is using React Native in production web apps?
[Twitter](https://mobile.twitter.com),
[Expo](https://docs.expo.io/workflow/web/),
[Major League Soccer](https://matchcenter.mlssoccer.com),
[Flipkart](https://twitter.com/naqvitalha/status/969577892991549440),
[Uber](https://www.youtube.com/watch?v=RV9rxrNIxnY),
[The Times](https://github.com/newsuk/times-components),
[DataCamp](https://www.datacamp.com/community/tech/porting-practice-to-web-part1).
[The Times](https://github.com/newsuk/times-components), [DataCamp](https://www.datacamp.com/community/tech/porting-practice-to-web-part1).
Browser support: Chrome, Firefox, Edge, Safari 7+, IE 10+.
Components and APIs deprecated in React Native are not supported by React Native for Web.
## Quick start
The easiest way to get started is to edit this
[CodeSandbox](https://codesandbox.io/s/q4qymyp2l6) template. You dont need to
install anything to try it out.
[CodeSandbox](https://codesandbox.io/s/q4qymyp2l6) template (or
[Glitch](https://glitch.com/edit/#!/react-native)). You dont need to install
anything to try it out.
For installation and configuration details please read the [getting
started](https://github.com/necolas/react-native-web/blob/master/docs/guides/getting-started.md)
guide.
## Documentation
The [documentation app](https://necolas.github.com/react-native-web/docs) covers
installation, configuration, APIs, and guides.
Please refer to the [React Native documentation][react-native-url] for the
overall API, design details, and 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).
The [React Native documentation][react-native-url] contains more information
about the [Gesture Responder
system](https://facebook.github.io/react-native/docs/gesture-responder-system.html),
[animations](https://facebook.github.io/react-native/docs/animations.html), and
other design details.
Some components and APIs are extended with additional features for the web. And
in a few cases, features present for Android or iOS are missing on the web.
These differences are documented [on the website][website-url].
## Libraries and integrations
### Guides
List of React Native packages with known web compatibility:
These guides provide a detailed look at using React Native to create accessible
web experiences. Certain web-specific patterns are documented in the "web
recipes" guide.
* [React Native Directory](https://reactnative.directory/?web=true)
* [Getting started](https://github.com/necolas/react-native-web/blob/master/docs/guides/getting-started.md)
* [Client-side rendering](https://github.com/necolas/react-native-web/blob/master/docs/guides/client-side-rendering.md)
* [Server-side rendering](https://github.com/necolas/react-native-web/blob/master/docs/guides/server-side-rendering.md)
* [Style](https://github.com/necolas/react-native-web/blob/master/docs/guides/style.md)
* [Accessibility](https://github.com/necolas/react-native-web/blob/master/docs/guides/accessibility.md)
* [Internationalization](https://github.com/necolas/react-native-web/blob/master/docs/guides/internationalization.md)
* [Direct manipulation](https://github.com/necolas/react-native-web/blob/master/docs/guides/direct-manipulation.md)
* [Web recipes](https://github.com/necolas/react-native-web/blob/master/docs/guides/web-recipes.md)
* [Multi-platform apps](https://github.com/necolas/react-native-web/blob/master/docs/guides/multi-platform-apps.md)
* [Experimental / unstable use](https://github.com/necolas/react-native-web/blob/master/docs/guides/advanced.md)
## Integrations
Examples of using React Native for Web with other web tools:
* [Docz](https://github.com/doczjs/docz/tree/master/examples/react-native)
* [Docz](https://github.com/pedronauck/docz-plugin-react-native)
* [Gatsby](https://github.com/slorber/gatsby-plugin-react-native-web)
* [Next.js](https://github.com/zeit/next.js/tree/master/examples/with-react-native-web)
(and [example recipes](https://gist.github.com/necolas/f9034091723f1b279be86c7429eb0c96))
* [Next.js](https://github.com/zeit/next.js/tree/master/examples/with-react-native-web) (and [example recipes](https://gist.github.com/necolas/f9034091723f1b279be86c7429eb0c96))
* [Phenomic](https://github.com/phenomic/phenomic/tree/master/examples/react-native-web-app)
* [Razzle](https://github.com/jaredpalmer/razzle/tree/master/examples/with-react-native-web)
* [Storybook](https://github.com/necolas/react-native-web/tree/master/packages/docs/)
* [Storybook](https://github.com/necolas/react-native-web/tree/master/packages/website/storybook/.storybook)
* [Styleguidist](https://github.com/styleguidist/react-styleguidist/tree/master/examples/react-native)
## Examples
And here is a simple example to get you started. The documentation includes
interactive examples and the [source
code](https://github.com/necolas/react-native-web/blob/master/packages/docs) is
also available.
Check out all the [React Native examples][examples-url] ([source
code](https://github.com/necolas/react-native-web/blob/master/packages/examples)).
There are more examples [on the website][website-url] ([source
code](https://github.com/necolas/react-native-web/blob/master/packages/website)).
And here is a simple example to get you started:
```js
import React from 'react';
@@ -105,30 +121,34 @@ Native. This allows the app to be rendered to web and native platforms.
## Compatibility with React Native
React Native v0.60
React Native v0.55
### Components
| Name | Status | Notes |
| :----------------------- | :----- | :---- |
| ActivityIndicator | ✓ | |
| ART | ✓ | |
| Button | ✓ | |
| CheckBox | ✓ | |
| FlatList | ✓ | |
| Image | ✓ | Missing multiple sources ([#515](https://github.com/necolas/react-native-web/issues/515)) and HTTP headers ([#1019](https://github.com/necolas/react-native-web/issues/1019)). |
| ImageBackground | ✓ | |
| KeyboardAvoidingView | (✓) | Mock. No equivalent web APIs. |
| Modal | ✓ | |
| ListView | ✓ | |
| Modal | ✘ | Not started ([#1020](https://github.com/necolas/react-native-web/issues/1020)). |
| Picker | ✓ | |
| Pressable | ✓ | |
| RefreshControl | ✘ | Not started ([#1027](https://github.com/necolas/react-native-web/issues/1027)). |
| SafeAreaView | ✓ | |
| ScrollView | ✓ | Missing momentum scroll events ([#1021](https://github.com/necolas/react-native-web/issues/1021)). |
| SectionList | ✓ | |
| Slider | ✘ | Not started ([#1022](https://github.com/necolas/react-native-web/issues/1022)). |
| StatusBar | (✓) | Mock. No equivalent web APIs. |
| SwipeableFlatList | ✓ | |
| SwipeableListView | ✓ | |
| Switch | ✓ | |
| Text | ✓ | Missing `onLongPress` ([#1011](https://github.com/necolas/react-native-web/issues/1011)) support. |
| TextInput | ✓ | Missing rich text features ([#1023](https://github.com/necolas/react-native-web/issues/1023)), and auto-expanding behaviour ([#795](https://github.com/necolas/react-native-web/issues/795)). |
| Text | ✓ | Missing `onLongPress` ([#1011](https://github.com/necolas/react-native-web/issues/1011)) and `numberOfLines` ([#13](https://github.com/necolas/react-native-web/issues/13)) support. |
| TextInput | ✓ | Missing `onContentSizeChange` ([#793](https://github.com/necolas/react-native-web/issues/793)), rich text features ([#1023](https://github.com/necolas/react-native-web/issues/1023)), and auto-expanding behaviour ([#795](https://github.com/necolas/react-native-web/issues/795)). |
| Touchable | ✓ | Includes additional support for mouse and keyboard interactions. |
| TouchableHighlight | ✓ | |
| TouchableNativeFeedback | ✘ | Not started ([#1024](https://github.com/necolas/react-native-web/issues/1024)). |
@@ -136,6 +156,7 @@ React Native v0.60
| TouchableWithoutFeedback | ✓ | |
| View | ✓ | |
| VirtualizedList | ✓ | |
| WebView | ✘ | Not started ([1025](https://github.com/necolas/react-native-web/issues/1025)). |
| YellowBox | (✓) | Mock. No YellowBox functionality. |
### Modules
@@ -145,16 +166,21 @@ React Native v0.60
| AccessibilityInfo | (✓) | Mock. No equivalent web APIs. |
| Alert | ✘ | Not started ([#1026](https://github.com/necolas/react-native-web/issues/1026)). |
| Animated | ✓ | Missing `useNativeDriver` support. |
| Appearance | ✓ | |
| AppRegistry | ✓ | Includes additional support for server rendering with `getApplication`. |
| AppState | ✓ | |
| AsyncStorage | ✓ | |
| BackHandler | (✓) | Mock. No equivalent web APIs. |
| CameraRoll | ✘ | No equivalent web APIs. |
| Clipboard | ✓ | |
| ColorPropType | ✓ | |
| DeviceInfo | (✓) | Limited information. |
| Dimensions | ✓ | |
| Easing | ✓ | |
| EdgeInsetsPropType | ✓ | |
| Geolocation | ✓ | |
| I18nManager | ✓ | Includes additional support for runtime switch to RTL. |
| ImageEditor | ✘ | No equivalent web APIs. |
| ImageStore | ✘ | No equivalent web APIs. |
| InteractionManager | (✓) | |
| Keyboard | (✓) | Mock. |
| LayoutAnimation | (✓) | Missing translation to web animations. |
@@ -162,16 +188,18 @@ React Native v0.60
| NativeEventEmitter | ✓ | |
| NativeMethodsMixin | ✓ | |
| NativeModules | (✓) | Mocked. Missing ability to load native modules. |
| NetInfo | ✓ | Missing functionality to detect expensive connections as there are no equivalent web APIs. |
| PanResponder | ✓ | |
| PixelRatio | ✓ | |
| Platform | ✓ | |
| PointPropType | ✓ | |
| Settings | ✘ | No equivalent web APIs. |
| Share | ✓ | Only available over HTTPS. Read about the [Web Share API](https://wicg.github.io/web-share/). |
| StyleSheet | ✓ | |
| TextPropTypes | ✓ | |
| UIManager | ✓ | |
| Vibration | ✓ | |
| useColorScheme | ✓ | |
| useWindowDimensions | ✓ | |
| ViewPropTypes | ✓ | |
## Contributing
@@ -210,6 +238,8 @@ MIT license.
[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
[examples-url]: https://necolas.github.io/react-native-web/examples/
[website-url]: https://necolas.github.io/react-native-web/storybook/
[react-native-url]: https://facebook.github.io/react-native/
[contributing-url]: https://github.com/necolas/react-native-web/blob/master/.github/CONTRIBUTING.md
[good-first-issue-url]: https://github.com/necolas/react-native-web/labels/good%20first%20issue

View File

@@ -1,7 +0,0 @@
module.exports = function(api) {
api.cache(true);
return {
presets: ['./scripts/babel/preset']
};
};

View File

@@ -1,7 +1,3 @@
import { Meta } from '@storybook/addon-docs/blocks';
<Meta title="Guides|Accessibility" />
# Accessibility
On the Web, assistive technologies (e.g., VoiceOver, TalkBack screen readers)
@@ -15,43 +11,38 @@ props: `accessible`, `accessibilityLabel`, `accessibilityLiveRegion`,
## Accessibility properties
### accessible
When `true`, indicates that the view is an accessibility element. When a view
is an accessibility element, it groups its children into a single focusable
component. By default, all touchable elements, buttons, and links are
"accessible". Prefer using `accessibilityRole` (e.g., `button`, `link`) to
create focusable HTML elements wherever possible. On web, `accessible={true}`
is implemented using `tabIndex`.
### accessibilityLabel
Overrides the text that's read by a screen reader when a person interacts with
the element. (This is implemented using `aria-label`.)
When a view is marked as `accessible`, it is a good practice to set an
`accessibilityLabel` on the view, so that people who use screen readers know
what element they have selected. On web, `accessibilityLabel` is implemented
using `aria-label`.
```js
<View accessibilityLabel="Timeline: trending topics">
<View>...</View>
</View>
```
<TouchableOpacity accessibilityLabel={'Tap me!'} accessible={true} onPress={this._onPress}>
<View style={styles.button}>
<Text style={styles.buttonText}>Press me!</Text>
</View>
</TouchableOpacity>
```
### accessibilityLiveRegion: 'assertive' | 'none' | 'polite'
### accessibilityRole
Allows assistive technologies to announce dynamic changes to a view. The values
of this attribute are expressed in degrees of importance. When regions are
specified as `polite` (recommended), updates take low priority. When regions are
specified as `assertive`, assistive technologies will interrupt and immediately
announce the change. (This is implemented using `aria-live`.)
* `none`: Accessibility services should not announce changes to this view.
* `polite`: Accessibility services should announce changes to this view.
* `assertive`: Accessibility services should interrupt ongoing speech to immediately announce changes to this view.
```js
<Text accessibilityLiveRegion="polite">
Clicked {count} times
</Text>
```
### accessibilityRole: ?string
Indicates to assistive technologies how to describe the view, e.g., that it is a "button".
Allows assistive technologies to present and support interaction with the view
in a manner that is consistent with user expectations for similar views of that
type. For example, marking a touchable view with an accessibilityRole of button.
(This is implemented using ARIA roles.)
In some cases, we also want to alert the end user of the type of selected
component (i.e., that it is a “button”). To provide more context to screen
readers on the web, you should use the `accessibilityRole` property. (Note that
React Native for Web also provides a compatibility mapping of equivalent
`accessibilityTraits` and `accessibilityComponentType` values to
`accessibilityRole`).
The `accessibilityRole` prop is used to infer an [analogous HTML
element][html-aria-url] and ARIA `role`, where possible. In most cases, both
@@ -74,36 +65,53 @@ The `heading` role can be combined with `aria-level`:
* `<Text accessibilityRole="heading" aria-level="3" />` => `<h3 />`.
The `button` role renders an accessible button but is not implemented using the
native `button` element due to some browsers limiting the use of flexbox layout
on its children.
native `button` element due to some browsers limiting the use of flexbox layout on
its children.
* `<View accessibilityRole="button" />` => `<div role="button" tabIndex="0" />`.
In the example below, the `TouchableHighlight` is announced by screen readers
as a button and it responds to touch, mouse, and keyboard interactions.
```js
<TouchableHighlight accessibilityRole="button" onPress={this._handlePress}>
<View style={styles.button}>
<Text style={styles.buttonText}>Press me!</Text>
</View>
</TouchableHighlight>
```
Note: Avoid changing `accessibilityRole` values over time or after user
actions. Generally, accessibility APIs do not provide a means of notifying
assistive technologies of a `role` value change.
### accessibilityState: ?object
### accessibilityLiveRegion
...
When components dynamically change we may need to inform the user. The
`accessibilityLiveRegion` property serves this purpose and can be set to
`none`, `polite` and `assertive`. On web, `accessibilityLiveRegion` is
implemented using `aria-live`.
### accessibilityValue: ?object
* `none`: Accessibility services should not announce changes to this view.
* `polite`: Accessibility services should announce changes to this view.
* `assertive`: Accessibility services should interrupt ongoing speech to immediately announce changes to this view.
...
```
<TouchableWithoutFeedback onPress={this._addOne}>
<View style={styles.embedded}>
<Text>Click me</Text>
</View>
</TouchableWithoutFeedback>
### accessible
<Text accessibilityLiveRegion="polite">
Clicked {this.state.count} times
</Text>
```
When `true`, indicates that the view is an accessibility element. When a view
is an accessibility element, it groups its children into a single focusable
component. By default, all touchable elements, buttons, and links are
"accessible". Prefer using `accessibilityRole` (e.g., `button`, `link`) to
create focusable HTML elements wherever possible. On web, `accessible={true}`
is implemented using `tabIndex`.
When `true`, indicates that the view is an accessibility element (i.e.,
focusable) and groups its child content. By default, all the touchable elements
and elements with `accessibilityRole` of `button` and `link` are accessible.
(This is implemented using `tabindex`.)
In the above example, method `_addOne` changes the `state.count` variable. As
soon as an end user clicks the `TouchableWithoutFeedback`, screen readers
announce text in the `Text` view because of its
`accessibilityLiveRegion="polite"` property.
### importantForAccessibility
@@ -111,14 +119,7 @@ The `importantForAccessibility` property controls if a view appears in the
accessibility tree and if it is reported to accessibility services. On web, a
value of `no` will remove a focusable element from the tab flow, and a value of
`no-hide-descendants` will also hide the entire subtree from assistive
technologies. (This is implemented using `aria-hidden`).
### nativeID
Used to locate this view from any native DOM code, or to define accessibility
relationships. This is rendered to the native `id` DOM attribute.
## Additional topics
technologies (this is implemented using `aria-hidden`).
### Spatial navigation
@@ -132,7 +133,7 @@ that React Native considers focusable can be matched by the attribute
const focusableElements = document.querySelectorAll('[data-focusable="true"]');
```
### Other ARIA properties
### Other
Other ARIA properties can be set via [direct
manipulation](./direct-manipulation.md) or props (this may change in the

View File

@@ -1,12 +1,8 @@
import { Meta } from '@storybook/addon-docs/blocks';
<Meta title="Guides|Unstable uses" />
# Unstable APIs
# Experimental / unstable uses
## Use with existing React DOM components
React Native for Web exports a web-specific module called `unstable_createElement`,
React Native for Web exports a web-specific module called `createElement`,
which can be used to wrap React DOM components. This allows you to use React
Native's accessibility and style optimizations.
@@ -15,8 +11,8 @@ In the example below, `Video` will now accept common React Native props such as
props.
```js
import { unstable_createElement } from 'react-native-web';
const Video = (props) => unstable_createElement('video', props);
import { createElement } from 'react-native-web';
const Video = (props) => createElement('video', props);
```
This also works with composite components defined in your existing component
@@ -24,10 +20,10 @@ gallery or dependencies ([live example](https://www.webpackbin.com/bins/-KiTSGFw
```js
import RaisedButton from 'material-ui/RaisedButton';
import { unstable_createElement } from 'react-native-web';
import { createElement } from 'react-native-web';
import { StyleSheet } from 'react-native';
const CustomButton = (props) => unstable_createElement(RaisedButton, {
const CustomButton = (props) => createElement(RaisedButton, {
...props,
style: [ styles.button, props.style ]
});
@@ -39,11 +35,11 @@ const styles = StyleSheet.create({
});
```
And `unstable_createElement` can be used as drop-in replacement for `React.createElement`:
And `createElement` can be used as drop-in replacement for `React.createElement`:
```js
/* @jsx unstable_createElement */
import { unstable_createElement } from 'react-native-web';
/* @jsx createElement */
import { createElement } from 'react-native-web';
const Video = (props) => <video {...props} style={[ { marginVertical: 10 }, props.style ]} />
```
@@ -59,7 +55,7 @@ an API inspired by styled-components ([live
example](https://www.webpackbin.com/bins/-KjT9ziwv4O7FDZdvsnX)).
```js
import { unstable_createElement } from 'react-native-web';
import { createElement } from 'react-native-web';
import { StyleSheet } from 'react-native';
/**
@@ -82,7 +78,7 @@ const styled = (Component, styler) => {
return (
isDOMComponent
? unstable_createElement(Component, nextProps)
? createElement(Component, nextProps)
: <Component {...nextProps} />
);
}
@@ -101,3 +97,17 @@ const styles = StyleSheet.create({
const StyledView = styled(View, styles.container);
```
## Use with react-sketchapp
Use with [react-sketchapp](http://airbnb.io/react-sketchapp/) requires that you
alias `react-native` to `react-sketchapp`. This will allow you to render simple
React Native components in Sketch. Sketch-specific components like `Artboard`
should be imported from `react-sketchapp`.
If you're using `skpm`, you can rely on an [undocumented
feature](https://github.com/sketch-pm/skpm/blob/master/lib/utils/webpackConfig.js)
which will merge your `webpack.config.js`, `.babelrc`, or `package.json` Babel
config into its internal webpack config. The simplest option may be to use the
[babel-plugin-module-alias](https://www.npmjs.com/package/babel-plugin-module-alias)
and configure it in your `package.json`.

View File

@@ -1,7 +1,3 @@
import { Meta } from '@storybook/addon-docs/blocks';
<Meta title="Guides|Client-side" />
# Client-side rendering
Render apps using `AppRegistry`:
@@ -32,6 +28,10 @@ import { render } from 'react-native';
render(<AppHeader />, document.getElementById('react-app-header'))
```
(Components will also be rendered within a tree produced by calling
`ReactDOM.render` (i.e., an existing web app), but
otherwise it is not recommended.)
You might need to adjust the styles of the HTML document's root elements for
your app to fill the viewport.
@@ -40,6 +40,3 @@ your app to fill the viewport.
<body style="height:100%">
<div id="react-root" style="display:flex;height:100%"></div>
```
NOTE: Components will also be rendered within a tree produced by calling
`ReactDOM.render` (i.e., an existing web app), but it is not recommended.

View File

@@ -1,7 +1,3 @@
import { Meta } from '@storybook/addon-docs/blocks';
<Meta title="Guides|Direct manipulation" />
# Direct manipulation
React Native provides several methods to directly access the underlying host

View File

@@ -1,57 +1,37 @@
import { Meta } from '@storybook/addon-docs/blocks';
<Meta title="Overview|Getting started" />
# Getting started
This guide will help you render components and applications with React Native
for Web.
If you're not familiar with setting up a new React web project, please refer to
the [React documentation](https://reactjs.org/).
## Install
```
npm install react react-dom react-native-web
```
Your application may need to polyfill `Promise`, `Object.assign`, `Array.from`,
and [`ResizeObserver`](https://github.com/que-etc/resize-observer-polyfill) as
necessary for your desired browser support.
## Recommended starter kits
If you're not familiar with setting up a new React web project, please follow
the recommendations in the [React documentation](https://reactjs.org/).
### Expo
[Expo](https://expo.io) is a framework and a platform for universal React
applications. [Expo for Web](https://docs.expo.io/workflow/web/) uses React
Native for Web and provides dozens of additional cross-platform APIs.
## Install
```
npm install expo-cli --global
expo init my-app
cd my-app
expo start
yarn add react react-dom react-native-web
```
### Create React App
[Create React App](https://github.com/facebook/create-react-app) is a basic way
to setup a simple, web-only React app with built-in support for aliasing
`react-native-web` to `react-native`. However, it's generally recommended that
you use Expo.
And if you need to use `ART`:
```
npx create-react-app my-app
cd my-app
npm install react-native-web
npm start
yarn add react-art
```
## Standalone configurations
## Starter kits
### Configuring a module bundler
[create-react-app](https://github.com/facebookincubator/create-react-app)
includes built-in support for aliasing `react-native-web` to `react-native`.
```
create-react-app my-app
```
## Configuring a module bundler
If you have a custom setup, you may choose to configure your module bundler to
alias the package to `react-native`.
@@ -72,9 +52,11 @@ module.exports = {
}
```
### Configuring Babel
Now you can create your components and applications with the React Native API.
[Babel](https://babeljs.io/) supports module aliasing using
## Configuring Babel
If you need to do the aliasing with Babel you can use
[babel-plugin-module-resolver](https://www.npmjs.com/package/babel-plugin-module-resolver)
```
@@ -89,7 +71,7 @@ module.exports = {
}
```
### Configuring Jest
## Configuring Jest
[Jest](https://facebook.github.io/jest/) can be configured using the provided
preset. This will map `react-native` to `react-native-web` and provide
@@ -103,7 +85,7 @@ appropriate mocks:
Please refer to the Jest documentation for more information.
### Configuring Flow
## Configuring Flow
[Flow](https://flow.org) can be configured to understand the aliased module:
@@ -116,22 +98,6 @@ You may also need to include a custom libdef
([example](https://gist.github.com/paularmstrong/f60b40d16fc83e1e8e532d483336f9bb))
in your config.
### Configuring Node.js
Node.js can alias `react-native` to `react-native-web` using
[`module-alias`](https://www.npmjs.com/package/module-alias). This is useful if
you want to pre-render the app (e.g., server-side rendering or build-time
rendering).
```js
// Install the `module-alias` package as a dependency first
const moduleAlias = require("module-alias");
moduleAlias.addAliases({
"react-native": require.resolve("react-native-web"),
});
moduleAlias();
```
## Other notes
### Safari flexbox performance

View File

@@ -1,7 +1,3 @@
import { Meta } from '@storybook/addon-docs/blocks';
<Meta title="Guides|Internationalization" />
# Internationalization
To support right-to-left languages, application layout can be automatically

View File

@@ -1,14 +1,5 @@
import { Meta } from '@storybook/addon-docs/blocks';
<Meta title="Guides|Multi-platform" />
# Multi-platform applications
If you are interested in making a multi-platform app it is strongly recommended
that you use Expo. Recent Expo releases include [web
support](https://docs.expo.io/versions/v35.0.0/guides/running-in-the-browser/)
and take care of all the configuration work required.
## Web-specific code
Minor platform differences can use the `Platform` module.
@@ -45,9 +36,8 @@ target platform.
What follows is merely an _example_ of one basic way to package a web app using
[Webpack](https://webpack.js.org) and [Babel](https://babeljs.io/). (You can
also use the React Native bundler, [Metro](https://github.com/facebook/metro),
to build web apps although it is not officially supported: see
[#1257](https://github.com/necolas/react-native-web/issues/1257#issuecomment-541443684).)
also use the React Native bundler, [Metro](https://github.com/facebook/metro), to
build web apps.)
Packaging web apps is subtly different to packaging React Native apps and is
complicated by the need to tree-shake and code-split non-trivial apps.
@@ -94,8 +84,8 @@ const babelLoaderConfiguration = {
loader: 'babel-loader',
options: {
cacheDirectory: true,
// The 'metro-react-native-babel-preset' preset is recommended to match React Native's packager
presets: ['module:metro-react-native-babel-preset'],
// The 'react-native' preset is recommended to match React Native's packager
presets: ['react-native'],
// Re-write paths to import only the modules needed by the app
plugins: ['react-native-web']
}

View File

@@ -1,7 +1,3 @@
import { Meta } from '@storybook/addon-docs/blocks';
<Meta title="Guides|Server-side" />
# Server-side rendering
Server-side rendering to HTML is supported using `AppRegistry`:

View File

@@ -1,7 +1,3 @@
import { Meta } from '@storybook/addon-docs/blocks';
<Meta title="Guides|Style" />
# Style
React Native relies on JavaScript to define and resolve the styles of your
@@ -62,7 +58,7 @@ registered object, a plain object, or an array.
<View style={{ transform: [ { translateX } ] }} />
// array of registered or plain objects
<View style={[ styles.container, props.style ]} />
<View style={[ styles.container, this.props.style ]} />
```
The array syntax will merge styles from left-to-right as normal JavaScript
@@ -71,7 +67,7 @@ objects, and can be used to conditionally apply styles:
```js
<View style={[
styles.container,
state.active && styles.active
this.state.active && styles.active
]} />
```
@@ -87,30 +83,28 @@ To let other components customize the style of a component's children you can
expose a prop so styles can be explicitly passed into the component.
```js
import { View } from 'react-native';
class List extends React.Component {
static propTypes = {
style: ViewPropTypes.style,
elementStyle: ViewPropTypes.style,
}
type ViewProps = $PropertyType<View, 'props'>;
type ViewStyle = $PropertyType<ViewProps, 'style'>;
type Props = {
elementStyle: ViewStyle,
style: ViewStyle,
};
export default function List(props: Props) {
return (
<View style={props.style}>
{elements.map((element) =>
<View style={[ styles.element, props.elementStyle ]} />
)}
</View>
);
render() {
return (
<View style={this.props.style}>
{elements.map((element) =>
<View style={[ styles.element, this.props.elementStyle ]} />
)}
</View>
);
}
}
```
In another file:
```js
<List elementStyle={styles.listElement} style={styles.list} />
<List style={styles.list} elementStyle={styles.listElement} />
```
You also have much greater control over how styles are composed when compared
@@ -127,11 +121,10 @@ the CSS and takes precedence over the previous rules, resulting in a margin of
```html
<style>
.marginTop { margin-top: 10px; }
.marginBottom { margin-bottom: 20px; }
.margin { margin: 0; }
.marginTop { margin-top: 10px; }
.marginBottom { margin-bottom: 20px; }
.margin { margin: 0; }
</style>
<div class="marginTop marginBottom margin"></div>
```
@@ -174,10 +167,10 @@ Output:
```html
<style>
.rn-1mnahxq { margin-top: 0px; }
.rn-61z16t { margin-right: 0px; }
.rn-p1pxzi { margin-bottom: 0px; }
.rn-11wrixw { margin-left: 0px; }
.rn-1mnahxq { margin-top: 0px; }
.rn-61z16t { margin-right: 0px; }
.rn-p1pxzi { margin-bottom: 0px; }
.rn-11wrixw { margin-left: 0px; }
</style>
<div class="rn-156q2ks rn-61z16t rn-p1pxzi rn-11wrixw"></div>

View File

@@ -1,7 +1,3 @@
import { Meta } from '@storybook/addon-docs/blocks';
<Meta title="Guides|Web recipes" />
# Web recipes
Examples of how to implement web patterns with React Native.

View File

@@ -1,65 +1,64 @@
{
"private": true,
"version": "0.14.7",
"name": "monorepo",
"version": "0.9.13",
"name": "react-native-web-monorepo",
"scripts": {
"clean": "del-cli ./packages/*/dist",
"clean": "del ./packages/*/dist",
"compile": "npm-run-all clean -p \"compile:* -- {@}\" --",
"compile:commonjs": "cd packages/react-native-web && cross-env BABEL_ENV=commonjs babel --root-mode upward src --out-dir dist/cjs --ignore \"**/__tests__\"",
"compile:es": "cd packages/react-native-web && babel --root-mode upward src --out-dir dist --ignore \"**/__tests__\"",
"compile:commonjs": "cd packages/react-native-web && BABEL_ENV=commonjs babel src --out-dir dist/cjs --ignore \"**/__tests__\"",
"compile:es": "cd packages/react-native-web && babel src --out-dir dist --ignore \"**/__tests__\"",
"benchmarks": "cd packages/benchmarks && yarn build",
"benchmarks:release": "cd packages/benchmarks && yarn release",
"docs": "cd packages/docs && yarn start",
"docs:release": "cd packages/docs && yarn release",
"examples": "cd packages/examples && yarn build",
"examples:release": "cd packages/examples && yarn release",
"website": "cd packages/website && yarn start",
"website:release": "cd packages/website && yarn release",
"flow": "flow",
"fmt": "prettier --write \"**/*.js\"",
"jest": "jest --config ./scripts/jest/config.js",
"jest": "BABEL_ENV=commonjs jest --config ./scripts/jest/config.js",
"lint": "yarn lint:check --fix",
"lint:check": "eslint packages scripts",
"precommit": "lint-staged",
"prerelease": "yarn test && yarn compile && yarn compile:commonjs",
"release": "node ./scripts/release/publish.js",
"postrelease": "yarn benchmarks:release && yarn docs:release",
"postrelease": "yarn benchmarks:release && yarn examples:release && yarn website:release",
"test": "yarn flow && yarn lint:check && yarn jest --runInBand"
},
"devDependencies": {
"@babel/cli": "^7.8.4",
"@babel/core": "^7.8.4",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3",
"@babel/plugin-proposal-object-rest-spread": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.8.3",
"@babel/preset-env": "^7.8.4",
"@babel/preset-flow": "^7.8.3",
"@babel/preset-react": "^7.8.3",
"@testing-library/react": "^9.3.0",
"babel-eslint": "^10.0.3",
"babel-jest": "^25.1.0",
"babel-loader": "^8.0.6",
"babel-plugin-add-module-exports": "^1.0.2",
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
"caniuse-api": "^3.0.0",
"cross-env": "^6.0.3",
"del-cli": "^3.0.0",
"eslint": "^6.5.1",
"eslint-config-prettier": "^6.4.0",
"eslint-plugin-flowtype": "^4.7.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-react": "^7.16.0",
"eslint-plugin-react-hooks": "^2.3.0",
"flow-bin": "^0.109.0",
"glob": "^7.1.4",
"husky": "^3.0.8",
"inline-style-prefixer": "^5.1.0",
"jest": "^25.1.0",
"jest-canvas-mock": "^2.2.0",
"lint-staged": "^9.4.2",
"metro-react-native-babel-preset": "^0.56.0",
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-eslint": "^8.2.3",
"babel-loader": "^7.1.2",
"babel-plugin-add-module-exports": "^0.2.1",
"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.6.0",
"enzyme-adapter-react-16": "^1.5.0",
"enzyme-to-json": "^3.3.3",
"eslint": "^4.19.1",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-promise": "^3.7.0",
"eslint-plugin-react": "^7.7.0",
"flow-bin": "^0.63.1",
"glob": "^7.1.2",
"husky": "^0.14.3",
"inline-style-prefixer": "^5.0.3",
"jest": "^22.4.3",
"jest-canvas-mock": "^1.0.2",
"lint-staged": "^7.1.0",
"npm-run-all": "^4.1.3",
"prettier": "^1.18.2",
"react": "^16.10.2",
"react-dom": "^16.10.2",
"react-test-renderer": "^16.10.2"
"prettier": "^1.12.1",
"react": "^16.7.0",
"react-art": "^16.7.0",
"react-dom": "^16.7.0",
"react-test-renderer": "^16.7.0"
},
"workspaces": [
"packages/*"

View File

@@ -1,14 +1,10 @@
{
"publishConfig": {
"registry": "https://registry.npmjs.org/"
},
"name": "babel-plugin-react-native-web",
"version": "0.14.7",
"version": "0.9.13",
"description": "Babel plugin for React Native for Web",
"main": "index.js",
"devDependencies": {
"babel-core": "^6.26.3",
"babel-plugin-tester": "^7.0.4"
"babel-plugin-tester": "^5.0.0"
},
"author": "Nicolas Gallagher",
"license": "MIT",

View File

@@ -3,28 +3,30 @@
exports[`Rewrite react-native to react-native-web export from "react-native": export from "react-native" 1`] = `
"
export { View } from 'react-native';
export { StyleSheet, Text, unstable_createElement } 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 unstable_createElement } from 'react-native-web/dist/exports/createElement';
export { default as createElement } from 'react-native-web/dist/exports/createElement';
"
`;
exports[`Rewrite react-native to react-native-web export from "react-native-web": export from "react-native-web" 1`] = `
"
export { View } from 'react-native-web';
export { StyleSheet, Text, unstable_createElement } 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 unstable_createElement } from 'react-native-web/dist/exports/createElement';
export { default as createElement } from 'react-native-web/dist/exports/createElement';
"
`;
@@ -32,7 +34,7 @@ exports[`Rewrite react-native to react-native-web import from "native-native": i
"
import ReactNative from 'react-native';
import { View } from 'react-native';
import { Invalid, View as MyView } from 'react-native';
import { Invalid, View as MyView, ViewPropTypes } from 'react-native';
import * as ReactNativeModules from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
@@ -41,6 +43,7 @@ import ReactNative from 'react-native-web/dist/index';
import View from 'react-native-web/dist/exports/View';
import { Invalid } from 'react-native-web/dist/index';
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';
"
`;
@@ -49,7 +52,7 @@ exports[`Rewrite react-native to react-native-web import from "native-native": i
"
import ReactNative from 'react-native';
import { View } from 'react-native';
import { Invalid, View as MyView } from 'react-native';
import { Invalid, View as MyView, ViewPropTypes } from 'react-native';
import * as ReactNativeModules from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
@@ -58,19 +61,21 @@ import ReactNative from 'react-native-web/dist/cjs/index';
import View from 'react-native-web/dist/cjs/exports/View';
import { Invalid } from 'react-native-web/dist/cjs/index';
import MyView from 'react-native-web/dist/cjs/exports/View';
import ViewPropTypes from 'react-native-web/dist/cjs/exports/ViewPropTypes';
import * as ReactNativeModules from 'react-native-web/dist/cjs/index';
"
`;
exports[`Rewrite react-native to react-native-web import from "react-native-web": import from "react-native-web" 1`] = `
"
import { unstable_createElement } from 'react-native-web';
import { StyleSheet, View, TouchableOpacity, processColor } from 'react-native-web';
import { createElement } from 'react-native-web';
import { ColorPropType, StyleSheet, View, TouchableOpacity, processColor } from 'react-native-web';
import * as ReactNativeModules from 'react-native-web';
↓ ↓ ↓ ↓ ↓ ↓
import unstable_createElement from 'react-native-web/dist/exports/createElement';
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';
@@ -87,7 +92,7 @@ const { StyleSheet, TouchableOpacity } = require('react-native');
↓ ↓ ↓ ↓ ↓ ↓
const ReactNative = require('react-native-web/dist/index');
const ReactNative = require('react-native-web/dist/index').default;
const View = require('react-native-web/dist/exports/View').default;
@@ -105,7 +110,7 @@ const { StyleSheet, TouchableOpacity } = require('react-native');
↓ ↓ ↓ ↓ ↓ ↓
const ReactNative = require('react-native-web/dist/cjs/index');
const ReactNative = require('react-native-web/dist/cjs/index').default;
const View = require('react-native-web/dist/cjs/exports/View').default;
@@ -118,14 +123,16 @@ const TouchableOpacity = require('react-native-web/dist/cjs/exports/TouchableOpa
exports[`Rewrite react-native to react-native-web require "react-native-web": require "react-native-web" 1`] = `
"
const ReactNative = require('react-native-web');
const { unstable_createElement } = require('react-native-web');
const { StyleSheet, View, TouchableOpacity, processColor } = 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/dist/index');
const ReactNative = require('react-native-web/dist/index').default;
const unstable_createElement = require('react-native-web/dist/exports/createElement').default;
const createElement = require('react-native-web/dist/exports/createElement').default;
const ColorPropType = require('react-native-web/dist/exports/ColorPropType').default;
const StyleSheet = require('react-native-web/dist/exports/StyleSheet').default;

View File

@@ -7,7 +7,7 @@ const tests = [
title: 'import from "native-native"',
code: `import ReactNative from 'react-native';
import { View } from 'react-native';
import { Invalid, View as MyView } from 'react-native';
import { Invalid, View as MyView, ViewPropTypes } from 'react-native';
import * as ReactNativeModules from 'react-native';`,
snapshot: true
},
@@ -15,28 +15,28 @@ import * as ReactNativeModules from 'react-native';`,
title: 'import from "native-native"',
code: `import ReactNative from 'react-native';
import { View } from 'react-native';
import { Invalid, View as MyView } from 'react-native';
import { Invalid, View as MyView, ViewPropTypes } from 'react-native';
import * as ReactNativeModules from 'react-native';`,
snapshot: true,
pluginOptions: { commonjs: true }
},
{
title: 'import from "react-native-web"',
code: `import { unstable_createElement } from 'react-native-web';
import { StyleSheet, View, TouchableOpacity, processColor } 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 { StyleSheet, Text, unstable_createElement } 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 { StyleSheet, Text, unstable_createElement } from 'react-native-web';`,
export { ColorPropType, StyleSheet, Text, createElement } from 'react-native-web';`,
snapshot: true
},
{
@@ -57,20 +57,13 @@ const { StyleSheet, TouchableOpacity } = require('react-native');`,
{
title: 'require "react-native-web"',
code: `const ReactNative = require('react-native-web');
const { unstable_createElement } = require('react-native-web');
const { StyleSheet, View, TouchableOpacity, processColor } = require('react-native-web');`,
const { createElement } = require('react-native-web');
const { ColorPropType, StyleSheet, View, TouchableOpacity, processColor } = require('react-native-web');`,
snapshot: true
}
];
pluginTester({
babelOptions: {
generatorOpts: {
jsescOption: {
quotes: 'single'
}
}
},
plugin,
tests
});

View File

@@ -4,11 +4,10 @@ const isCommonJS = opts => opts.commonjs === true;
const getDistLocation = (importName, opts) => {
const format = isCommonJS(opts) ? 'cjs/' : '';
const internalName = importName === 'unstable_createElement' ? 'createElement' : importName;
if (internalName === 'index') {
if (importName === 'index') {
return `react-native-web/dist/${format}index`;
} else if (internalName && moduleMap[internalName]) {
return `react-native-web/dist/${format}exports/${internalName}`;
} else if (importName && moduleMap[importName]) {
return `react-native-web/dist/${format}exports/${importName}`;
}
};
@@ -119,9 +118,12 @@ module.exports = function({ types: t }) {
const importIndex = t.variableDeclaration(path.node.kind, [
t.variableDeclarator(
t.identifier(name),
t.callExpression(t.identifier('require'), [
t.stringLiteral(getDistLocation('index', state.opts))
])
t.memberExpression(
t.callExpression(t.identifier('require'), [
t.stringLiteral(getDistLocation('index', state.opts))
]),
t.identifier('default')
)
)
]);

View File

@@ -1,56 +1,56 @@
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
module.exports = {
ART: true,
AccessibilityInfo: true,
ActivityIndicator: true,
Alert: true,
Animated: true,
AppRegistry: true,
AppState: true,
Appearance: true,
AsyncStorage: true,
BackHandler: true,
Button: true,
CheckBox: true,
Clipboard: true,
DeviceEventEmitter: true,
ColorPropType: true,
DeviceInfo: true,
Dimensions: true,
DrawerLayoutAndroid: true,
Easing: true,
EdgeInsetsPropType: true,
FlatList: true,
I18nManager: true,
Image: true,
ImageBackground: true,
InputAccessoryView: true,
InteractionManager: true,
Keyboard: true,
KeyboardAvoidingView: true,
LayoutAnimation: true,
Linking: true,
LogBox: true,
ListView: true,
Modal: true,
NativeEventEmitter: true,
NativeModules: true,
NetInfo: true,
PanResponder: true,
PermissionsAndroid: true,
Picker: true,
PixelRatio: true,
Platform: true,
Pressable: true,
PointPropType: true,
ProgressBar: true,
RefreshControl: true,
SafeAreaView: true,
ScrollView: true,
SectionList: true,
Settings: true,
Share: true,
Slider: true,
StatusBar: true,
StyleSheet: true,
SwipeableFlatList: true,
SwipeableListView: true,
Switch: true,
Systrace: true,
TVEventHandler: true,
Text: true,
TextInput: true,
ToastAndroid: true,
TextPropTypes: true,
Touchable: true,
TouchableHighlight: true,
TouchableNativeFeedback: true,
@@ -59,13 +59,12 @@ module.exports = {
UIManager: true,
Vibration: true,
View: true,
ViewPropTypes: true,
VirtualizedList: true,
YellowBox: true,
createElement: true,
findNodeHandle: true,
processColor: true,
render: true,
unmountComponentAtNode: true,
useColorScheme: true,
useWindowDimensions: true
unmountComponentAtNode: true
};

View File

@@ -47,24 +47,24 @@ included.
### MacBook Pro (2011)
MacBook Pro (13-inch, Early 2011); 2.3 GHz Intel Core i5; 8 GB 1333 MHz DDR3 RAM. Google Chrome 72.
MacBook Pro (13-inch, Early 2011); 2.3 GHz Intel Core i5; 8 GB 1333 MHz DDR3 RAM. Google Chrome 63.
Typical render timings: mean ± standard deviations.
| Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) |
| :--- | ---: | ---: | ---: |
| `css-modules` | `23.41` `±03.06` | `35.38` `±06.41` | - |
| `react-native-web@0.11.0` | `28.37` `±04.39` | `41.50` `±05.75` | `23.13` `±03.51` |
| `inline-styles` | `66.19` `±06.31` | `104.22` `±10.22` | `09.96` `±02.76` |
| `css-modules` | `30.19` `±04.84` | `38.25` `±04.85` | - |
| `react-native-web@0.4.0` | `36.40` `±04.98` | `51.28` `±05.58` | `19.36` `±02.56` |
| `inline-styles` | `64.12` `±07.69` | `94.49` `±11.34` | `09.84` `±02.36` |
### Moto G4
Moto G4 (Android 7); Octa-core (4x1.5 GHz & 4x1.2 Ghz); 2 GB RAM. Google Chrome 72.
Moto G4 (Android 7); Octa-core (4x1.5 GHz & 4x1.2 Ghz); 2 GB RAM. Google Chrome 63.
Typical render timings: mean ± standard deviations.
| Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) |
| :--- | ---: | ---: | ---: |
| `css-modules` | `71.33` 09.68` | `101.36` 12.36` | - |
| `react-native-web@0.11.0` | `83.65` `±12.40` | `123.59` `±14.40` | `75.41` `±07.74` |
| `inline-styles` | `188.35` 17.69` | `282.35` `±22.48` | `28.10` `±06.87` |
| `css-modules` | `98.24` 20.26` | `143.75` 25.50` | - |
| `react-native-web@0.4.0` | `131.46` `±18.96` | `174.70` `±14.88` | `60.87` `±06.32` |
| `inline-styles` | `184.58` 26.23` | `273.86` `±26.23` | `30.28` `±07.44` |

View File

@@ -1,33 +1,35 @@
{
"private": true,
"name": "benchmarks",
"version": "0.14.7",
"version": "0.9.13",
"scripts": {
"build": "mkdir -p dist && cp -f index.html dist/index.html && ./node_modules/.bin/webpack-cli --config ./webpack.config.js",
"release": "NODE_ENV=production yarn build && git checkout gh-pages && rm -rf ../../benchmarks && mv dist ../../benchmarks && git add -A && git commit -m \"Benchmarks deploy\" && git push origin gh-pages && git checkout -"
},
"dependencies": {
"aphrodite": "^2.4.0",
"aphrodite": "^2.2.3",
"classnames": "^2.2.6",
"d3-scale-chromatic": "^1.5.0",
"emotion": "^10.0.27",
"fela": "^11.0.2",
"react-fela": "^11.0.2",
"react-jss": "^10.0.4",
"react-native-web": "0.14.7",
"reactxp": "^2.0.0",
"styled-components": "^5.0.0",
"styled-jsx": "^3.2.4",
"styletron-engine-atomic": "^1.4.4",
"styletron-react": "^5.2.6"
"d3-scale-chromatic": "^1.3.3",
"emotion": "^10.0.5",
"fela": "^10.0.2",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"react-fela": "^10.0.2",
"react-jss": "^8.6.1",
"react-native-web": "0.9.13",
"reactxp": "^1.5.0",
"styled-components": "^4.1.3",
"styled-jsx": "^3.1.2",
"styletron-engine-atomic": "^1.0.13",
"styletron-react": "^4.4.4"
},
"devDependencies": {
"babel-plugin-react-native-web": "0.14.7",
"css-loader": "^3.4.2",
"style-loader": "^1.1.3",
"url-loader": "^3.0.0",
"webpack": "^4.41.5",
"webpack-bundle-analyzer": "^3.6.0",
"webpack-cli": "^3.3.10"
"babel-plugin-react-native-web": "0.9.13",
"css-loader": "^2.0.2",
"style-loader": "^0.23.1",
"url-loader": "^1.1.2",
"webpack": "^4.28.1",
"webpack-bundle-analyzer": "^3.0.3",
"webpack-cli": "^3.1.2"
}
}

View File

@@ -1,3 +1,5 @@
/* eslint-disable react/prop-types */
import Benchmark from './Benchmark';
import { Picker, StyleSheet, ScrollView, TouchableOpacity, View } from 'react-native';
import React, { Component } from 'react';

View File

@@ -2,11 +2,12 @@
* The MIT License (MIT)
* Copyright (c) 2017 Paul Armstrong
* https://github.com/paularmstrong/react-component-benchmark
* @flow
*/
/* global $Values */
/**
* @flow
*/
import * as Timing from './timing';
import React, { Component } from 'react';
import { getMean, getMedian, getStdDev } from './math';

View File

@@ -1,9 +1,21 @@
import { StyleSheet, TouchableHighlight, Text } from 'react-native';
import { ColorPropType, StyleSheet, TouchableHighlight, Text } from 'react-native';
import React, { Component } from 'react';
import { bool, func, string } from 'prop-types';
export default class Button extends Component<*> {
static displayName = '@app/Button';
static propTypes = {
accessibilityLabel: string,
color: ColorPropType,
disabled: bool,
onPress: func.isRequired,
style: TouchableHighlight.propTypes.style,
testID: string,
textStyle: Text.propTypes.style,
title: string.isRequired
};
render() {
const {
accessibilityLabel,

View File

@@ -1,5 +1,5 @@
import React, { Fragment } from 'react';
import { unstable_createElement as createElement, StyleSheet } from 'react-native';
import { createElement, StyleSheet, Text } from 'react-native';
const styles = StyleSheet.create({
root: {
@@ -18,16 +18,16 @@ const createIcon = children => {
createElement(
'svg',
{
style: StyleSheet.compose(
styles.root,
props.style
),
style: StyleSheet.compose(styles.root, props.style),
width: 24,
height: 24,
viewBox: '0 0 24 24'
},
children
);
Icon.propTypes = {
style: Text.propTypes.style
};
return Icon;
};

View File

@@ -1,8 +1,15 @@
import { colors } from './theme';
import { element } from 'prop-types';
import React, { Component } from 'react';
import { StyleSheet, View } from 'react-native';
export default class Layout extends Component {
static propTypes = {
actionPanel: element,
listPanel: element,
viewPanel: element
};
state = {
widescreen: false
};

View File

@@ -1,5 +1,4 @@
/* @noflow */
/* eslint-disable react/prop-types */
import Text from './Text';
import { StyleSheet, View } from 'react-native';
import React, { Fragment } from 'react';

View File

@@ -1,6 +1,8 @@
/* eslint-disable react/prop-types */
import { bool } from 'prop-types';
import React from 'react';
import { StyleSheet, Text } from 'react-native';
import { bool } from 'prop-types';
import { colors } from './theme';
class AppText extends React.Component {

View File

@@ -1,4 +1,5 @@
import { BenchmarkType } from '../app/Benchmark';
import { number, object } from 'prop-types';
import React from 'react';
import { interpolatePurples, interpolateBuPu, interpolateRdPu } from 'd3-scale-chromatic';
@@ -9,6 +10,15 @@ class SierpinskiTriangle extends React.Component {
static benchmarkType = BenchmarkType.UPDATE;
static propTypes = {
components: object,
depth: number,
renderCount: number,
s: number,
x: number,
y: number
};
static defaultProps = {
depth: 0,
renderCount: 0
@@ -35,7 +45,7 @@ class SierpinskiTriangle extends React.Component {
}
// introduce randomness to ensure that repeated runs don't produce the same colors
const color = fn((renderCount * Math.random()) / 20);
const color = fn(renderCount * Math.random() / 20);
return (
<Dot color={color} size={targetSize} x={x - targetSize / 2} y={y - targetSize / 2} />
);

View File

@@ -1,36 +0,0 @@
import { BenchmarkType } from '../app/Benchmark';
import React, { Component } from 'react';
class TextTree extends Component {
static displayName = 'TextTree';
static benchmarkType = BenchmarkType.MOUNT;
render() {
const { breadth, components, depth, id, wrap } = this.props;
const { TextBox } = components;
let result = (
<TextBox children={'TextBox ${id % 3}'} color={id % 3} outer>
{depth === 0 && <TextBox children={'Depth 0'} color={(id % 3) + 3} />}
{depth !== 0 &&
Array.from({ length: breadth }).map((el, i) => (
<TextTree
breadth={breadth}
components={components}
depth={depth - 1}
id={i}
key={i}
wrap={wrap}
/>
))}
</TextBox>
);
for (let i = 0; i < wrap; i++) {
result = <TextBox>{result}</TextBox>;
}
return result;
}
}
export default TextTree;

View File

@@ -1,4 +1,5 @@
import { BenchmarkType } from '../app/Benchmark';
import { number, object } from 'prop-types';
import React, { Component } from 'react';
class Tree extends Component {
@@ -6,13 +7,21 @@ class Tree extends Component {
static benchmarkType = BenchmarkType.MOUNT;
static propTypes = {
breadth: number.isRequired,
components: object,
depth: number.isRequired,
id: number.isRequired,
wrap: number.isRequired
};
render() {
const { breadth, components, depth, id, wrap } = this.props;
const { Box } = components;
let result = (
<Box color={id % 3} layout={depth % 2 === 0 ? 'column' : 'row'} outer>
{depth === 0 && <Box color={(id % 3) + 3} fixed />}
{depth === 0 && <Box color={id % 3 + 3} fixed />}
{depth !== 0 &&
Array.from({ length: breadth }).map((el, i) => (
<Tree

View File

@@ -1,5 +1,3 @@
/* @noflow */
import { type Component } from 'react';
import packageJson from '../package.json';
@@ -10,7 +8,6 @@ type ComponentsType = {
Box: Component,
Dot: Component,
Provider: Component,
TextBox: Component,
View: Component
};

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import React from 'react';
import View from './View';
import { StyleSheet } from 'aphrodite';

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { css, StyleSheet } from 'aphrodite';

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import React from 'react';
import View from './View';

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import React from 'react';
import styles from './view-styles.css';

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import React from 'react';
import View from './View';

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { css } from 'emotion';

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import { css } from 'emotion';
import React from 'react';

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import React from 'react';
import View from './View';

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import React from 'react';
const Dot = ({ size, x, y, children, color }) => (

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import React from 'react';
const compose = (s1, s2) => {
@@ -11,15 +12,7 @@ const compose = (s1, s2) => {
class View extends React.Component {
render() {
const { style, ...other } = this.props;
return (
<div
{...other}
style={compose(
viewStyle,
style
)}
/>
);
return <div {...other} style={compose(viewStyle, style)} />;
}
}

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import { createComponent } from 'react-fela';
const Dot = createComponent(

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { createRenderer } from 'fela';
import { Provider as FelaProvider } from 'react-fela';

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import { createComponent } from 'react-fela';
const View = createComponent(

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import injectSheet from 'react-jss';
import React from 'react';

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import injectSheet from 'react-jss';
import React from 'react';

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import injectSheet from 'react-jss';
import React from 'react';

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { StyleSheet, View } from 'react-native';

View File

@@ -1,4 +1,5 @@
import { unstable_createElement as createElement, StyleSheet } from 'react-native';
/* eslint-disable react/prop-types */
import { createElement, StyleSheet } from 'react-native';
const Dot = ({ size, x, y, children, color }) =>
createElement('div', {

View File

@@ -1,38 +0,0 @@
import React from 'react';
import { StyleSheet, Text } from 'react-native';
const TextBox = ({ color, outer = false, ...other }) => (
<Text {...other} style={[styles.root, styles[`color${color}`], outer && styles.outer]} />
);
const styles = StyleSheet.create({
root: {
color: 'white'
},
outer: {
fontStyle: 'italic'
},
row: {
flexDirection: 'row'
},
color0: {
color: '#14171A'
},
color1: {
color: '#AAB8C2'
},
color2: {
color: '#E6ECF0'
},
color3: {
color: '#FFAD1F'
},
color4: {
color: '#F45D22'
},
color5: {
color: '#E0245E'
}
});
export default TextBox;

View File

@@ -0,0 +1,108 @@
import PropTypes from 'prop-types';
import theme from './theme';
import React, { PureComponent } from 'react';
import { StyleSheet, Text } from 'react-native';
class AppText extends PureComponent {
static displayName = 'AppText';
static propTypes = {
align: PropTypes.oneOf(['center', 'left', 'right']),
color: PropTypes.oneOf(['blue', 'deepGray', 'normal', 'red', 'white']),
fontStyle: PropTypes.oneOf(['normal', 'italic']),
size: PropTypes.oneOf(['small', 'normal', 'large']),
uppercase: PropTypes.bool,
weight: PropTypes.oneOf(['normal', 'bold'])
};
render() {
const { align, color, fontStyle, size, uppercase, weight, ...other } = this.props;
const style = [
styles.root,
align && alignStyles[align],
color && colorStyles[color],
fontStyle && fontStyles[fontStyle],
size && sizeStyles[size],
weight && weightStyles[weight],
uppercase === true && styles.uppercase
];
return <Text {...other} style={style} />;
}
}
const styles = StyleSheet.create({
root: {
fontFamily: theme.fontFamily,
fontSize: theme.fontSize.normal,
fontWeight: 'normal',
lineHeight: theme.createLength(theme.lineHeight),
wordWrap: 'break-word'
},
uppercase: {
textTransform: 'uppercase'
}
});
const alignStyles = StyleSheet.create({
center: {
textAlign: 'center'
},
left: {
textAlign: 'left'
},
right: {
textAlign: 'right'
}
});
const colorStyles = StyleSheet.create({
blue: {
color: theme.colors.blue
},
deepGray: {
color: theme.colors.deepGray
},
normal: {
color: theme.colors.textBlack
},
red: {
color: theme.colors.red
},
white: {
color: theme.colors.white
}
});
const fontStyles = StyleSheet.create({
normal: {
fontStyle: 'normal'
},
italic: {
fontStyle: 'italic'
}
});
const sizeStyles = StyleSheet.create({
small: {
fontSize: theme.fontSize.small
},
normal: {
fontSize: theme.fontSize.normal
},
large: {
fontSize: theme.fontSize.large
}
});
const weightStyles = StyleSheet.create({
normal: {
fontWeight: '400'
},
bold: {
fontWeight: 'bold'
}
});
export default AppText;

View File

@@ -0,0 +1,41 @@
import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';
import { StyleSheet, View, ViewPropTypes } from 'react-native';
class AspectRatio extends PureComponent {
static displayName = 'AspectRatio';
static propTypes = {
children: PropTypes.any,
ratio: PropTypes.number,
style: ViewPropTypes.style
};
static defaultProps = {
ratio: 1
};
render() {
const { children, ratio, style } = this.props;
const percentage = 100 / ratio;
return (
<View style={[styles.root, style]}>
<View style={[styles.shim, { paddingBottom: `${percentage}%` }]} />
<View style={StyleSheet.absoluteFill}>{children}</View>
</View>
);
}
}
const styles = StyleSheet.create({
root: {
overflow: 'hidden'
},
shim: {
display: 'block',
width: '100%'
}
});
export default AspectRatio;

View File

@@ -0,0 +1,53 @@
import PropTypes from 'prop-types';
import { StyleSheet, View, ViewPropTypes } from 'react-native';
import React, { Component } from 'react';
import theme from './theme';
class GridView extends Component {
static displayName = 'GridView';
static propTypes = {
children: PropTypes.node,
hasGap: PropTypes.bool,
style: ViewPropTypes.style
};
render() {
const { children, hasGap, style, ...other } = this.props;
return (
<View {...other} style={[style, styles.root, hasGap && styles.hasGap]}>
{React.Children.map(children, child => {
return (
child &&
React.cloneElement(child, {
style: [child.props.style, styles.column, hasGap && styles.hasGapColumn]
})
);
})}
</View>
);
}
}
const styles = StyleSheet.create({
root: {
flexDirection: 'row'
},
/**
* 1. Distribute all space (rather than extra space)
* 2. Prevent wide content from forcing wider flex columns
*/
column: {
flexBasis: 0, // 1
minWidth: 0 // 2
},
hasGap: {
marginHorizontal: theme.createLength(theme.spaceX * -0.5, 'rem')
},
hasGapColumn: {
marginHorizontal: theme.createLength(theme.spaceX * 0.5, 'rem')
}
});
export default GridView;

View File

@@ -0,0 +1,20 @@
/* eslint-disable react/prop-types */
import { createElement } from 'react-native';
import React from 'react';
import styles from './styles';
const IconDirectMessage = props =>
createElement('svg', {
children: (
<g>
<path d="M43.34 14H12.66L28 27.946z" />
<path d="M51.392 14.789L30.018 34.22c-.009.008-.028.006-.039.012-.563.5-1.266.768-1.98.768-.72 0-1.442-.258-2.017-.78L4.609 14.79A3.957 3.957 0 0 0 3 18v37a1.998 1.998 0 0 0 2 2c.464 0 .924-.162 1.292-.473L19 46h30c2.243 0 4-1.757 4-4V18a3.96 3.96 0 0 0-1.608-3.211z" />
</g>
),
style: [styles.icon, props.style],
viewBox: '0 0 56 72'
});
IconDirectMessage.metadata = { height: 72, width: 56 };
export default IconDirectMessage;

View File

@@ -0,0 +1,19 @@
/* eslint-disable react/prop-types */
import { createElement } from 'react-native';
import React from 'react';
import styles from './styles';
const IconHeart = props =>
createElement('svg', {
children: (
<g>
<path d="M38.723 12c-7.187 0-11.16 7.306-11.723 8.131C26.437 19.306 22.504 12 15.277 12 8.791 12 3.533 18.163 3.533 24.647 3.533 39.964 21.891 55.907 27 56c5.109-.093 23.467-16.036 23.467-31.353C50.467 18.163 45.209 12 38.723 12z" />
</g>
),
style: [styles.icon, props.style],
viewBox: '0 0 54 72'
});
IconHeart.metadata = { height: 72, width: 54 };
export default IconHeart;

View File

@@ -0,0 +1,19 @@
/* eslint-disable react/prop-types */
import { createElement } from 'react-native';
import React from 'react';
import styles from './styles';
const IconReply = props =>
createElement('svg', {
children: (
<g>
<path d="M41 31h-9V19a2.999 2.999 0 0 0-4.817-2.386l-21 16a3 3 0 0 0-.001 4.773l21 16a3.006 3.006 0 0 0 3.15.301A2.997 2.997 0 0 0 32 51V39h9c5.514 0 10 4.486 10 10a4 4 0 0 0 8 0c0-9.925-8.075-18-18-18z" />
</g>
),
style: [styles.icon, props.style],
viewBox: '0 0 62 72'
});
IconReply.metadata = { height: 72, width: 62 };
export default IconReply;

View File

@@ -0,0 +1,19 @@
/* eslint-disable react/prop-types */
import { createElement } from 'react-native';
import React from 'react';
import styles from './styles';
const IconRetweet = props =>
createElement('svg', {
children: (
<g>
<path d="M70.676 36.644A3 3 0 0 0 68 35h-7V19a4 4 0 0 0-4-4H34a4 4 0 0 0 0 8h18a1 1 0 0 1 1 .998V35h-7a3.001 3.001 0 0 0-2.419 4.775l11 15a3.003 3.003 0 0 0 4.839-.001l11-15a3.001 3.001 0 0 0 .256-3.13zM40.001 48H22a.995.995 0 0 1-.992-.96L21.001 36h7a3.001 3.001 0 0 0 2.419-4.775l-11-15a3.003 3.003 0 0 0-4.839.001l-11 15A3 3 0 0 0 6.001 36h7l.011 16.003a4 4 0 0 0 4 3.997h22.989a4 4 0 0 0 0-8z" />
</g>
),
style: [styles.icon, props.style],
viewBox: '0 0 74 72'
});
IconRetweet.metadata = { height: 72, width: 74 };
export default IconRetweet;

View File

@@ -0,0 +1,78 @@
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, ViewPropTypes, StyleSheet } from 'react-native';
const getIcon = (icon, highlighted) => {
switch (icon) {
case 'like':
return <IconHeart />;
case 'reply':
return <IconReply />;
case 'retweet':
return <IconRetweet />;
case 'directMessage':
return <IconDirectMessage />;
default:
return null;
}
};
export default class TweetAction extends React.Component {
static displayName = 'TweetAction';
static propTypes = {
count: PropTypes.number,
displayMode: PropTypes.oneOf(['like', 'reply', 'retweet', 'directMessage']),
highlighted: PropTypes.bool,
onPress: PropTypes.func,
style: ViewPropTypes.style
};
render() {
const { count, displayMode, highlighted, onPress, style } = this.props;
return (
<View accessibilityRole="button" onPress={onPress} style={[styles.root, style]}>
<Text
style={[
styles.inner,
displayMode === 'like' && highlighted && styles.likedColor,
displayMode === 'retweet' && highlighted && styles.retweetedColor
]}
>
{getIcon(displayMode, highlighted)}
{count > 0 ? <Text style={styles.count}>{count}</Text> : null}
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
root: {
minHeight: theme.createLength(theme.lineHeight, 'rem'),
overflow: 'visible',
userSelect: 'none',
whiteSpace: 'nowrap'
},
inner: {
alignItems: 'center',
color: theme.colors.deepGray,
display: 'flex',
flexDirection: 'row'
},
count: {
marginLeft: '0.25em'
},
retweetedColor: {
color: theme.colors.green
},
likedColor: {
color: theme.colors.red
}
});

View File

@@ -0,0 +1,52 @@
import PropTypes from 'prop-types';
import TweetAction from './TweetAction';
import { View, ViewPropTypes, StyleSheet } from 'react-native';
import React, { PureComponent } from 'react';
const actionNames = ['reply', 'retweet', 'like', 'directMessage'];
export default class TweetActionsBar extends PureComponent {
static propTypes = {
actions: PropTypes.arrayOf(
PropTypes.shape({
count: PropTypes.number,
label: PropTypes.string,
highlighted: PropTypes.bool,
name: PropTypes.oneOf(actionNames).isRequired,
onPress: PropTypes.func
})
),
style: ViewPropTypes.style
};
render() {
const { actions, style } = this.props;
/* eslint-disable react/jsx-handler-names */
return (
<View style={[styles.root, style]}>
{actions.map((action, i) => (
<TweetAction
accessibilityLabel={actions.label}
count={action.count}
displayMode={action.name}
highlighted={action.highlighted}
key={i}
onPress={action.onPress}
style={styles.action}
/>
))}
</View>
);
}
}
const styles = StyleSheet.create({
root: {
flexDirection: 'row'
},
action: {
display: 'block',
marginRight: '10%'
}
});

View File

@@ -0,0 +1,29 @@
import AppText from './AppText';
import React from 'react';
import TweetTextPart from './TweetTextPart';
import { array, number, string } from 'prop-types';
class TweetText extends React.Component {
static displayName = 'TweetText';
static propTypes = {
displayMode: TweetTextPart.propTypes.displayMode,
lang: string,
numberOfLines: number,
textParts: array.isRequired
};
render() {
const { displayMode, lang, numberOfLines, textParts, ...other } = this.props;
return (
<AppText {...other} lang={lang} numberOfLines={numberOfLines}>
{textParts.map((part, i) => (
<TweetTextPart displayMode={displayMode} key={i} part={part} />
))}
</AppText>
);
}
}
export default TweetText;

View File

@@ -0,0 +1,113 @@
/* eslint-disable react/prop-types */
import { Image, StyleSheet, Text } from 'react-native';
import PropTypes from 'prop-types';
import React from 'react';
import theme from './theme';
const createTextEntity = ({ part }) => <Text>{`${part.prefix}${part.text}`}</Text>;
const createTwemojiEntity = ({ part }) => (
<Image
accessibilityLabel={part.text}
draggable={false}
source={{ uri: part.emoji }}
style={styles.twemoji}
/>
);
// @mention, #hashtag, $cashtag
const createSymbolEntity = ({ displayMode, part }) => {
const links = displayMode === 'links';
return (
<Text accessibilityRole={links ? 'link' : null} href={part.url} style={[links && styles.link]}>
{`${part.prefix}${part.text}`}
</Text>
);
};
// internal links
const createLinkEntity = ({ displayMode, part }) => {
const { displayUrl, linkRelation, url } = part;
const links = displayMode === 'links';
return (
<Text
accessibilityRole={links ? 'link' : null}
href={url}
rel={links ? linkRelation : null}
style={[links && styles.link]}
>
{displayUrl}
</Text>
);
};
// external links
const createExternalLinkEntity = ({ displayMode, part }) => {
const { displayUrl, linkRelation, url } = part;
const links = displayMode === 'links';
return (
<Text
accessibilityRole={links ? 'link' : null}
href={url}
rel={links ? linkRelation : null}
style={[links && styles.link]}
target="_blank"
>
{displayUrl}
</Text>
);
};
class TweetTextPart extends React.Component {
static displayName = 'TweetTextPart';
static propTypes = {
displayMode: PropTypes.oneOf(['links', 'no-links']),
part: PropTypes.object
};
static defaultProps = {
displayMode: 'links'
};
render() {
let renderer;
const { isEmoji, isEntity, isHashtag, isMention, isMedia, isUrl } = this.props.part;
if (isEmoji || isEntity || isUrl || isMedia) {
if (isUrl) {
renderer = createExternalLinkEntity;
} else if (isHashtag || isMention) {
renderer = createSymbolEntity;
} else if (isEmoji) {
renderer = createTwemojiEntity;
} else {
renderer = createLinkEntity;
}
} else {
renderer = createTextEntity;
}
return renderer(this.props);
}
}
const styles = StyleSheet.create({
link: {
color: theme.colors.blue,
textDecorationLine: 'none',
unicodeBidi: 'embed'
},
twemoji: {
display: 'inline-block',
height: '1.25em',
width: '1.25em',
paddingRight: '0.05em',
paddingLeft: '0.1em',
textAlignVertical: '-0.2em'
}
});
export default TweetTextPart;

View File

@@ -0,0 +1,65 @@
import AspectRatio from './AspectRatio';
import PropTypes from 'prop-types';
import { Image, StyleSheet, ViewPropTypes } from 'react-native';
import React, { PureComponent } from 'react';
import theme from './theme';
class UserAvatar extends PureComponent {
static displayName = 'UserAvatar';
static propTypes = {
accessibilityLabel: PropTypes.string,
circle: PropTypes.bool,
style: ViewPropTypes.style,
uri: PropTypes.string
};
static defaultProps = {
circle: false
};
render() {
const { accessibilityLabel, circle, style, uri } = this.props;
return (
<AspectRatio ratio={1} style={[styles.root, style]}>
{uri ? (
<Image
accessibilityLabel={accessibilityLabel}
onLoad={this._handleLoad}
ref={this._setImageRef}
source={{ uri }}
style={[styles.image, circle && styles.circle]}
/>
) : null}
</AspectRatio>
);
}
_handleLoad = () => {
this._imageRef && this._imageRef.setNativeProps(nativeProps);
};
_setImageRef = component => {
this._imageRef = component;
};
}
const nativeProps = { style: { backgroundColor: '#fff' } };
const styles = StyleSheet.create({
root: {
borderRadius: '0.35rem'
},
circle: {
borderRadius: '9999px'
},
image: {
backgroundColor: theme.colors.fadedGray,
display: 'block',
height: '100%',
width: '100%'
}
});
export default UserAvatar;

View File

@@ -0,0 +1,52 @@
import AppText from './AppText';
import PropTypes from 'prop-types';
import { StyleSheet, ViewPropTypes } from 'react-native';
import React, { PureComponent } from 'react';
class UserNames extends PureComponent {
static displayName = 'UserNames';
static propTypes = {
fullName: PropTypes.string,
layout: PropTypes.oneOf(['nowrap', 'stack']),
onPress: PropTypes.func,
screenName: PropTypes.string,
style: ViewPropTypes.style
};
static defaultProps = {
layout: 'nowrap'
};
render() {
const { fullName, layout, onPress, screenName, style, ...other } = this.props;
return (
<AppText
{...other}
color="deepGray"
numberOfLines={layout === 'nowrap' ? 1 : null}
onPress={onPress}
style={[styles.root, style]}
>
<AppText color="normal" weight="bold">
{fullName}
</AppText>
{layout === 'stack' ? ' \u000A' : ' '}
<AppText color="deepGray" style={styles.screenName}>{`@${screenName}`}</AppText>
</AppText>
);
}
}
const styles = StyleSheet.create({
root: {
display: 'inline-block'
},
screenName: {
unicodeBidi: 'embed',
writingDirection: 'ltr'
}
});
export default UserNames;

View File

@@ -0,0 +1,144 @@
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 { Image, StyleSheet, Text, View } from 'react-native';
import React, { Component } from 'react';
import theme from './theme';
export class Tweet extends Component {
static displayName = 'Tweet';
static propTypes = {
tweet: PropTypes.object.isRequired
};
render() {
const { tweet } = this.props;
const { id, lang, media, textParts, timestamp, user } = tweet;
const { fullName, profileImageUrl, screenName } = user;
return (
<View accessibilityRole="article" accessible style={styles.root}>
<GridView hasGap>
<View style={styles.avatarColumn}>
<View
accessibilityRole="link"
accessible
href={`/${screenName}`}
style={styles.avatarLink}
>
<UserAvatar style={styles.avatar} uri={profileImageUrl} />
</View>
</View>
<View style={styles.bodyColumn}>
<View style={styles.body}>
<View style={styles.row}>
<Text
accessibilityRole="link"
children={timestamp}
href={`/${screenName}/status/${id}`}
style={styles.timestamp}
/>
<UserNames fullName={fullName} screenName={screenName} />
</View>
<View accessibilityRole="heading" aria-level="4">
<TweetText displayMode={'links'} lang={lang} textParts={textParts} />
</View>
{media ? (
<View style={styles.richContent}>
<AspectRatio ratio={16 / 9}>
<Image
resizeMode={Image.resizeMode.cover}
source={media.source}
style={styles.media}
/>
</AspectRatio>
</View>
) : null}
</View>
<TweetActionsBar
actions={[
{ name: 'reply', label: 'Reply' },
{
name: 'retweet',
label: 'Retweet',
count: tweet.retweet_count,
highlighted: tweet.retweeted
},
{
name: 'like',
label: 'Like',
count: tweet.favorite_count,
highlighted: tweet.favorited
},
{ name: 'directMessage', label: 'Direct Message' }
]}
style={styles.actionBar}
/>
</View>
</GridView>
</View>
);
}
}
const styles = StyleSheet.create({
root: {
paddingVertical: theme.createLength(theme.spaceY * 0.75, 'rem'),
paddingHorizontal: theme.createLength(theme.spaceX, 'rem')
},
avatarColumn: {
flexGrow: 1,
minWidth: 32
},
bodyColumn: {
flexGrow: 7
},
row: {
flexDirection: 'row',
justifyContent: 'space-between'
},
avatarLink: {
display: 'block',
flexShrink: 1,
flexGrow: 0,
width: '100%'
},
avatar: {
width: '100%'
},
body: {
marginTop: '-0.15rem'
},
timestamp: {
color: theme.colors.deepGray,
marginLeft: theme.createLength(theme.spaceX, 'rem'),
order: 1,
textDecorationLine: 'none',
whiteSpace: 'nowrap'
},
actionBar: {
marginTop: theme.createLength(theme.spaceY * 0.5, 'rem')
},
richContent: {
borderRadius: '0.35rem',
marginTop: theme.createLength(theme.spaceY * 0.5, 'rem'),
overflow: 'hidden'
},
media: {
...StyleSheet.absoluteFillObject,
margin: 'auto',
width: 'auto',
height: 'auto'
}
});
export default Tweet;

View File

@@ -0,0 +1,15 @@
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
icon: {
display: 'inline-block',
fill: 'currentcolor',
height: '1.25em',
maxWidth: '100%',
position: 'relative',
userSelect: 'none',
textAlignVertical: 'text-bottom'
}
});
export default styles;

View File

@@ -0,0 +1,40 @@
const colors = {
blue: '#1B95E0',
lightBlue: '#71C9F8',
green: '#17BF63',
orange: '#F45D22',
purple: '#794BC4',
red: '#E0245E',
white: '#FFFFFF',
yellow: '#FFAD1F',
deepGray: '#657786',
fadedGray: '#E6ECF0',
faintGray: '#F5F8FA',
gray: '#AAB8C2',
lightGray: '#CCD6DD',
textBlack: '#14171A'
};
const fontSize = {
root: '14px',
// font scale
small: '0.85rem',
normal: '1rem',
large: '1.25rem'
};
const theme = {
colors,
fontFamily:
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif, ' +
'"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', // emoji fonts
fontSize,
lineHeight: 1.3125,
spaceX: 0.6,
spaceY: 1.3125,
createLength(num, unit) {
return `${num}${unit}`;
}
};
export default theme;

View File

@@ -1,13 +1,11 @@
import Box from './Box';
import Dot from './Dot';
import Provider from './Provider';
import TextBox from './TextBox';
import { View } from 'react-native';
export default {
Box,
Dot,
Provider,
TextBox,
View
};

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { Styles, View } from 'reactxp';

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { Styles, View } from 'reactxp';

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { object } from 'prop-types';
import { View } from 'reactxp';

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import styled from 'styled-components';
import View from './View';

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import React from 'react';
import View from './View';

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import React from 'react';
import View from './View';

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import React from 'react';
class View extends React.Component {

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import { withStyle } from 'styletron-react';
import View from './View';

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import { styled } from 'styletron-react';
const Dot = styled('div', ({ size, x, y, children, color }) => ({

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { Client as Styletron } from 'styletron-engine-atomic';
import { Provider as StyletronProvider } from 'styletron-react';

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import { styled } from 'styletron-react';
const View = styled('div', ({ style }) => ({

View File

@@ -1,6 +1,5 @@
import App from './app/App';
import impl from './impl';
import TextTree from './cases/TextTree';
import Tree from './cases/Tree';
import SierpinskiTriangle from './cases/SierpinskiTriangle';
@@ -51,13 +50,6 @@ const tests = {
},
Provider: components.Provider,
sampleCount: 100
})),
'Mount text tree': createTestBlock(components => ({
benchmarkType: 'mount',
Component: TextTree,
getComponentProps: () => ({ breadth: 6, components, depth: 3, id: 0, wrap: 2 }),
Provider: components.Provider,
sampleCount: 50
}))
};

View File

@@ -23,11 +23,7 @@ module.exports = {
'style-loader',
{
loader: 'css-loader',
options: {
modules: {
localIdentName: '[hash:base64:8]'
}
}
options: { modules: true, localIdentName: '[hash:base64:8]' }
}
]
},
@@ -38,7 +34,7 @@ module.exports = {
loader: 'babel-loader',
options: {
cacheDirectory: false,
presets: [babelPreset],
presets: babelPreset,
plugins: ['styled-jsx/babel']
}
}

View File

@@ -1,20 +0,0 @@
const path = require('path');
const webpack = require('webpack');
module.exports = async ({ config, mode }) => {
config.module.rules.push({
test: /\.(gif|jpe?g|png|svg)$/,
use: {
loader: 'url-loader',
options: { name: '[name].[ext]' }
}
});
config.resolve.extensions = ['.web.js', '.js', '.json', '.web.jsx', '.jsx'];
config.resolve.alias = {
'react-native': 'react-native-web'
};
return config;
};

View File

@@ -1 +0,0 @@
// import '@storybook/addon-options/register';

View File

@@ -1,48 +0,0 @@
import { create } from '@storybook/theming';
// import centered from './decorator-centered';
import { addParameters, configure, addDecorator } from '@storybook/react';
// Option defaults:
addParameters({
options: {
storySort: (a, b) => {
const sectionA = a[1].id.split('-')[0];
const sectionB = b[1].id.split('-')[0];
return sectionB.localeCompare(sectionA);
},
theme: create({
base: 'light',
brandTitle: 'React Native for Web',
brandUrl: 'https://necolas.github.io/react-native-web'
// To control appearance:
// brandImage: 'http://url.of/some.svg',
}),
/**
* regex for finding the hierarchy separator
* @example:
* null - turn off hierarchy
* /\// - split by `/`
* /\./ - split by `.`
* /\/|\./ - split by `/` or `.`
* @type {Regex}
*/
hierarchySeparator: /\/|\./,
/**
* regex for finding the hierarchy root separator
* @example:
* null - turn off multiple hierarchy roots
* /\|/ - split by `|`
* @type {Regex}
*/
hierarchyRootSeparator: /\|/,
panelPosition: 'bottom'
}
});
// addDecorator(centered);
const context = require.context('../src', true, /\.stories\.(js|mdx)$/);
configure(context, module);

View File

@@ -1,10 +0,0 @@
module.exports = [
{
name: '@storybook/addon-docs/preset',
options: {
configureJSX: true,
babelOptions: {},
sourceLoaderOptions: null
}
}
];

View File

@@ -1,7 +0,0 @@
module.exports = function(api) {
api.cache(true);
return {
presets: ['module:metro-react-native-babel-preset'],
plugins: ['react-native-web']
};
};

View File

@@ -1,23 +0,0 @@
{
"private": true,
"name": "docs",
"version": "0.14.7",
"scripts": {
"build": "build-storybook --docs -o ./dist -c ./.storybook",
"start": "start-storybook --docs -p 9001 -c ./.storybook",
"release": "yarn build && git checkout gh-pages && rm -rf ../../docs && mv dist ../../docs && git add -A && git commit -m \"Deploy documentation\" && git push origin gh-pages && git checkout -"
},
"dependencies": {
"@storybook/addon-docs": "^5.3.9",
"@storybook/addon-options": "^5.3.9",
"@storybook/cli": "^5.3.9",
"@storybook/react": "^5.3.9",
"@storybook/theming": "^5.3.9",
"react-native-web": "0.14.7"
},
"devDependencies": {
"babel-plugin-react-native-web": "0.14.7",
"url-loader": "^3.0.0",
"webpack": "^4.41.5"
}
}

View File

@@ -1,109 +0,0 @@
import { Meta, Props } from '@storybook/addon-docs/blocks';
<Meta title="APIs|AppRegistry" />
# AppRegistry
AppRegistry is the control point for registering, running, prerendering, and
unmounting all apps. App root components should register themselves with
`AppRegistry.registerComponent`. Apps can be run by invoking
`AppRegistry.runApplication`.
## Methods
### getAppKeys()
Returns an array of all registered app keys
```js
const appKeys = AppRegistry.getAppKeys();
```
### getApplication(appKey, appParams)
A web-only method for server-side rendering to HTML and CSS. It returns an
object containing the given application's element and a function to get styles
once the element is rendered.
Additional props can be passed to the `getStyleElement` function, e.g., your CSP
policy may require a `nonce` to be set on style elements.
```js
const appKey = 'MyApp';
const appParams = { ... };
const { element, getStyleElement } = AppRegistry.getApplication(appKey, appParams);
```
### registerComponent(appKey, getComponent)
Register a component provider under the given appKey.
```js
const appKey = 'MyApp';
const getComponent = () => App;
AppRegistry.registerComponent(appKey, getComponent)
```
### registerConfig(config)
Register multiple applications. AppConfig type is:
```js
type AppConfig = {
appKey: string;
component: ComponentProvider;
run?: function
}
const config = [{
appKey: 'FirstApp',
component: () => FirstApp
}, {
appKey: 'SecondApp',
component: () => SecondApp
}];
AppRegistry.registerConfig(config)
```
### registerRunnable(appKey, run)
Register a custom render function for an application. The function will receive
the `appParameters` passed to `runApplication`.
```js
AppRegistry.registerRunnable('MyApp', (appParams) => { ... });
```
### runApplication(appKey, appParams)
Runs the application that was registered under `appKey`. The `appParameters`
must include the `rootTag` into which the application is rendered, and
optionally any `initialProps` or render callback. If the client should hydrate
server-rendered HTML, set `hydrate` to `true`.
```js
AppRegistry.runApplication('MyApp', {
callback: () => { console.log('React rendering has finished') },
hydrate: true,
initialProps: {},
rootTag: document.getElementById('react-root'),
})
```
### setComponentProviderInstrumentationHook(componentProvider)
```js
type setComponentProviderInstrumentationHook = (componentProvider: func) => Component;
```
### setWrapperComponentProvider(appParams)
```js
type setWrapperComponentProvider = (appParameters: object) => Component;
```
### unmountApplicationComponentAtRootTag(rootTag)
To "stop" an application when a view should be destroyed, call
`AppRegistry.unmountApplicationComponentAtRootTag` with the `rootTag` that was passed
into `runApplication`.

View File

@@ -1,6 +0,0 @@
export default {
title: 'APIs|AppState',
includeStories: []
};
export { default as stateChanges } from './examples/StateChanges';

View File

@@ -1,46 +0,0 @@
import { Meta, Props, Preview, Story } from '@storybook/addon-docs/blocks';
import * as Stories from './AppState.stories.js';
<Meta title="APIs|AppState" />
# AppState
AppState can tell you if the app is in the foreground or background, and notify
you when the state changes. States: `active` (the app is running in the
foreground), `background` (the app is running in the background, i.e., the user
has not focused the app's tab).
## Properties
### isAvailable
Determines whether the browser environment supports `AppState`.
### currentState
Returns the current state of the app: "active" or "background".
## Methods
### addEventListener(type, handler)
Add a handler to AppState changes by listening to the `change` event type and
providing the handler. The handler is called with the app state value.
```js
AppState.addEventListener('change', (currentState) => {});
```
### removeEventListener(type, handler)
Remove a handler by passing the `change` event type and the handler.
AppState.removeEventListener('change', handler);
## Example
<Preview withSource='none'>
<Story name="stateChanges">
<Stories.stateChanges />
</Story>
</Preview>

View File

@@ -1,32 +0,0 @@
import React from 'react';
import { AppState, Text, View } from 'react-native';
export default function StateChanges() {
const [state, updateState] = React.useState({
active: 0,
background: 0,
currentState: AppState.currentState
});
React.useEffect(() => {
const handleChange = nextState => {
updateState(previousState => ({
...previousState,
[nextState]: previousState[nextState] + 1
}));
};
AppState.addEventListener('change', handleChange);
return () => {
AppState.removeEventListener('change', handleChange);
};
}, []);
return (
<View>
<Text>Active count: {state.active}</Text>
<Text>Background count: {state.background}</Text>
<Text>Current state is: {state.currentState}</Text>
</View>
);
}

View File

@@ -1,63 +0,0 @@
import { Meta, Preview } from '@storybook/addon-docs/blocks';
import * as Stories from './examples';
<Meta title="APIs|Appearance" />
# Appearance
The Appearance module exposes information about the user's appearance
preferences, such as their preferred color scheme (light or dark). In
`react-native-web` this is achieved using the `prefers-color-scheme` media query.
## Methods
### getColorScheme()
You can use the Appearance module to determine if the user prefers a dark color
scheme:
```js
const colorScheme = Appearance.getColorScheme();
if (colorScheme === 'dark') {
// Use dark color scheme
}
```
Although the color scheme is available immediately, this may change (e.g.
scheduled color scheme change at sunrise or sunset). Any rendering logic or
styles that depend on the user preferred color scheme should try to call this
function on every render, rather than caching the value.
## Hooks
### useColorScheme
The `useColorScheme` React hook provides and subscribes to color scheme updates
from the Appearance module. The return value indicates the current user
preferred color scheme. The value may be updated later, either through direct
user action (e.g. theme selection in device settings) or on a schedule (e.g.
light and dark themes that follow the day/night cycle).
Supported color schemes:
- `'light'`: The user prefers a light color theme.
- `'dark'`: The user prefers a dark color theme.
- `null`: The user has not indicated a preferred color theme.
```js
import * as React from 'react';
import { Text, useColorScheme } from 'react-native';
const MyComponent = () => {
const colorScheme = useColorScheme();
return <Text>Your color scheme is: {colorScheme}</Text>;
};
```
This produces:
<Preview withSource="none">
<Story name="colorScheme">
<Stories.colorSchemeText />
</Story>
</Preview>

View File

@@ -1,7 +0,0 @@
import * as React from 'react';
import { Text, useColorScheme } from 'react-native';
export default function ColorSchemeText() {
const colorScheme = useColorScheme();
return <Text>Your color scheme is: {colorScheme}</Text>;
}

View File

@@ -1 +0,0 @@
export { default as colorSchemeText } from './ColorSchemeText';

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