mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-04-10 10:01:12 +08:00
Compare commits
84 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4af1eb981 | ||
|
|
405a3b79e8 | ||
|
|
afd5293172 | ||
|
|
e4831b7bd8 | ||
|
|
baffc9a9e6 | ||
|
|
4151b47005 | ||
|
|
96ec805f59 | ||
|
|
d2df2c296e | ||
|
|
af80b046fa | ||
|
|
1d01af57d0 | ||
|
|
e7613ca4d1 | ||
|
|
0e81c6ef27 | ||
|
|
7fa8940325 | ||
|
|
3e681bed3e | ||
|
|
ecd4b40c71 | ||
|
|
e7cb364b63 | ||
|
|
90bd23f783 | ||
|
|
47a281373a | ||
|
|
6a0302169c | ||
|
|
b195f2b1f5 | ||
|
|
7b81d2a7ec | ||
|
|
a1d8ea776e | ||
|
|
15b960f097 | ||
|
|
001be82178 | ||
|
|
5eeef9e3d2 | ||
|
|
6a310999d0 | ||
|
|
a9ad313a79 | ||
|
|
d0ca7585ab | ||
|
|
3f8624e25f | ||
|
|
8f0c39c2fe | ||
|
|
4f9216853b | ||
|
|
14f7dfd515 | ||
|
|
c336995952 | ||
|
|
2afa5a3cf7 | ||
|
|
9fb818cfd4 | ||
|
|
3153cd8213 | ||
|
|
026a92fd53 | ||
|
|
e0f010da47 | ||
|
|
b299eb6c59 | ||
|
|
da38e87b50 | ||
|
|
a40521f485 | ||
|
|
f62ed22a14 | ||
|
|
bfaca0557b | ||
|
|
c3eedabac4 | ||
|
|
cf43ffd700 | ||
|
|
a8e5d43db5 | ||
|
|
b4e3427fea | ||
|
|
a82cfbe504 | ||
|
|
0eae7bed2e | ||
|
|
b8f54f61f0 | ||
|
|
d5c6b98340 | ||
|
|
16b2fb9bd7 | ||
|
|
48e62fcd64 | ||
|
|
0816c40790 | ||
|
|
2756ab49c3 | ||
|
|
10407f3aa2 | ||
|
|
0ee3310290 | ||
|
|
19b356aaea | ||
|
|
ea744fe780 | ||
|
|
f254c8eae6 | ||
|
|
5fcb36fc21 | ||
|
|
377f23f725 | ||
|
|
fc0b81416a | ||
|
|
cb545b0dac | ||
|
|
f684a3656e | ||
|
|
45975d3a1e | ||
|
|
2d83ffbd6b | ||
|
|
64307c066a | ||
|
|
bb66639519 | ||
|
|
c00360491b | ||
|
|
4bc16fa3eb | ||
|
|
2237777341 | ||
|
|
6a2252891a | ||
|
|
206a236df2 | ||
|
|
ff5a928a50 | ||
|
|
61bf7e76b0 | ||
|
|
e3170623f1 | ||
|
|
392de22992 | ||
|
|
c84163d80e | ||
|
|
50442c4e7c | ||
|
|
eb0e0b8771 | ||
|
|
15d5e57e92 | ||
|
|
def873e9e3 | ||
|
|
9e9b40f155 |
@@ -2,3 +2,4 @@ coverage
|
||||
dist
|
||||
node_modules
|
||||
packages/**/vendor/*
|
||||
packages/examples
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
"document": false,
|
||||
"navigator": false,
|
||||
"window": false,
|
||||
// Flow global types
|
||||
// Flow global types,
|
||||
"$Enum": false,
|
||||
"HTMLInputElement": false,
|
||||
"ReactClass": false,
|
||||
"ReactComponent": false,
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
[ignore]
|
||||
<PROJECT_ROOT>/.*/__tests__/.*
|
||||
<PROJECT_ROOT>/packages/.*/dist/.*
|
||||
<PROJECT_ROOT>/website/.*
|
||||
<PROJECT_ROOT>/packages/examples/.*
|
||||
<PROJECT_ROOT>/packages/website/.*
|
||||
.*/node_modules/babel-plugin-transform-react-remove-prop-types/*
|
||||
|
||||
[include]
|
||||
|
||||
3
.github/CONTRIBUTING.md
vendored
3
.github/CONTRIBUTING.md
vendored
@@ -4,7 +4,8 @@
|
||||
|
||||
Before opening an issue, please search the [issue
|
||||
tracker](https://github.com/necolas/react-native-web/issues) to make sure your
|
||||
issue hasn't already been reported.
|
||||
issue hasn't already been reported. Please note that your issue may be closed
|
||||
if it doesn't include the information requested in the issue template.
|
||||
|
||||
## Getting started
|
||||
|
||||
|
||||
37
.github/ISSUE_TEMPLATE/bug.md
vendored
37
.github/ISSUE_TEMPLATE/bug.md
vendored
@@ -4,29 +4,46 @@ about: "If something isn't working as expected \U0001F914"
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
Thank you for reporting an issue. Please note that an issue must include the
|
||||
information that is marked as REQUIRED below, or it may be closed.
|
||||
-->
|
||||
|
||||
**The problem**
|
||||
A clear and concise description of the bug or problem.
|
||||
<!--
|
||||
REQUIRED: A clear and concise description of the bug or problem.
|
||||
-->
|
||||
|
||||
**How to reproduce**
|
||||
Test case: <!-- FORK THIS TEMPLATE: https://codesandbox.io/s/6lx6ql1w5r -->
|
||||
<!--
|
||||
REQUIRED: Create a test case by forking this template https://codesandbox.io/s/6lx6ql1w5r
|
||||
|
||||
Failing to include a reduced test case may result in the issue being closed,
|
||||
and will delay any potential fix. Your application or GitHub project is NOT
|
||||
considered a reduced test case. If the issue only affects certain browsers,
|
||||
providing screenshots is also helpful.
|
||||
-->
|
||||
Simplified test case: <!-- add link here -->
|
||||
|
||||
Steps to reproduce:
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
<!-- If applicable, add screenshots to help explain your problem. -->
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
<!--
|
||||
REQUIRED: A clear and concise description of what you expected to happen.
|
||||
-->
|
||||
|
||||
**Environment (include versions). Did this work in previous versions?**
|
||||
|
||||
* OS:
|
||||
* Device:
|
||||
* Browser:
|
||||
* React Native for Web (version):
|
||||
* React (version):
|
||||
* React Native for Web (version): TBC
|
||||
* React (version): TBC
|
||||
* Browser: TBC
|
||||
|
||||
<!--
|
||||
OPTIONAL:
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
-->
|
||||
|
||||
8
.github/ISSUE_TEMPLATE/feature.md
vendored
8
.github/ISSUE_TEMPLATE/feature.md
vendored
@@ -5,13 +5,13 @@ about: If you have a suggestion
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is, e.g., I have an issue when [...]
|
||||
<!-- A clear and concise description of what the problem is, e.g., I have an issue when [...] -->
|
||||
|
||||
**Describe a solution you'd like**
|
||||
A clear and concise description of what you want to happen. Add any considered drawbacks.
|
||||
<!-- A clear and concise description of what you want to happen. Add any considered drawbacks. -->
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
<!-- Add any other context or screenshots about the feature request here. -->
|
||||
|
||||
5
.prettierignore
Normal file
5
.prettierignore
Normal file
@@ -0,0 +1,5 @@
|
||||
coverage
|
||||
dist
|
||||
node_modules
|
||||
packages/**/vendor/*
|
||||
packages/examples
|
||||
150
README.md
150
README.md
@@ -2,41 +2,48 @@
|
||||
|
||||
[![npm version][package-badge]][package-url] [![Build Status][ci-badge]][ci-url] [](https://reactjs.org/docs/how-to-contribute.html#your-first-pull-request)
|
||||
|
||||
"React Native for Web" brings the platform-agnostic Components and APIs of
|
||||
[React Native][react-native-url] to the Web.
|
||||
**Compatibility: React Native 0.55**.
|
||||
|
||||
* **High-quality user interfaces**: React Native for Web makes it easy to
|
||||
"React Native for Web" makes it possible to run [React
|
||||
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),
|
||||
adaptive web UIs in JavaScript. It provides native-like interactions, support
|
||||
adaptive web UIs in JavaScript. It provides native-quality interactions, support
|
||||
for multiple input modes (touch, mouse, keyboard), optimized vendor-prefixed
|
||||
styles, built-in support for RTL layout, built-in accessibility, and integrates
|
||||
with React Dev Tools.
|
||||
|
||||
* **Write once, render anywhere**: React Native for Web interoperates with
|
||||
existing React DOM components and is compatible with the majority of the
|
||||
React Native API. You can develop new components for native and web without
|
||||
rewriting existing code. React Native for Web can also render to HTML and
|
||||
critical CSS on the server using Node.js.
|
||||
* **Write once, render anywhere**: interoperates with existing React DOM
|
||||
components and is compatible with the majority of the React Native API. You can
|
||||
develop new components for native and web without rewriting existing code.
|
||||
React Native for Web can also render to HTML and critical CSS on the server
|
||||
using Node.js.
|
||||
|
||||
Who is using React Native for Web? [Twitter](https://mobile.twitter.com),
|
||||
[Major League Soccer](https://matchcenter.mlssoccer.com),
|
||||
Who is using React Native in production web apps?
|
||||
[Twitter](https://mobile.twitter.com), [Major League
|
||||
Soccer](https://matchcenter.mlssoccer.com),
|
||||
[Flipkart](https://www.flipkart.com/), Playstation, Uber, [The
|
||||
Times](https://github.com/newsuk/times-components), [React Native's
|
||||
documentation](http://facebook.github.io/react-native/).
|
||||
Times](https://github.com/newsuk/times-components).
|
||||
|
||||
Browser support: Chrome, Firefox, Edge, Safari 7+, IE 10+.
|
||||
|
||||
## Quick start
|
||||
|
||||
The easiest way to get started with React Native for Web is to use this
|
||||
The easiest way to get started is to edit this
|
||||
[CodeSandbox](https://codesandbox.io/s/q4qymyp2l6) template (or
|
||||
[Glitch](https://glitch.com/edit/#!/react-native)). You don’t 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/packages/website/guides/getting-started.md)
|
||||
guide.
|
||||
|
||||
## Documentation
|
||||
|
||||
You can find the React Native for Web API documentation [on the
|
||||
website][website-url].
|
||||
You can find the API documentation [on the website][website-url].
|
||||
|
||||
Please refer to the [React Native documentation][react-native-url] for more
|
||||
design details, and for information about the [Gesture Responder
|
||||
@@ -45,18 +52,20 @@ and [animations](https://facebook.github.io/react-native/docs/animations.html).
|
||||
|
||||
### Guides
|
||||
|
||||
* [Getting started](https://github.com/necolas/react-native-web/blob/master/website/guides/getting-started.md)
|
||||
* [Style](https://github.com/necolas/react-native-web/blob/master/website/guides/style.md)
|
||||
* [Accessibility](https://github.com/necolas/react-native-web/blob/master/website/guides/accessibility.md)
|
||||
* [Internationalization](https://github.com/necolas/react-native-web/blob/master/website/guides/internationalization.md)
|
||||
* [Direct manipulation](https://github.com/necolas/react-native-web/blob/master/website/guides/direct-manipulation.md)
|
||||
* [Advanced use](https://github.com/necolas/react-native-web/blob/master/website/guides/advanced.md)
|
||||
* [Getting started](https://github.com/necolas/react-native-web/blob/master/packages/website/guides/getting-started.md)
|
||||
* [Style](https://github.com/necolas/react-native-web/blob/master/packages/website/guides/style.md)
|
||||
* [Accessibility](https://github.com/necolas/react-native-web/blob/master/packages/website/guides/accessibility.md)
|
||||
* [Internationalization](https://github.com/necolas/react-native-web/blob/master/packages/website/guides/internationalization.md)
|
||||
* [Direct manipulation](https://github.com/necolas/react-native-web/blob/master/packages/website/guides/direct-manipulation.md)
|
||||
* [Experimental / unstable use](https://github.com/necolas/react-native-web/blob/master/packages/website/guides/advanced.md)
|
||||
|
||||
## Examples
|
||||
|
||||
There are several examples [on the website][website-url] and in the [website's
|
||||
source code](https://github.com/necolas/react-native-web/blob/master/website).
|
||||
Here is an example to get you started:
|
||||
There are examples [on the website][website-url] ([source
|
||||
code](https://github.com/necolas/react-native-web/blob/master/packages/website).
|
||||
And all the [React Native examples][examples-url] ([source
|
||||
code](https://github.com/necolas/react-native-web/blob/master/packages/examples))
|
||||
are also available. Here is an example to get you started:
|
||||
|
||||
```js
|
||||
import React from 'react';
|
||||
@@ -95,9 +104,97 @@ Examples of using React Native for Web with other web tools:
|
||||
* [Next.js](https://github.com/zeit/next.js/tree/master/examples/with-react-native-web)
|
||||
* [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/website/storybook/.storybook)
|
||||
* [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)
|
||||
|
||||
Example recipes for web-specific UI patterns:
|
||||
|
||||
* [Links](https://codesandbox.io/s/53r88k5opx)
|
||||
* [Hover styles](https://codesandbox.io/s/o9q8vy70l5)
|
||||
* [Root element styles](https://codesandbox.io/s/52x1871vjl)
|
||||
|
||||
## Compatibility with React Native
|
||||
|
||||
React Native v0.55
|
||||
|
||||
### Components
|
||||
|
||||
| Name | Status | Notes |
|
||||
| :----------------------- | :------------------ | :---- |
|
||||
| ActivityIndicator | Available | |
|
||||
| ART | Available | |
|
||||
| Button | Available | |
|
||||
| CheckBox | Available | |
|
||||
| FlatList | Available | |
|
||||
| Image | Available (partial) | Missing multiple sources and HTTP headers. |
|
||||
| ImageBackground | Available | |
|
||||
| KeyboardAvoidingView | Available (mock) | |
|
||||
| ListView | Available | |
|
||||
| Modal | Not started | |
|
||||
| Picker | Available | |
|
||||
| RefreshControl | Not started | |
|
||||
| SafeAreaView | Available | |
|
||||
| ScrollView | Available (partial) | Missing momentum scroll events. |
|
||||
| SectionList | Available | |
|
||||
| Slider | Not started | |
|
||||
| StatusBar | Mock | |
|
||||
| SwipeableFlatList | Available | |
|
||||
| SwipeableListView | Available | |
|
||||
| Switch | Available | |
|
||||
| Text | Available (partial) | Missing `onLongPress` support. |
|
||||
| TextInput | Available (partial) | Missing rich text features and auto-expanding behaviour. |
|
||||
| Touchable | Available | Includes additional support for mouse and keyboard interactions. |
|
||||
| TouchableHighlight | Available | |
|
||||
| TouchableNativeFeedback | Not started | |
|
||||
| TouchableOpacity | Available | |
|
||||
| TouchableWithoutFeedback | Available | |
|
||||
| View | Available | |
|
||||
| VirtualizedList | Available | |
|
||||
| WebView | Not started | |
|
||||
| YellowBox | Mock | |
|
||||
|
||||
### Modules
|
||||
|
||||
| Name | Status | Notes |
|
||||
| :----------------------- | :------------------ | :---- |
|
||||
| AccessibilityInfo | Mock | No equivalent web APIs. |
|
||||
| Alert | Not started | |
|
||||
| Animated | Available | Missing `useNativeDriver` support. |
|
||||
| AppRegistry | Available | Includes additional support for SSR with `getApplication`. |
|
||||
| AppState | Available | |
|
||||
| AsyncStorage | Available | |
|
||||
| BackHandler | Mock | No equivalent web APIs. |
|
||||
| CameraRoll | Not started | No equivalent web APIs. |
|
||||
| Clipboard | Available | |
|
||||
| ColorPropType | Available | |
|
||||
| DeviceInfo | Available (partial) | |
|
||||
| Dimensions | Available | |
|
||||
| Easing | Available | |
|
||||
| EdgeInsetsPropType | Available | |
|
||||
| Geolocation | Available | |
|
||||
| I18nManager | Available | Includes additional support for runtime switch to RTL. |
|
||||
| ImageEditor | Not started | No equivalent web APIs. |
|
||||
| ImageStore | Not started | No equivalent web APIs. |
|
||||
| InteractionManager | Available (partial) | |
|
||||
| Keyboard | Mock | |
|
||||
| LayoutAnimation | Available (partial) | Missing transform to web animation. |
|
||||
| Linking | Available | |
|
||||
| NativeEventEmitter | Available | |
|
||||
| NativeMethodsMixin | Available | |
|
||||
| NativeModules | Available (partial) | Mocked. Missing ability to load native modules. |
|
||||
| NetInfo | Available (partial) | Missing functionality to detect extensive connections. |
|
||||
| PanResponder | Available | |
|
||||
| PixelRatio | Available | |
|
||||
| Platform | Available | |
|
||||
| PointPropType | Available | |
|
||||
| Settings | Not started | |
|
||||
| Share | Available | Only available over HTTPS. Read about the [Web Share API](https://wicg.github.io/web-share/). |
|
||||
| StyleSheet | Available | |
|
||||
| TextPropTypes | Available | |
|
||||
| UIManager | Available | |
|
||||
| Vibration | Available | |
|
||||
| ViewPropTypes | Available | |
|
||||
|
||||
## Contributing
|
||||
|
||||
The main purpose of this repository is to help evolve React web and native
|
||||
@@ -135,6 +232,7 @@ 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
|
||||
|
||||
43
package.json
43
package.json
@@ -1,25 +1,27 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "0.7.0",
|
||||
"version": "0.8.8",
|
||||
"name": "react-native-web-monorepo",
|
||||
"scripts": {
|
||||
"clean": "del ./packages/*/dist",
|
||||
"compile": "yarn clean && cd packages/react-native-web && babel src --out-dir dist --ignore \"**/__tests__\"",
|
||||
"compile:commonjs": "cd packages/react-native-web && MODULES=commonjs babel src --out-dir dist/cjs --ignore \"**/__tests__\"",
|
||||
"compile": "npm-run-all clean -p \"compile:* -- {@}\" --",
|
||||
"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",
|
||||
"website": "cd website && yarn start",
|
||||
"website:release": "cd website && 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": "find packages scripts types website -name '*.js' | grep -v -E '(node_modules|dist|vendor)' | xargs yarn fmt:cmd",
|
||||
"fmt:cmd": "prettier --write",
|
||||
"jest": "MODULES=commonjs jest --config ./scripts/jest/config.js",
|
||||
"fmt": "prettier --write \"**/*.js\"",
|
||||
"jest": "BABEL_ENV=commonjs jest --config ./scripts/jest/config.js",
|
||||
"lint": "yarn lint:check --fix",
|
||||
"lint:check": "eslint packages scripts website",
|
||||
"lint:check": "eslint packages scripts",
|
||||
"precommit": "lint-staged",
|
||||
"prerelease": "yarn test && yarn compile && yarn compile:commonjs",
|
||||
"release": "node ./scripts/release/publish.js",
|
||||
"postrelease": "yarn website:release && yarn benchmarks:release",
|
||||
"postrelease": "yarn benchmarks:release && yarn examples:release && yarn website:release",
|
||||
"test": "yarn flow && yarn lint:check && yarn jest --runInBand"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -27,6 +29,7 @@
|
||||
"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",
|
||||
@@ -49,24 +52,26 @@
|
||||
"jest": "^22.4.3",
|
||||
"jest-canvas-mock": "^1.0.2",
|
||||
"lint-staged": "^7.1.0",
|
||||
"npm-run-all": "^4.1.3",
|
||||
"prettier": "^1.12.1",
|
||||
"react": "^16.3.2",
|
||||
"react-art": "^16.3.2",
|
||||
"react-dom": "^16.3.2",
|
||||
"react-test-renderer": "^16.3.2"
|
||||
"react": "^16.4.1",
|
||||
"react-art": "^16.4.1",
|
||||
"react-dom": "^16.4.1",
|
||||
"react-test-renderer": "^16.4.1"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
"website"
|
||||
"packages/*"
|
||||
],
|
||||
"lint-staged": {
|
||||
"packages/react-native-web/src/index.js": [
|
||||
"node ./scripts/babel/createModuleMap.js",
|
||||
"prettier --write ./packages/babel-plugin-react-native-web/src/moduleMap.js",
|
||||
"git add ./packages/babel-plugin-react-native-web/src/moduleMap.js"
|
||||
],
|
||||
"**/*.js": [
|
||||
"prettier --write",
|
||||
"git update-index --again",
|
||||
"eslint"
|
||||
],
|
||||
"packages/react-native-web/src/index.js": [
|
||||
"node ./scripts/babel/createModuleMap.js"
|
||||
]
|
||||
},
|
||||
"prettier": {
|
||||
|
||||
@@ -17,10 +17,18 @@ yarn add --dev babel-plugin-react-native-web
|
||||
|
||||
```
|
||||
{
|
||||
"plugins": ["react-native-web"]
|
||||
"plugins": [
|
||||
["react-native-web", { commonjs: true }]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
You should configure the plugin to match the module format used by your
|
||||
bundler. Most modern bundlers will use a package's ES modules by default (i.e.,
|
||||
if `package.json` has a `module` field). But if you need the plugin to rewrite
|
||||
import paths to point to CommonJS modules, you must set the `commonjs` option
|
||||
to `true`.
|
||||
|
||||
## Example
|
||||
|
||||
NOTE: `react-native-web` internal paths are _not stable_ and you must not rely
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "babel-plugin-react-native-web",
|
||||
"version": "0.7.0",
|
||||
"version": "0.8.8",
|
||||
"description": "Babel plugin for React Native for Web",
|
||||
"main": "index.js",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -48,6 +48,24 @@ import * as ReactNativeModules from 'react-native-web/dist/index';
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Rewrite react-native to react-native-web import from "native-native": import from "native-native" 2`] = `
|
||||
"
|
||||
import ReactNative from 'react-native';
|
||||
import { View } from 'react-native';
|
||||
import { Invalid, View as MyView, ViewPropTypes } from 'react-native';
|
||||
import * as ReactNativeModules from 'react-native';
|
||||
|
||||
↓ ↓ ↓ ↓ ↓ ↓
|
||||
|
||||
import ReactNative from 'react-native-web/dist/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 { createElement } from 'react-native-web';
|
||||
@@ -70,7 +88,7 @@ exports[`Rewrite react-native to react-native-web require "react-native": requir
|
||||
"
|
||||
const ReactNative = require('react-native');
|
||||
const { View } = require('react-native');
|
||||
const { StyleSheet, TouchableOpacity } = require('react-native');
|
||||
const { StyleSheet, TouchableOpacity } = require('react-native');
|
||||
|
||||
↓ ↓ ↓ ↓ ↓ ↓
|
||||
|
||||
@@ -84,6 +102,24 @@ const TouchableOpacity = require('react-native-web/dist/exports/TouchableOpacity
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Rewrite react-native to react-native-web require "react-native": require "react-native" 2`] = `
|
||||
"
|
||||
const ReactNative = require('react-native');
|
||||
const { View } = require('react-native');
|
||||
const { StyleSheet, TouchableOpacity } = require('react-native');
|
||||
|
||||
↓ ↓ ↓ ↓ ↓ ↓
|
||||
|
||||
const ReactNative = require('react-native-web/dist/cjs/index').default;
|
||||
|
||||
const View = require('react-native-web/dist/cjs/exports/View').default;
|
||||
|
||||
const StyleSheet = require('react-native-web/dist/cjs/exports/StyleSheet').default;
|
||||
|
||||
const TouchableOpacity = require('react-native-web/dist/cjs/exports/TouchableOpacity').default;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Rewrite react-native to react-native-web require "react-native-web": require "react-native-web" 1`] = `
|
||||
"
|
||||
const ReactNative = require('react-native-web');
|
||||
|
||||
@@ -11,6 +11,15 @@ import { Invalid, View as MyView, ViewPropTypes } from 'react-native';
|
||||
import * as ReactNativeModules from 'react-native';`,
|
||||
snapshot: true
|
||||
},
|
||||
{
|
||||
title: 'import from "native-native"',
|
||||
code: `import ReactNative from 'react-native';
|
||||
import { View } from 'react-native';
|
||||
import { Invalid, View as MyView, ViewPropTypes } from 'react-native';
|
||||
import * as ReactNativeModules from 'react-native';`,
|
||||
snapshot: true,
|
||||
pluginOptions: { commonjs: true }
|
||||
},
|
||||
{
|
||||
title: 'import from "react-native-web"',
|
||||
code: `import { createElement } from 'react-native-web';
|
||||
@@ -34,9 +43,17 @@ export { ColorPropType, StyleSheet, Text, createElement } from 'react-native-web
|
||||
title: 'require "react-native"',
|
||||
code: `const ReactNative = require('react-native');
|
||||
const { View } = require('react-native');
|
||||
const { StyleSheet, TouchableOpacity } = require('react-native');`,
|
||||
const { StyleSheet, TouchableOpacity } = require('react-native');`,
|
||||
snapshot: true
|
||||
},
|
||||
{
|
||||
title: 'require "react-native"',
|
||||
code: `const ReactNative = require('react-native');
|
||||
const { View } = require('react-native');
|
||||
const { StyleSheet, TouchableOpacity } = require('react-native');`,
|
||||
snapshot: true,
|
||||
pluginOptions: { commonjs: true }
|
||||
},
|
||||
{
|
||||
title: 'require "react-native-web"',
|
||||
code: `const ReactNative = require('react-native-web');
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
const moduleMap = require('./moduleMap');
|
||||
|
||||
const getDistLocation = importName =>
|
||||
importName && moduleMap[importName] ? `react-native-web/dist/exports/${importName}` : undefined;
|
||||
const isCommonJS = opts => opts.commonjs === true;
|
||||
|
||||
const getDistLocation = (importName, opts) => {
|
||||
const format = isCommonJS(opts) ? 'cjs/' : '';
|
||||
if (importName === 'index') {
|
||||
return `react-native-web/dist/${format}index`;
|
||||
} else if (importName && moduleMap[importName]) {
|
||||
return `react-native-web/dist/${format}exports/${importName}`;
|
||||
}
|
||||
};
|
||||
|
||||
const isReactNativeRequire = (t, node) => {
|
||||
const { declarations } = node;
|
||||
@@ -35,7 +43,7 @@ module.exports = function({ types: t }) {
|
||||
.map(specifier => {
|
||||
if (t.isImportSpecifier(specifier)) {
|
||||
const importName = specifier.imported.name;
|
||||
const distLocation = getDistLocation(importName);
|
||||
const distLocation = getDistLocation(importName, state.opts);
|
||||
|
||||
if (distLocation) {
|
||||
return t.importDeclaration(
|
||||
@@ -46,7 +54,7 @@ module.exports = function({ types: t }) {
|
||||
}
|
||||
return t.importDeclaration(
|
||||
[specifier],
|
||||
t.stringLiteral('react-native-web/dist/index')
|
||||
t.stringLiteral(getDistLocation('index', state.opts))
|
||||
);
|
||||
})
|
||||
.filter(Boolean);
|
||||
@@ -62,7 +70,7 @@ module.exports = function({ types: t }) {
|
||||
if (t.isExportSpecifier(specifier)) {
|
||||
const exportName = specifier.exported.name;
|
||||
const localName = specifier.local.name;
|
||||
const distLocation = getDistLocation(localName);
|
||||
const distLocation = getDistLocation(localName, state.opts);
|
||||
|
||||
if (distLocation) {
|
||||
return t.exportNamedDeclaration(
|
||||
@@ -75,7 +83,7 @@ module.exports = function({ types: t }) {
|
||||
return t.exportNamedDeclaration(
|
||||
null,
|
||||
[specifier],
|
||||
t.stringLiteral('react-native-web/dist/index')
|
||||
t.stringLiteral(getDistLocation('index', state.opts))
|
||||
);
|
||||
})
|
||||
.filter(Boolean);
|
||||
@@ -89,7 +97,7 @@ module.exports = function({ types: t }) {
|
||||
if (t.isObjectPattern(id)) {
|
||||
const imports = id.properties
|
||||
.map(identifier => {
|
||||
const distLocation = getDistLocation(identifier.key.name);
|
||||
const distLocation = getDistLocation(identifier.key.name, state.opts);
|
||||
if (distLocation) {
|
||||
return t.variableDeclaration(path.node.kind, [
|
||||
t.variableDeclarator(
|
||||
@@ -112,7 +120,7 @@ module.exports = function({ types: t }) {
|
||||
t.identifier(name),
|
||||
t.memberExpression(
|
||||
t.callExpression(t.identifier('require'), [
|
||||
t.stringLiteral('react-native-web/dist/index')
|
||||
t.stringLiteral(getDistLocation('index', state.opts))
|
||||
]),
|
||||
t.identifier('default')
|
||||
)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
|
||||
module.exports = {
|
||||
ART: true,
|
||||
AccessibilityInfo: true,
|
||||
ActivityIndicator: true,
|
||||
Alert: true,
|
||||
Animated: true,
|
||||
AppRegistry: true,
|
||||
AppState: true,
|
||||
@@ -11,6 +13,7 @@ module.exports = {
|
||||
CheckBox: true,
|
||||
Clipboard: true,
|
||||
ColorPropType: true,
|
||||
DeviceInfo: true,
|
||||
Dimensions: true,
|
||||
Easing: true,
|
||||
EdgeInsetsPropType: true,
|
||||
@@ -21,9 +24,11 @@ module.exports = {
|
||||
InteractionManager: true,
|
||||
Keyboard: true,
|
||||
KeyboardAvoidingView: true,
|
||||
LayoutAnimation: true,
|
||||
Linking: true,
|
||||
ListView: true,
|
||||
Modal: true,
|
||||
NativeEventEmitter: true,
|
||||
NativeModules: true,
|
||||
NetInfo: true,
|
||||
PanResponder: true,
|
||||
@@ -36,9 +41,12 @@ module.exports = {
|
||||
SafeAreaView: true,
|
||||
ScrollView: true,
|
||||
SectionList: true,
|
||||
Share: true,
|
||||
Slider: true,
|
||||
StatusBar: true,
|
||||
StyleSheet: true,
|
||||
SwipeableFlatList: true,
|
||||
SwipeableListView: true,
|
||||
Switch: true,
|
||||
Text: true,
|
||||
TextInput: true,
|
||||
@@ -53,6 +61,7 @@ module.exports = {
|
||||
View: true,
|
||||
ViewPropTypes: true,
|
||||
VirtualizedList: true,
|
||||
YellowBox: true,
|
||||
createElement: true,
|
||||
findNodeHandle: true,
|
||||
processColor: true,
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "benchmarks",
|
||||
"version": "0.7.0",
|
||||
"version": "0.8.8",
|
||||
"scripts": {
|
||||
"build": "mkdir -p dist && cp -f index.html dist/index.html && ./node_modules/.bin/webpack-cli --config ./webpack.config.js",
|
||||
"release": "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.2.0",
|
||||
"classnames": "^2.2.5",
|
||||
"d3-scale-chromatic": "^1.2.0",
|
||||
"emotion": "^9.1.3",
|
||||
"fela": "^6.1.7",
|
||||
"aphrodite": "^2.2.2",
|
||||
"classnames": "^2.2.6",
|
||||
"d3-scale-chromatic": "^1.3.0",
|
||||
"emotion": "^9.2.4",
|
||||
"fela": "^6.1.9",
|
||||
"glamor": "2.20.40",
|
||||
"radium": "^0.24.0",
|
||||
"react": "^16.3.2",
|
||||
"react-dom": "^16.3.2",
|
||||
"react-fela": "^7.2.0",
|
||||
"react-jss": "^8.4.0",
|
||||
"react-native-web": "0.7.0",
|
||||
"reactxp": "^1.1.1",
|
||||
"styled-components": "^3.2.6",
|
||||
"styled-jsx": "^2.2.6",
|
||||
"react": "^16.4.1",
|
||||
"react-dom": "^16.4.1",
|
||||
"react-fela": "^7.3.1",
|
||||
"react-jss": "^8.6.1",
|
||||
"react-native-web": "0.8.8",
|
||||
"reactxp": "^1.3.0",
|
||||
"styled-components": "^3.3.3",
|
||||
"styled-jsx": "^2.2.7",
|
||||
"styletron-engine-atomic": "^1.0.5",
|
||||
"styletron-react": "^4.2.1"
|
||||
"styletron-react": "^4.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-plugin-react-native-web": "0.7.0",
|
||||
"css-loader": "^0.28.11",
|
||||
"babel-plugin-react-native-web": "0.8.8",
|
||||
"css-loader": "^1.0.0",
|
||||
"style-loader": "^0.21.0",
|
||||
"url-loader": "^1.0.1",
|
||||
"webpack": "^4.8.1",
|
||||
"webpack-bundle-analyzer": "^2.11.1",
|
||||
"webpack-cli": "^2.1.3"
|
||||
"webpack": "^4.15.1",
|
||||
"webpack-bundle-analyzer": "^2.13.1",
|
||||
"webpack-cli": "^3.0.8"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ module.exports = {
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: { module: true, localIdentName: '[hash:base64:8]' }
|
||||
options: { modules: true, localIdentName: '[hash:base64:8]' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
34
packages/examples/README.md
Normal file
34
packages/examples/README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# examples
|
||||
|
||||
These examples are a clone of React Native's
|
||||
[RNTester](https://github.com/facebook/react-native/tree/v0.55.4/RNTester).
|
||||
RNTester showcases React Native views and modules. It's used to manually verify
|
||||
the appearance and behavior of React Native for Web.
|
||||
|
||||
Try the [examples app](https://necolas.github.io/react-native-web/examples) online.
|
||||
|
||||
To run the examples locally:
|
||||
|
||||
```
|
||||
yarn examples
|
||||
open ./packages/examples/dist/index.html
|
||||
```
|
||||
|
||||
Develop against these examples:
|
||||
|
||||
```
|
||||
yarn compile --watch
|
||||
NODE_ENV=development yarn examples --watch
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
The RNTester examples rely on features specific to [React Native's
|
||||
packager](https://github.com/facebook/metro) such as loading resolution
|
||||
variants of images, Babel transforms, compiling ES module dependencies, and
|
||||
Haste – Facebook's module system. These features are *emulated* in this
|
||||
package's `webpack.config.js`.
|
||||
|
||||
The commonjs export in `react-native-web` is also used to ensure that webpack
|
||||
can load the aliased modules correctly (as compiled ES modules are only
|
||||
available from the `.default` property of a module.)
|
||||
2
packages/examples/SHA
Normal file
2
packages/examples/SHA
Normal file
@@ -0,0 +1,2 @@
|
||||
facebook/react-native@v0.55.4
|
||||
facebook/react-native@370bcffba748e895ad8afa825bfef40bff859c95
|
||||
23
packages/examples/package.json
Normal file
23
packages/examples/package.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "react-native-examples",
|
||||
"version": "0.8.8",
|
||||
"scripts": {
|
||||
"build": "mkdir -p dist && cp -f src/index.html dist/index.html && ./node_modules/.bin/webpack-cli --config ./webpack.config.js",
|
||||
"release": "yarn build && git checkout gh-pages && rm -rf ../../examples && mv dist ../../examples && git add -A && git commit -m \"Examples deploy\" && git push origin gh-pages && git checkout -"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.26.0",
|
||||
"react": "^16.4.1",
|
||||
"react-dom": "^16.4.1",
|
||||
"react-native-web": "0.8.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-plugin-react-native-web": "0.8.8",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"file-loader": "^1.1.11",
|
||||
"webpack": "^4.8.1",
|
||||
"webpack-bundle-analyzer": "^2.11.1",
|
||||
"webpack-cli": "^2.1.3"
|
||||
}
|
||||
}
|
||||
90
packages/examples/src/RNTester/ARTExample.js
Normal file
90
packages/examples/src/RNTester/ARTExample.js
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule ARTExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
ART,
|
||||
Platform,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
const {
|
||||
Surface,
|
||||
Path,
|
||||
Group,
|
||||
Transform,
|
||||
Shape,
|
||||
} = ART;
|
||||
|
||||
|
||||
var scale = Platform.isTVOS ? 4 : 1;
|
||||
|
||||
class ARTExample extends React.Component<{}> {
|
||||
render(){
|
||||
const pathRect = new Path()
|
||||
.moveTo(scale * 0,scale * 0)
|
||||
.lineTo(scale * 0,scale * 110)
|
||||
.lineTo(scale * 110,scale * 110)
|
||||
.lineTo(scale * 110,scale * 0)
|
||||
.close();
|
||||
|
||||
const pathCircle0 = new Path()
|
||||
.moveTo(scale * 30,scale * 5)
|
||||
.arc(scale * 0,scale * 50,scale * 25)
|
||||
.arc(scale * 0,-scale * 50,scale * 25)
|
||||
.close();
|
||||
|
||||
const pathCircle1 = new Path()
|
||||
.moveTo(scale * 30,scale * 55)
|
||||
.arc(scale * 0,scale * 50,scale * 25)
|
||||
.arc(scale * 0,-scale * 50,scale * 25)
|
||||
.close();
|
||||
|
||||
const pathCircle2 = new Path()
|
||||
.moveTo(scale * 55,scale * 30)
|
||||
.arc(scale * 50,scale * 0,scale * 25)
|
||||
.arc(-scale * 50,scale * 0,scale * 25)
|
||||
.close();
|
||||
|
||||
const pathCircle3 = new Path()
|
||||
.moveTo(scale * 55,scale * 80)
|
||||
.arc(scale * 50,scale * 0,scale * 25)
|
||||
.arc(-scale * 50,scale * 0,scale * 25)
|
||||
.close();
|
||||
|
||||
return (
|
||||
<View>
|
||||
<Surface width={scale * 200} height={scale * 200}>
|
||||
<Group>
|
||||
<Shape d={pathRect} stroke="#000080" fill="#000080" strokeWidth={scale}/>
|
||||
<Shape d={pathCircle0} stroke="#FF0000" fill="#FF0000" strokeWidth={scale}/>
|
||||
<Shape d={pathCircle1} stroke="#00FF00" fill="#00FF00" strokeWidth={scale}/>
|
||||
<Shape d={pathCircle2} stroke="#00FFFF" fill="#00FFFF" strokeWidth={scale}/>
|
||||
<Shape d={pathCircle3} stroke="#FFFFFF" fill="#FFFFFF" strokeWidth={scale}/>
|
||||
</Group>
|
||||
</Surface>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
exports.title = '<ART>';
|
||||
exports.displayName = 'ARTExample';
|
||||
exports.description = 'ART input for numeric values';
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'ART Example',
|
||||
render(): React.Element<any> {
|
||||
return <ARTExample />;
|
||||
}
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,236 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule AccessibilityAndroidExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
AccessibilityInfo,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
ToastAndroid,
|
||||
TouchableWithoutFeedback,
|
||||
} = ReactNative;
|
||||
|
||||
var RNTesterBlock = require('./RNTesterBlock');
|
||||
var RNTesterPage = require('./RNTesterPage');
|
||||
|
||||
var importantForAccessibilityValues = ['auto', 'yes', 'no', 'no-hide-descendants'];
|
||||
|
||||
class AccessibilityAndroidExample extends React.Component {
|
||||
static title = 'Accessibility';
|
||||
static description = 'Examples of using Accessibility API.';
|
||||
|
||||
state = {
|
||||
count: 0,
|
||||
backgroundImportantForAcc: 0,
|
||||
forgroundImportantForAcc: 0,
|
||||
screenReaderEnabled: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
AccessibilityInfo.addEventListener(
|
||||
'change',
|
||||
this._handleScreenReaderToggled
|
||||
);
|
||||
AccessibilityInfo.fetch().done((isEnabled) => {
|
||||
this.setState({
|
||||
screenReaderEnabled: isEnabled
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
AccessibilityInfo.removeEventListener(
|
||||
'change',
|
||||
this._handleScreenReaderToggled
|
||||
);
|
||||
}
|
||||
|
||||
_handleScreenReaderToggled = (isEnabled) => {
|
||||
this.setState({
|
||||
screenReaderEnabled: isEnabled,
|
||||
});
|
||||
}
|
||||
|
||||
_addOne = () => {
|
||||
this.setState({
|
||||
count: ++this.state.count,
|
||||
});
|
||||
};
|
||||
|
||||
_changeBackgroundImportantForAcc = () => {
|
||||
this.setState({
|
||||
backgroundImportantForAcc: (this.state.backgroundImportantForAcc + 1) % 4,
|
||||
});
|
||||
};
|
||||
|
||||
_changeForgroundImportantForAcc = () => {
|
||||
this.setState({
|
||||
forgroundImportantForAcc: (this.state.forgroundImportantForAcc + 1) % 4,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<RNTesterPage title={'Accessibility'}>
|
||||
|
||||
<RNTesterBlock title="Nonaccessible view with TextViews">
|
||||
<View>
|
||||
<Text style={{color: 'green',}}>
|
||||
This is
|
||||
</Text>
|
||||
<Text style={{color: 'blue'}}>
|
||||
nontouchable normal view.
|
||||
</Text>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
|
||||
<RNTesterBlock title="Accessible view with TextViews wihout label">
|
||||
<View accessible={true}>
|
||||
<Text style={{color: 'green',}}>
|
||||
This is
|
||||
</Text>
|
||||
<Text style={{color: 'blue'}}>
|
||||
nontouchable accessible view without label.
|
||||
</Text>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
|
||||
<RNTesterBlock title="Accessible view with TextViews with label">
|
||||
<View accessible={true}
|
||||
accessibilityLabel="I have label, so I read it instead of embedded text.">
|
||||
<Text style={{color: 'green',}}>
|
||||
This is
|
||||
</Text>
|
||||
<Text style={{color: 'blue'}}>
|
||||
nontouchable accessible view with label.
|
||||
</Text>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
|
||||
<RNTesterBlock title="Touchable with component type = button">
|
||||
<TouchableWithoutFeedback
|
||||
onPress={() => ToastAndroid.show('Toasts work by default', ToastAndroid.SHORT)}
|
||||
accessibilityComponentType="button">
|
||||
<View style={styles.embedded}>
|
||||
<Text>Click me</Text>
|
||||
<Text>Or not</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
</RNTesterBlock>
|
||||
|
||||
<RNTesterBlock title="LiveRegion">
|
||||
<TouchableWithoutFeedback onPress={this._addOne}>
|
||||
<View style={styles.embedded}>
|
||||
<Text>Click me</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
<Text accessibilityLiveRegion="polite">
|
||||
Clicked {this.state.count} times
|
||||
</Text>
|
||||
</RNTesterBlock>
|
||||
|
||||
<RNTesterBlock title="Check if the screen reader is enabled">
|
||||
<Text>
|
||||
The screen reader is {this.state.screenReaderEnabled ? 'enabled' : 'disabled'}.
|
||||
</Text>
|
||||
</RNTesterBlock>
|
||||
|
||||
<RNTesterBlock title="Overlapping views and importantForAccessibility property">
|
||||
<View style={styles.container}>
|
||||
<View
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: 10,
|
||||
top: 10,
|
||||
right: 10,
|
||||
height: 100,
|
||||
backgroundColor: 'green'}}
|
||||
accessible={true}
|
||||
accessibilityLabel="First layout"
|
||||
importantForAccessibility={
|
||||
importantForAccessibilityValues[this.state.backgroundImportantForAcc]}>
|
||||
<View accessible={true}>
|
||||
<Text style={{fontSize: 25}}>
|
||||
Hello
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: 10,
|
||||
top: 25,
|
||||
right: 10,
|
||||
height: 110,
|
||||
backgroundColor: 'yellow', opacity: 0.5}}
|
||||
accessible={true}
|
||||
accessibilityLabel="Second layout"
|
||||
importantForAccessibility={
|
||||
importantForAccessibilityValues[this.state.forgroundImportantForAcc]}>
|
||||
<View accessible={true}>
|
||||
<Text style={{fontSize: 20}}>
|
||||
world
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<TouchableWithoutFeedback onPress={this._changeBackgroundImportantForAcc}>
|
||||
<View style={styles.embedded}>
|
||||
<Text>
|
||||
Change importantForAccessibility for background layout.
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
<View accessible={true}>
|
||||
<Text>
|
||||
Background layout importantForAccessibility
|
||||
</Text>
|
||||
<Text>
|
||||
{importantForAccessibilityValues[this.state.backgroundImportantForAcc]}
|
||||
</Text>
|
||||
</View>
|
||||
<TouchableWithoutFeedback onPress={this._changeForgroundImportantForAcc}>
|
||||
<View style={styles.embedded}>
|
||||
<Text>
|
||||
Change importantForAccessibility for forground layout.
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
<View accessible={true}>
|
||||
<Text>
|
||||
Forground layout importantForAccessibility
|
||||
</Text>
|
||||
<Text>
|
||||
{importantForAccessibilityValues[this.state.forgroundImportantForAcc]}
|
||||
</Text>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
|
||||
</RNTesterPage>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
embedded: {
|
||||
backgroundColor: 'yellow',
|
||||
padding:10,
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: 'white',
|
||||
padding: 10,
|
||||
height:150,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = AccessibilityAndroidExample;
|
||||
116
packages/examples/src/RNTester/AccessibilityIOSExample.js
Normal file
116
packages/examples/src/RNTester/AccessibilityIOSExample.js
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule AccessibilityIOSExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
AccessibilityInfo,
|
||||
Text,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
class AccessibilityIOSExample extends React.Component<{}> {
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<View
|
||||
onAccessibilityTap={() => alert('onAccessibilityTap success')}
|
||||
accessible={true}>
|
||||
<Text>
|
||||
Accessibility normal tap example
|
||||
</Text>
|
||||
</View>
|
||||
<View onMagicTap={() => alert('onMagicTap success')}
|
||||
accessible={true}>
|
||||
<Text>
|
||||
Accessibility magic tap example
|
||||
</Text>
|
||||
</View>
|
||||
<View accessibilityLabel="Some announcement"
|
||||
accessible={true}>
|
||||
<Text>
|
||||
Accessibility label example
|
||||
</Text>
|
||||
</View>
|
||||
<View accessibilityTraits={['button', 'selected']}
|
||||
accessible={true}>
|
||||
<Text>
|
||||
Accessibility traits example
|
||||
</Text>
|
||||
</View>
|
||||
<Text>
|
||||
Text's accessibilityLabel is the raw text itself unless it is set explicitly.
|
||||
</Text>
|
||||
<Text accessibilityLabel="Test of accessibilityLabel"
|
||||
accessible={true}>
|
||||
This text component's accessibilityLabel is set explicitly.
|
||||
</Text>
|
||||
<View accessibilityElementsHidden={true}>
|
||||
<Text>This view's children are hidden from the accessibility tree</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ScreenReaderStatusExample extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
screenReaderEnabled: false,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
AccessibilityInfo.addEventListener(
|
||||
'change',
|
||||
this._handleScreenReaderToggled
|
||||
);
|
||||
AccessibilityInfo.fetch().done((isEnabled) => {
|
||||
this.setState({
|
||||
screenReaderEnabled: isEnabled
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
AccessibilityInfo.removeEventListener(
|
||||
'change',
|
||||
this._handleScreenReaderToggled
|
||||
);
|
||||
}
|
||||
|
||||
_handleScreenReaderToggled = (isEnabled) => {
|
||||
this.setState({
|
||||
screenReaderEnabled: isEnabled,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Text>
|
||||
The screen reader is {this.state.screenReaderEnabled ? 'enabled' : 'disabled'}.
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
exports.title = 'AccessibilityIOS';
|
||||
exports.description = 'Interface to show iOS\' accessibility samples';
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'Accessibility elements',
|
||||
render(): React.Element<any> { return <AccessibilityIOSExample />; }
|
||||
},
|
||||
{
|
||||
title: 'Check if the screen reader is enabled',
|
||||
render(): React.Element<any> { return <ScreenReaderStatusExample />; }
|
||||
},
|
||||
];
|
||||
211
packages/examples/src/RNTester/ActionSheetIOSExample.js
Normal file
211
packages/examples/src/RNTester/ActionSheetIOSExample.js
Normal file
@@ -0,0 +1,211 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule ActionSheetIOSExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
ActionSheetIOS,
|
||||
StyleSheet,
|
||||
takeSnapshot,
|
||||
Text,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
var BUTTONS = [
|
||||
'Option 0',
|
||||
'Option 1',
|
||||
'Option 2',
|
||||
'Delete',
|
||||
'Cancel',
|
||||
];
|
||||
var DESTRUCTIVE_INDEX = 3;
|
||||
var CANCEL_INDEX = 4;
|
||||
|
||||
class ActionSheetExample extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
clicked: 'none',
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Text onPress={this.showActionSheet} style={style.button}>
|
||||
Click to show the ActionSheet
|
||||
</Text>
|
||||
<Text>
|
||||
Clicked button: {this.state.clicked}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
showActionSheet = () => {
|
||||
ActionSheetIOS.showActionSheetWithOptions({
|
||||
options: BUTTONS,
|
||||
cancelButtonIndex: CANCEL_INDEX,
|
||||
destructiveButtonIndex: DESTRUCTIVE_INDEX,
|
||||
},
|
||||
(buttonIndex) => {
|
||||
this.setState({ clicked: BUTTONS[buttonIndex] });
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
class ActionSheetTintExample extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
clicked: 'none',
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Text onPress={this.showActionSheet} style={style.button}>
|
||||
Click to show the ActionSheet
|
||||
</Text>
|
||||
<Text>
|
||||
Clicked button: {this.state.clicked}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
showActionSheet = () => {
|
||||
ActionSheetIOS.showActionSheetWithOptions({
|
||||
options: BUTTONS,
|
||||
cancelButtonIndex: CANCEL_INDEX,
|
||||
destructiveButtonIndex: DESTRUCTIVE_INDEX,
|
||||
tintColor: 'green',
|
||||
},
|
||||
(buttonIndex) => {
|
||||
this.setState({ clicked: BUTTONS[buttonIndex] });
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
class ShareActionSheetExample extends React.Component<$FlowFixMeProps, $FlowFixMeState> {
|
||||
state = {
|
||||
text: ''
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Text onPress={this.showShareActionSheet} style={style.button}>
|
||||
Click to show the Share ActionSheet
|
||||
</Text>
|
||||
<Text>
|
||||
{this.state.text}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
showShareActionSheet = () => {
|
||||
ActionSheetIOS.showShareActionSheetWithOptions({
|
||||
url: this.props.url,
|
||||
message: 'message to go with the shared url',
|
||||
subject: 'a subject to go in the email heading',
|
||||
excludedActivityTypes: [
|
||||
'com.apple.UIKit.activity.PostToTwitter'
|
||||
]
|
||||
},
|
||||
(error) => alert(error),
|
||||
(completed, method) => {
|
||||
var text;
|
||||
if (completed) {
|
||||
text = `Shared via ${method}`;
|
||||
} else {
|
||||
text = 'You didn\'t share';
|
||||
}
|
||||
this.setState({text});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
class ShareScreenshotExample extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
text: ''
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Text onPress={this.showShareActionSheet} style={style.button}>
|
||||
Click to show the Share ActionSheet
|
||||
</Text>
|
||||
<Text>
|
||||
{this.state.text}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
showShareActionSheet = () => {
|
||||
// Take the snapshot (returns a temp file uri)
|
||||
takeSnapshot('window').then((uri) => {
|
||||
// Share image data
|
||||
ActionSheetIOS.showShareActionSheetWithOptions({
|
||||
url: uri,
|
||||
excludedActivityTypes: [
|
||||
'com.apple.UIKit.activity.PostToTwitter'
|
||||
]
|
||||
},
|
||||
(error) => alert(error),
|
||||
(completed, method) => {
|
||||
var text;
|
||||
if (completed) {
|
||||
text = `Shared via ${method}`;
|
||||
} else {
|
||||
text = 'You didn\'t share';
|
||||
}
|
||||
this.setState({text});
|
||||
});
|
||||
}).catch((error) => alert(error));
|
||||
};
|
||||
}
|
||||
|
||||
var style = StyleSheet.create({
|
||||
button: {
|
||||
marginBottom: 10,
|
||||
fontWeight: '500',
|
||||
}
|
||||
});
|
||||
|
||||
exports.title = 'ActionSheetIOS';
|
||||
exports.description = 'Interface to show iOS\' action sheets';
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'Show Action Sheet',
|
||||
render(): React.Element<any> { return <ActionSheetExample />; }
|
||||
},
|
||||
{
|
||||
title: 'Show Action Sheet with tinted buttons',
|
||||
render(): React.Element<any> { return <ActionSheetTintExample />; }
|
||||
},
|
||||
{
|
||||
title: 'Show Share Action Sheet',
|
||||
render(): React.Element<any> {
|
||||
return <ShareActionSheetExample url="https://code.facebook.com" />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Share Local Image',
|
||||
render(): React.Element<any> {
|
||||
return <ShareActionSheetExample url="bunny.png" />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Share Screenshot',
|
||||
render(): React.Element<any> {
|
||||
return <ShareScreenshotExample />;
|
||||
}
|
||||
}
|
||||
];
|
||||
193
packages/examples/src/RNTester/ActivityIndicatorExample.js
Normal file
193
packages/examples/src/RNTester/ActivityIndicatorExample.js
Normal file
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule ActivityIndicatorExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { ActivityIndicator, StyleSheet, View } from 'react-native';
|
||||
|
||||
/**
|
||||
* Optional Flowtype state and timer types definition
|
||||
*/
|
||||
type State = { animating: boolean; };
|
||||
type Timer = number;
|
||||
|
||||
class ToggleAnimatingActivityIndicator extends Component<$FlowFixMeProps, State> {
|
||||
_timer: Timer;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
animating: true,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setToggleTimeout();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an
|
||||
* error found when Flow v0.63 was deployed. To see the error delete this
|
||||
* comment and run Flow. */
|
||||
clearTimeout(this._timer);
|
||||
}
|
||||
|
||||
setToggleTimeout() {
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an
|
||||
* error found when Flow v0.63 was deployed. To see the error delete this
|
||||
* comment and run Flow. */
|
||||
this._timer = setTimeout(() => {
|
||||
this.setState({animating: !this.state.animating});
|
||||
this.setToggleTimeout();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
animating={this.state.animating}
|
||||
style={[styles.centering, {height: 80}]}
|
||||
size="large"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
exports.displayName = (undefined: ?string);
|
||||
exports.framework = 'React';
|
||||
exports.title = '<ActivityIndicator>';
|
||||
exports.description = 'Animated loading indicators.';
|
||||
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'Default (small, white)',
|
||||
render() {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
style={[styles.centering, styles.gray]}
|
||||
color="white"
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Gray',
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<ActivityIndicator
|
||||
style={[styles.centering]}
|
||||
/>
|
||||
<ActivityIndicator
|
||||
style={[styles.centering, {backgroundColor: '#eeeeee'}]}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Custom colors',
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<ActivityIndicator color="#0000ff" />
|
||||
<ActivityIndicator color="#aa00aa" />
|
||||
<ActivityIndicator color="#aa3300" />
|
||||
<ActivityIndicator color="#00aa00" />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Large',
|
||||
render() {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
style={[styles.centering, styles.gray]}
|
||||
size="large"
|
||||
color="white"
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Large, custom colors',
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<ActivityIndicator
|
||||
size="large"
|
||||
color="#0000ff"
|
||||
/>
|
||||
<ActivityIndicator
|
||||
size="large"
|
||||
color="#aa00aa"
|
||||
/>
|
||||
<ActivityIndicator
|
||||
size="large"
|
||||
color="#aa3300"
|
||||
/>
|
||||
<ActivityIndicator
|
||||
size="large"
|
||||
color="#00aa00"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Start/stop',
|
||||
render() {
|
||||
return <ToggleAnimatingActivityIndicator />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Custom size',
|
||||
render() {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
style={[styles.centering, {transform: [{scale: 1.5}]}]}
|
||||
size="large"
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
// platform: 'android',
|
||||
title: 'Custom size (size: 75)',
|
||||
render() {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
style={styles.centering}
|
||||
size={75}
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
centering: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: 8,
|
||||
},
|
||||
gray: {
|
||||
backgroundColor: '#cccccc',
|
||||
},
|
||||
horizontal: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-around',
|
||||
padding: 8,
|
||||
},
|
||||
});
|
||||
157
packages/examples/src/RNTester/AlertExample.js
Normal file
157
packages/examples/src/RNTester/AlertExample.js
Normal file
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule AlertExample
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
Alert,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
var RNTesterBlock = require('./RNTesterBlock');
|
||||
|
||||
// corporate ipsum > lorem ipsum
|
||||
var alertMessage = 'Credibly reintermediate next-generation potentialities after goal-oriented ' +
|
||||
'catalysts for change. Dynamically revolutionize.';
|
||||
|
||||
/**
|
||||
* Simple alert examples.
|
||||
*/
|
||||
class SimpleAlertExampleBlock extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<TouchableHighlight style={styles.wrapper}
|
||||
onPress={() => Alert.alert(
|
||||
'Alert Title',
|
||||
alertMessage,
|
||||
)}>
|
||||
<View style={styles.button}>
|
||||
<Text>Alert with message and default button</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight style={styles.wrapper}
|
||||
onPress={() => Alert.alert(
|
||||
'Alert Title',
|
||||
alertMessage,
|
||||
[
|
||||
{text: 'OK', onPress: () => console.log('OK Pressed!')},
|
||||
]
|
||||
)}>
|
||||
<View style={styles.button}>
|
||||
<Text>Alert with one button</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight style={styles.wrapper}
|
||||
onPress={() => Alert.alert(
|
||||
'Alert Title',
|
||||
alertMessage,
|
||||
[
|
||||
{text: 'Cancel', onPress: () => console.log('Cancel Pressed!')},
|
||||
{text: 'OK', onPress: () => console.log('OK Pressed!')},
|
||||
]
|
||||
)}>
|
||||
<View style={styles.button}>
|
||||
<Text>Alert with two buttons</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight style={styles.wrapper}
|
||||
onPress={() => Alert.alert(
|
||||
'Alert Title',
|
||||
null,
|
||||
[
|
||||
{text: 'Foo', onPress: () => console.log('Foo Pressed!')},
|
||||
{text: 'Bar', onPress: () => console.log('Bar Pressed!')},
|
||||
{text: 'Baz', onPress: () => console.log('Baz Pressed!')},
|
||||
]
|
||||
)}>
|
||||
<View style={styles.button}>
|
||||
<Text>Alert with three buttons</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight style={styles.wrapper}
|
||||
onPress={() => Alert.alert(
|
||||
'Foo Title',
|
||||
alertMessage,
|
||||
'..............'.split('').map((dot, index) => ({
|
||||
text: 'Button ' + index,
|
||||
onPress: () => console.log('Pressed ' + index)
|
||||
}))
|
||||
)}>
|
||||
<View style={styles.button}>
|
||||
<Text>Alert with too many buttons</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight style={styles.wrapper}
|
||||
onPress={() => Alert.alert(
|
||||
'Alert Title',
|
||||
null,
|
||||
[
|
||||
{text: 'OK', onPress: () => console.log('OK Pressed!')},
|
||||
],
|
||||
{
|
||||
cancelable: false
|
||||
}
|
||||
)}>
|
||||
<View style={styles.button}>
|
||||
<Text>Alert that cannot be dismissed</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight style={styles.wrapper}
|
||||
onPress={() => Alert.alert(
|
||||
'',
|
||||
alertMessage,
|
||||
[
|
||||
{text: 'OK', onPress: () => console.log('OK Pressed!')},
|
||||
]
|
||||
)}>
|
||||
<View style={styles.button}>
|
||||
<Text>Alert without title</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AlertExample extends React.Component {
|
||||
static title = 'Alert';
|
||||
|
||||
static description = 'Alerts display a concise and informative message ' +
|
||||
'and prompt the user to make a decision.';
|
||||
|
||||
render() {
|
||||
return (
|
||||
<RNTesterBlock title={'Alert'}>
|
||||
<SimpleAlertExampleBlock />
|
||||
</RNTesterBlock>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
wrapper: {
|
||||
borderRadius: 5,
|
||||
marginBottom: 5,
|
||||
},
|
||||
button: {
|
||||
backgroundColor: '#eeeeee',
|
||||
padding: 10,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
AlertExample,
|
||||
SimpleAlertExampleBlock,
|
||||
};
|
||||
183
packages/examples/src/RNTester/AlertIOSExample.js
Normal file
183
packages/examples/src/RNTester/AlertIOSExample.js
Normal file
@@ -0,0 +1,183 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule AlertIOSExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
StyleSheet,
|
||||
View,
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
AlertIOS,
|
||||
} = ReactNative;
|
||||
|
||||
var { SimpleAlertExampleBlock } = require('./AlertExample');
|
||||
|
||||
exports.framework = 'React';
|
||||
exports.title = 'AlertIOS';
|
||||
exports.description = 'iOS alerts and action sheets';
|
||||
exports.examples = [{
|
||||
title: 'Alerts',
|
||||
render() {
|
||||
return <SimpleAlertExampleBlock />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Prompt Options',
|
||||
render(): React.Element<any> {
|
||||
return <PromptOptions />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Prompt Types',
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<TouchableHighlight
|
||||
style={styles.wrapper}
|
||||
onPress={() => AlertIOS.prompt('Plain Text Entry')}>
|
||||
|
||||
<View style={styles.button}>
|
||||
<Text>
|
||||
plain-text
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
style={styles.wrapper}
|
||||
onPress={() => AlertIOS.prompt('Secure Text', null, null, 'secure-text')}>
|
||||
|
||||
<View style={styles.button}>
|
||||
<Text>
|
||||
secure-text
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
style={styles.wrapper}
|
||||
onPress={() => AlertIOS.prompt('Login & Password', null, null, 'login-password')}>
|
||||
|
||||
<View style={styles.button}>
|
||||
<Text>
|
||||
login-password
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}];
|
||||
|
||||
class PromptOptions extends React.Component<$FlowFixMeProps, any> {
|
||||
customButtons: Array<Object>;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// $FlowFixMe this seems to be a Flow bug, `saveResponse` is defined below
|
||||
this.saveResponse = this.saveResponse.bind(this);
|
||||
|
||||
this.customButtons = [{
|
||||
text: 'Custom OK',
|
||||
onPress: this.saveResponse
|
||||
}, {
|
||||
text: 'Custom Cancel',
|
||||
style: 'cancel',
|
||||
}];
|
||||
|
||||
this.state = {
|
||||
promptValue: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Text style={{marginBottom: 10}}>
|
||||
<Text style={{fontWeight: 'bold'}}>Prompt value:</Text> {this.state.promptValue}
|
||||
</Text>
|
||||
|
||||
<TouchableHighlight
|
||||
style={styles.wrapper}
|
||||
onPress={() => AlertIOS.prompt('Type a value', null, this.saveResponse)}>
|
||||
|
||||
<View style={styles.button}>
|
||||
<Text>
|
||||
prompt with title & callback
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
|
||||
<TouchableHighlight
|
||||
style={styles.wrapper}
|
||||
onPress={() => AlertIOS.prompt('Type a value', null, this.customButtons)}>
|
||||
|
||||
<View style={styles.button}>
|
||||
<Text>
|
||||
prompt with title & custom buttons
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
|
||||
<TouchableHighlight
|
||||
style={styles.wrapper}
|
||||
onPress={() => AlertIOS.prompt('Type a phone number', null, null, 'plain-text', undefined, 'phone-pad')}>
|
||||
|
||||
<View style={styles.button}>
|
||||
<Text>
|
||||
prompt with title & custom keyboard
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
|
||||
<TouchableHighlight
|
||||
style={styles.wrapper}
|
||||
onPress={() => AlertIOS.prompt('Type a value', null, this.saveResponse, undefined, 'Default value')}>
|
||||
|
||||
<View style={styles.button}>
|
||||
<Text>
|
||||
prompt with title, callback & default value
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
|
||||
<TouchableHighlight
|
||||
style={styles.wrapper}
|
||||
onPress={() => AlertIOS.prompt('Type a value', null, this.customButtons, 'login-password', 'admin@site.com')}>
|
||||
|
||||
<View style={styles.button}>
|
||||
<Text>
|
||||
prompt with title, custom buttons, login/password & default value
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
saveResponse(promptValue) {
|
||||
this.setState({ promptValue: JSON.stringify(promptValue) });
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
wrapper: {
|
||||
borderRadius: 5,
|
||||
marginBottom: 5,
|
||||
},
|
||||
button: {
|
||||
backgroundColor: '#eeeeee',
|
||||
padding: 10,
|
||||
},
|
||||
});
|
||||
226
packages/examples/src/RNTester/AnimatedExample.js
Normal file
226
packages/examples/src/RNTester/AnimatedExample.js
Normal file
@@ -0,0 +1,226 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule AnimatedExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
Animated,
|
||||
Easing,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = ReactNative;
|
||||
var RNTesterButton = require('./RNTesterButton');
|
||||
|
||||
exports.framework = 'React';
|
||||
exports.title = 'Animated - Examples';
|
||||
exports.description = 'Animated provides a powerful ' +
|
||||
'and easy-to-use API for building modern, ' +
|
||||
'interactive user experiences.';
|
||||
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'FadeInView',
|
||||
description: 'Uses a simple timing animation to ' +
|
||||
'bring opacity from 0 to 1 when the component ' +
|
||||
'mounts.',
|
||||
render: function() {
|
||||
class FadeInView extends React.Component<$FlowFixMeProps, any> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
fadeAnim: new Animated.Value(0), // opacity 0
|
||||
};
|
||||
}
|
||||
componentDidMount() {
|
||||
Animated.timing( // Uses easing functions
|
||||
this.state.fadeAnim, // The value to drive
|
||||
{
|
||||
toValue: 1, // Target
|
||||
duration: 2000, // Configuration
|
||||
},
|
||||
).start(); // Don't forget start!
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<Animated.View // Special animatable View
|
||||
style={{
|
||||
opacity: this.state.fadeAnim, // Binds
|
||||
}}>
|
||||
{this.props.children}
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
}
|
||||
class FadeInExample extends React.Component<$FlowFixMeProps, any> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
show: true,
|
||||
};
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<RNTesterButton onPress={() => {
|
||||
this.setState((state) => (
|
||||
{show: !state.show}
|
||||
));
|
||||
}}>
|
||||
Press to {this.state.show ?
|
||||
'Hide' : 'Show'}
|
||||
</RNTesterButton>
|
||||
{this.state.show && <FadeInView>
|
||||
<View style={styles.content}>
|
||||
<Text>FadeInView</Text>
|
||||
</View>
|
||||
</FadeInView>}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
return <FadeInExample />;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Transform Bounce',
|
||||
description: 'One `Animated.Value` is driven by a ' +
|
||||
'spring with custom constants and mapped to an ' +
|
||||
'ordered set of transforms. Each transform has ' +
|
||||
'an interpolation to convert the value into the ' +
|
||||
'right range and units.',
|
||||
render: function() {
|
||||
this.anim = this.anim || new Animated.Value(0);
|
||||
return (
|
||||
<View>
|
||||
<RNTesterButton onPress={() => {
|
||||
Animated.spring(this.anim, {
|
||||
toValue: 0, // Returns to the start
|
||||
velocity: 3, // Velocity makes it move
|
||||
tension: -10, // Slow
|
||||
friction: 1, // Oscillate a lot
|
||||
}).start(); }}>
|
||||
Press to Fling it!
|
||||
</RNTesterButton>
|
||||
<Animated.View
|
||||
style={[styles.content, {
|
||||
transform: [ // Array order matters
|
||||
{scale: this.anim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [1, 4],
|
||||
})},
|
||||
{translateX: this.anim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, 500],
|
||||
})},
|
||||
{rotate: this.anim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [
|
||||
'0deg', '360deg' // 'deg' or 'rad'
|
||||
],
|
||||
})},
|
||||
]}
|
||||
]}>
|
||||
<Text>Transforms!</Text>
|
||||
</Animated.View>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Composite Animations with Easing',
|
||||
description: 'Sequence, parallel, delay, and ' +
|
||||
'stagger with different easing functions.',
|
||||
render: function() {
|
||||
this.anims = this.anims || [1,2,3].map(
|
||||
() => new Animated.Value(0)
|
||||
);
|
||||
return (
|
||||
<View>
|
||||
<RNTesterButton onPress={() => {
|
||||
var timing = Animated.timing;
|
||||
Animated.sequence([ // One after the other
|
||||
timing(this.anims[0], {
|
||||
toValue: 200,
|
||||
easing: Easing.linear,
|
||||
}),
|
||||
Animated.delay(400), // Use with sequence
|
||||
timing(this.anims[0], {
|
||||
toValue: 0,
|
||||
easing: Easing.elastic(2), // Springy
|
||||
}),
|
||||
Animated.delay(400),
|
||||
Animated.stagger(200,
|
||||
this.anims.map((anim) => timing(
|
||||
anim, {toValue: 200}
|
||||
)).concat(
|
||||
this.anims.map((anim) => timing(
|
||||
anim, {toValue: 0}
|
||||
))),
|
||||
),
|
||||
Animated.delay(400),
|
||||
Animated.parallel([
|
||||
Easing.inOut(Easing.quad), // Symmetric
|
||||
Easing.back(1.5), // Goes backwards first
|
||||
Easing.ease // Default bezier
|
||||
].map((easing, ii) => (
|
||||
timing(this.anims[ii], {
|
||||
toValue: 320, easing, duration: 3000,
|
||||
})
|
||||
))),
|
||||
Animated.delay(400),
|
||||
Animated.stagger(200,
|
||||
this.anims.map((anim) => timing(anim, {
|
||||
toValue: 0,
|
||||
easing: Easing.bounce, // Like a ball
|
||||
duration: 2000,
|
||||
})),
|
||||
),
|
||||
]).start(); }}>
|
||||
Press to Animate
|
||||
</RNTesterButton>
|
||||
{['Composite', 'Easing', 'Animations!'].map(
|
||||
(text, ii) => (
|
||||
<Animated.View
|
||||
key={text}
|
||||
style={[styles.content, {
|
||||
left: this.anims[ii]
|
||||
}]}>
|
||||
<Text>{text}</Text>
|
||||
</Animated.View>
|
||||
)
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Continuous Interactions',
|
||||
description: 'Gesture events, chaining, 2D ' +
|
||||
'values, interrupting and transitioning ' +
|
||||
'animations, etc.',
|
||||
render: () => (
|
||||
<Text>Checkout the Gratuitous Animation App!</Text>
|
||||
),
|
||||
}
|
||||
];
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
content: {
|
||||
backgroundColor: 'deepskyblue',
|
||||
borderWidth: 1,
|
||||
borderColor: 'dodgerblue',
|
||||
padding: 20,
|
||||
margin: 20,
|
||||
borderRadius: 10,
|
||||
alignItems: 'center',
|
||||
},
|
||||
});
|
||||
323
packages/examples/src/RNTester/AnimatedGratuitousApp/AnExApp.js
Normal file
323
packages/examples/src/RNTester/AnimatedGratuitousApp/AnExApp.js
Normal file
@@ -0,0 +1,323 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule AnExApp
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
Animated,
|
||||
LayoutAnimation,
|
||||
PanResponder,
|
||||
StyleSheet,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
var AnExSet = require('AnExSet');
|
||||
|
||||
var CIRCLE_SIZE = 80;
|
||||
var CIRCLE_MARGIN = 18;
|
||||
var NUM_CIRCLES = 30;
|
||||
|
||||
class Circle extends React.Component<any, any> {
|
||||
longTimer: number;
|
||||
|
||||
_onLongPress: () => void;
|
||||
_toggleIsActive: () => void;
|
||||
constructor(props: Object): void {
|
||||
super();
|
||||
this._onLongPress = this._onLongPress.bind(this);
|
||||
this._toggleIsActive = this._toggleIsActive.bind(this);
|
||||
this.state = {
|
||||
isActive: false,
|
||||
pan: new Animated.ValueXY(), // Vectors reduce boilerplate. (step1: uncomment)
|
||||
pop: new Animated.Value(0), // Initial value. (step2a: uncomment)
|
||||
};
|
||||
}
|
||||
|
||||
_onLongPress(): void {
|
||||
var config = {tension: 40, friction: 3};
|
||||
this.state.pan.addListener((value) => { // Async listener for state changes (step1: uncomment)
|
||||
this.props.onMove && this.props.onMove(value);
|
||||
});
|
||||
Animated.spring(this.state.pop, {
|
||||
toValue: 1, // Pop to larger size. (step2b: uncomment)
|
||||
...config, // Reuse config for convenient consistency (step2b: uncomment)
|
||||
}).start();
|
||||
this.setState({panResponder: PanResponder.create({
|
||||
onPanResponderMove: Animated.event([
|
||||
null, // native event - ignore (step1: uncomment)
|
||||
{dx: this.state.pan.x, dy: this.state.pan.y}, // links pan to gestureState (step1: uncomment)
|
||||
]),
|
||||
onPanResponderRelease: (e, gestureState) => {
|
||||
LayoutAnimation.easeInEaseOut(); // @flowfixme animates layout update as one batch (step3: uncomment)
|
||||
Animated.spring(this.state.pop, {
|
||||
toValue: 0, // Pop back to 0 (step2c: uncomment)
|
||||
...config,
|
||||
}).start();
|
||||
this.setState({panResponder: undefined});
|
||||
this.props.onMove && this.props.onMove({
|
||||
x: gestureState.dx + this.props.restLayout.x,
|
||||
y: gestureState.dy + this.props.restLayout.y,
|
||||
});
|
||||
this.props.onDeactivate();
|
||||
this.state.pan.removeAllListeners();
|
||||
},
|
||||
})}, () => {
|
||||
this.props.onActivate();
|
||||
});
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
if (this.state.panResponder) {
|
||||
var handlers = this.state.panResponder.panHandlers;
|
||||
var dragStyle = { // Used to position while dragging
|
||||
position: 'absolute', // Hoist out of layout (step1: uncomment)
|
||||
...this.state.pan.getLayout(), // Convenience converter (step1: uncomment)
|
||||
};
|
||||
} else {
|
||||
handlers = {
|
||||
onStartShouldSetResponder: () => !this.state.isActive,
|
||||
onResponderGrant: () => {
|
||||
this.state.pan.setValue({x: 0, y: 0}); // reset (step1: uncomment)
|
||||
this.state.pan.setOffset(this.props.restLayout); // offset from onLayout (step1: uncomment)
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses
|
||||
* an error found when Flow v0.63 was deployed. To see the error
|
||||
* delete this comment and run Flow. */
|
||||
this.longTimer = setTimeout(this._onLongPress, 300);
|
||||
},
|
||||
onResponderRelease: () => {
|
||||
if (!this.state.panResponder) {
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment
|
||||
* suppresses an error found when Flow v0.63 was deployed. To see
|
||||
* the error delete this comment and run Flow. */
|
||||
clearTimeout(this.longTimer);
|
||||
this._toggleIsActive();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
var animatedStyle: Object = {
|
||||
shadowOpacity: this.state.pop, // no need for interpolation (step2d: uncomment)
|
||||
transform: [
|
||||
{scale: this.state.pop.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [1, 1.3] // scale up from 1 to 1.3 (step2d: uncomment)
|
||||
})},
|
||||
],
|
||||
};
|
||||
var openVal = this.props.openVal;
|
||||
if (this.props.dummy) {
|
||||
animatedStyle.opacity = 0;
|
||||
} else if (this.state.isActive) {
|
||||
var innerOpenStyle = [styles.open, { // (step4: uncomment)
|
||||
left: openVal.interpolate({inputRange: [0, 1], outputRange: [this.props.restLayout.x, 0]}),
|
||||
top: openVal.interpolate({inputRange: [0, 1], outputRange: [this.props.restLayout.y, 0]}),
|
||||
width: openVal.interpolate({inputRange: [0, 1], outputRange: [CIRCLE_SIZE, this.props.containerLayout.width]}),
|
||||
height: openVal.interpolate({inputRange: [0, 1], outputRange: [CIRCLE_SIZE, this.props.containerLayout.height]}),
|
||||
margin: openVal.interpolate({inputRange: [0, 1], outputRange: [CIRCLE_MARGIN, 0]}),
|
||||
borderRadius: openVal.interpolate({inputRange: [-0.15, 0, 0.5, 1], outputRange: [0, CIRCLE_SIZE / 2, CIRCLE_SIZE * 1.3, 0]}),
|
||||
}];
|
||||
}
|
||||
return (
|
||||
<Animated.View
|
||||
onLayout={this.props.onLayout}
|
||||
style={[styles.dragView, dragStyle, animatedStyle, this.state.isActive ? styles.open : null]}
|
||||
{...handlers}>
|
||||
<Animated.View style={[styles.circle, innerOpenStyle]}>
|
||||
<AnExSet
|
||||
containerLayout={this.props.containerLayout}
|
||||
id={this.props.id}
|
||||
isActive={this.state.isActive}
|
||||
openVal={this.props.openVal}
|
||||
onDismiss={this._toggleIsActive}
|
||||
/>
|
||||
</Animated.View>
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
_toggleIsActive(velocity) {
|
||||
var config = {tension: 30, friction: 7};
|
||||
if (this.state.isActive) {
|
||||
Animated.spring(this.props.openVal, {toValue: 0, ...config}).start(() => { // (step4: uncomment)
|
||||
this.setState({isActive: false}, this.props.onDeactivate);
|
||||
}); // (step4: uncomment)
|
||||
} else {
|
||||
this.props.onActivate();
|
||||
this.setState({isActive: true, panResponder: undefined}, () => {
|
||||
// this.props.openVal.setValue(1); // (step4: comment)
|
||||
Animated.spring(this.props.openVal, {toValue: 1, ...config}).start(); // (step4: uncomment)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AnExApp extends React.Component<any, any> {
|
||||
static title = 'Animated - Gratuitous App';
|
||||
static description = 'Bunch of Animations - tap a circle to ' +
|
||||
'open a view with more animations, or longPress and drag to reorder circles.';
|
||||
|
||||
_onMove: (position: Point) => void;
|
||||
constructor(props: any): void {
|
||||
super(props);
|
||||
var keys = [];
|
||||
for (var idx = 0; idx < NUM_CIRCLES; idx++) {
|
||||
keys.push('E' + idx);
|
||||
}
|
||||
this.state = {
|
||||
keys,
|
||||
restLayouts: [],
|
||||
openVal: new Animated.Value(0),
|
||||
};
|
||||
this._onMove = this._onMove.bind(this);
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
var circles = this.state.keys.map((key, idx) => {
|
||||
if (key === this.state.activeKey) {
|
||||
return <Circle key={key + 'd'} dummy={true} />;
|
||||
} else {
|
||||
if (!this.state.restLayouts[idx]) {
|
||||
var onLayout = function(index, e) {
|
||||
var layout = e.nativeEvent.layout;
|
||||
this.setState((state) => {
|
||||
state.restLayouts[index] = layout;
|
||||
return state;
|
||||
});
|
||||
}.bind(this, idx);
|
||||
}
|
||||
return (
|
||||
<Circle
|
||||
key={key}
|
||||
id={key}
|
||||
openVal={this.state.openVal}
|
||||
onLayout={onLayout}
|
||||
restLayout={this.state.restLayouts[idx]}
|
||||
onActivate={this.setState.bind(this, {
|
||||
activeKey: key,
|
||||
activeInitialLayout: this.state.restLayouts[idx],
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
if (this.state.activeKey) {
|
||||
circles.push(
|
||||
<Animated.View key="dark" style={[styles.darkening, {opacity: this.state.openVal}]} />
|
||||
);
|
||||
circles.push(
|
||||
<Circle
|
||||
openVal={this.state.openVal}
|
||||
key={this.state.activeKey}
|
||||
id={this.state.activeKey}
|
||||
restLayout={this.state.activeInitialLayout}
|
||||
containerLayout={this.state.layout}
|
||||
onMove={this._onMove}
|
||||
onDeactivate={() => { this.setState({activeKey: undefined}); }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.grid} onLayout={(e) => this.setState({layout: e.nativeEvent.layout})}>
|
||||
{circles}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_onMove(position: Point): void {
|
||||
var newKeys = moveToClosest(this.state, position);
|
||||
if (newKeys !== this.state.keys) {
|
||||
LayoutAnimation.easeInEaseOut(); // animates layout update as one batch (step3: uncomment)
|
||||
this.setState({keys: newKeys});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Point = {x: number, y: number};
|
||||
function distance(p1: Point, p2: Point): number {
|
||||
var dx = p1.x - p2.x;
|
||||
var dy = p1.y - p2.y;
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
|
||||
function moveToClosest({activeKey, keys, restLayouts}, position) {
|
||||
var activeIdx = -1;
|
||||
var closestIdx = activeIdx;
|
||||
var minDist = Infinity;
|
||||
var newKeys = [];
|
||||
keys.forEach((key, idx) => {
|
||||
var dist = distance(position, restLayouts[idx]);
|
||||
if (key === activeKey) {
|
||||
idx = activeIdx;
|
||||
} else {
|
||||
newKeys.push(key);
|
||||
}
|
||||
if (dist < minDist) {
|
||||
minDist = dist;
|
||||
closestIdx = idx;
|
||||
}
|
||||
});
|
||||
if (closestIdx === activeIdx) {
|
||||
return keys; // nothing changed
|
||||
} else {
|
||||
newKeys.splice(closestIdx, 0, activeKey);
|
||||
return newKeys;
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
grid: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
circle: {
|
||||
width: CIRCLE_SIZE,
|
||||
height: CIRCLE_SIZE,
|
||||
borderRadius: CIRCLE_SIZE / 2,
|
||||
borderWidth: 1,
|
||||
borderColor: 'black',
|
||||
margin: CIRCLE_MARGIN,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
dragView: {
|
||||
shadowRadius: 10,
|
||||
shadowColor: 'rgba(0,0,0,0.7)',
|
||||
shadowOffset: {height: 8},
|
||||
alignSelf: 'flex-start',
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
open: {
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
width: undefined, // unset value from styles.circle
|
||||
height: undefined, // unset value from styles.circle
|
||||
borderRadius: 0, // unset value from styles.circle
|
||||
},
|
||||
darkening: {
|
||||
backgroundColor: 'black',
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = AnExApp;
|
||||
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule AnExBobble
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
Animated,
|
||||
PanResponder,
|
||||
StyleSheet,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
var NUM_BOBBLES = 5;
|
||||
var RAD_EACH = Math.PI / 2 / (NUM_BOBBLES - 2);
|
||||
var RADIUS = 160;
|
||||
var BOBBLE_SPOTS = [...Array(NUM_BOBBLES)].map((_, i) => { // static positions
|
||||
return i === 0 ? {x: 0, y: 0} : { // first bobble is the selector
|
||||
x: -Math.cos(RAD_EACH * (i - 1)) * RADIUS,
|
||||
y: -Math.sin(RAD_EACH * (i - 1)) * RADIUS,
|
||||
};
|
||||
});
|
||||
|
||||
class AnExBobble extends React.Component<Object, any> {
|
||||
constructor(props: Object) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
this.state.bobbles = BOBBLE_SPOTS.map((_, i) => {
|
||||
return new Animated.ValueXY();
|
||||
});
|
||||
this.state.selectedBobble = null;
|
||||
var bobblePanListener = (e, gestureState) => { // async events => change selection
|
||||
var newSelected = computeNewSelected(gestureState);
|
||||
if (this.state.selectedBobble !== newSelected) {
|
||||
if (this.state.selectedBobble !== null) {
|
||||
var restSpot = BOBBLE_SPOTS[this.state.selectedBobble];
|
||||
Animated.spring(this.state.bobbles[this.state.selectedBobble], {
|
||||
toValue: restSpot, // return previously selected bobble to rest position
|
||||
}).start();
|
||||
}
|
||||
if (newSelected !== null && newSelected !== 0) {
|
||||
Animated.spring(this.state.bobbles[newSelected], {
|
||||
toValue: this.state.bobbles[0], // newly selected should track the selector
|
||||
}).start();
|
||||
}
|
||||
this.state.selectedBobble = newSelected;
|
||||
}
|
||||
};
|
||||
var releaseBobble = () => {
|
||||
this.state.bobbles.forEach((bobble, i) => {
|
||||
Animated.spring(bobble, {
|
||||
toValue: {x: 0, y: 0} // all bobbles return to zero
|
||||
}).start();
|
||||
});
|
||||
};
|
||||
this.state.bobbleResponder = PanResponder.create({
|
||||
onStartShouldSetPanResponder: () => true,
|
||||
onPanResponderGrant: () => {
|
||||
BOBBLE_SPOTS.forEach((spot, idx) => {
|
||||
Animated.spring(this.state.bobbles[idx], {
|
||||
toValue: spot, // spring each bobble to its spot
|
||||
friction: 3, // less friction => bouncier
|
||||
}).start();
|
||||
});
|
||||
},
|
||||
onPanResponderMove: Animated.event(
|
||||
[ null, {dx: this.state.bobbles[0].x, dy: this.state.bobbles[0].y} ],
|
||||
{listener: bobblePanListener} // async state changes with arbitrary logic
|
||||
),
|
||||
onPanResponderRelease: releaseBobble,
|
||||
onPanResponderTerminate: releaseBobble,
|
||||
});
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
return (
|
||||
<View style={styles.bobbleContainer}>
|
||||
{this.state.bobbles.map((_, i) => {
|
||||
var j = this.state.bobbles.length - i - 1; // reverse so lead on top
|
||||
var handlers = j > 0 ? {} : this.state.bobbleResponder.panHandlers;
|
||||
return (
|
||||
<Animated.Image
|
||||
{...handlers}
|
||||
key={i}
|
||||
source={{uri: BOBBLE_IMGS[j]}}
|
||||
style={[styles.circle, {
|
||||
backgroundColor: randColor(), // re-renders are obvious
|
||||
transform: this.state.bobbles[j].getTranslateTransform(), // simple conversion
|
||||
}]}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
circle: {
|
||||
position: 'absolute',
|
||||
height: 60,
|
||||
width: 60,
|
||||
borderRadius: 30,
|
||||
borderWidth: 0.5,
|
||||
},
|
||||
bobbleContainer: {
|
||||
top: -68,
|
||||
paddingRight: 66,
|
||||
flexDirection: 'row',
|
||||
flex: 1,
|
||||
justifyContent: 'flex-end',
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
});
|
||||
|
||||
function computeNewSelected(
|
||||
gestureState: Object,
|
||||
): ?number {
|
||||
var {dx, dy} = gestureState;
|
||||
var minDist = Infinity;
|
||||
var newSelected = null;
|
||||
var pointRadius = Math.sqrt(dx * dx + dy * dy);
|
||||
if (Math.abs(RADIUS - pointRadius) < 80) {
|
||||
BOBBLE_SPOTS.forEach((spot, idx) => {
|
||||
var delta = {x: spot.x - dx, y: spot.y - dy};
|
||||
var dist = delta.x * delta.x + delta.y * delta.y;
|
||||
if (dist < minDist) {
|
||||
minDist = dist;
|
||||
newSelected = idx;
|
||||
}
|
||||
});
|
||||
}
|
||||
return newSelected;
|
||||
}
|
||||
|
||||
function randColor(): string {
|
||||
var colors = [0,1,2].map(() => Math.floor(Math.random() * 150 + 100));
|
||||
return 'rgb(' + colors.join(',') + ')';
|
||||
}
|
||||
|
||||
var BOBBLE_IMGS = [
|
||||
'https://scontent-sea1-1.xx.fbcdn.net/hphotos-xpf1/t39.1997-6/10173489_272703316237267_1025826781_n.png',
|
||||
'https://scontent-sea1-1.xx.fbcdn.net/hphotos-xaf1/l/t39.1997-6/p240x240/851578_631487400212668_2087073502_n.png',
|
||||
'https://scontent-sea1-1.xx.fbcdn.net/hphotos-xaf1/t39.1997-6/p240x240/851583_654446917903722_178118452_n.png',
|
||||
'https://scontent-sea1-1.xx.fbcdn.net/hphotos-xaf1/t39.1997-6/p240x240/851565_641023175913294_875343096_n.png',
|
||||
'https://scontent-sea1-1.xx.fbcdn.net/hphotos-xaf1/t39.1997-6/851562_575284782557566_1188781517_n.png',
|
||||
];
|
||||
|
||||
module.exports = AnExBobble;
|
||||
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule AnExChained
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
Animated,
|
||||
PanResponder,
|
||||
StyleSheet,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
class AnExChained extends React.Component<Object, any> {
|
||||
constructor(props: Object) {
|
||||
super(props);
|
||||
this.state = {
|
||||
stickers: [new Animated.ValueXY()], // 1 leader
|
||||
};
|
||||
var stickerConfig = {tension: 2, friction: 3}; // soft spring
|
||||
for (var i = 0; i < 4; i++) { // 4 followers
|
||||
var sticker = new Animated.ValueXY();
|
||||
Animated.spring(sticker, {
|
||||
...stickerConfig,
|
||||
toValue: this.state.stickers[i], // Animated toValue's are tracked
|
||||
}).start();
|
||||
this.state.stickers.push(sticker); // push on the followers
|
||||
}
|
||||
var releaseChain = (e, gestureState) => {
|
||||
this.state.stickers[0].flattenOffset(); // merges offset into value and resets
|
||||
Animated.sequence([ // spring to start after decay finishes
|
||||
Animated.decay(this.state.stickers[0], { // coast to a stop
|
||||
velocity: {x: gestureState.vx, y: gestureState.vy},
|
||||
deceleration: 0.997,
|
||||
}),
|
||||
Animated.spring(this.state.stickers[0], {
|
||||
toValue: {x: 0, y: 0} // return to start
|
||||
}),
|
||||
]).start();
|
||||
};
|
||||
this.state.chainResponder = PanResponder.create({
|
||||
onStartShouldSetPanResponder: () => true,
|
||||
onPanResponderGrant: () => {
|
||||
this.state.stickers[0].stopAnimation((value) => {
|
||||
this.state.stickers[0].setOffset(value); // start where sticker animated to
|
||||
this.state.stickers[0].setValue({x: 0, y: 0}); // avoid flicker before next event
|
||||
});
|
||||
},
|
||||
onPanResponderMove: Animated.event(
|
||||
[null, {dx: this.state.stickers[0].x, dy: this.state.stickers[0].y}] // map gesture to leader
|
||||
),
|
||||
onPanResponderRelease: releaseChain,
|
||||
onPanResponderTerminate: releaseChain,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.chained}>
|
||||
{this.state.stickers.map((_, i) => {
|
||||
var j = this.state.stickers.length - i - 1; // reverse so leader is on top
|
||||
var handlers = (j === 0) ? this.state.chainResponder.panHandlers : {};
|
||||
return (
|
||||
<Animated.Image
|
||||
{...handlers}
|
||||
key={i}
|
||||
source={CHAIN_IMGS[j]}
|
||||
style={[styles.sticker, {
|
||||
transform: this.state.stickers[j].getTranslateTransform(), // simple conversion
|
||||
}]}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
chained: {
|
||||
alignSelf: 'flex-end',
|
||||
top: -160,
|
||||
right: 126
|
||||
},
|
||||
sticker: {
|
||||
position: 'absolute',
|
||||
height: 120,
|
||||
width: 120,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
});
|
||||
|
||||
var CHAIN_IMGS = [
|
||||
require('../hawk.png'),
|
||||
require('../bunny.png'),
|
||||
require('../relay.png'),
|
||||
require('../hawk.png'),
|
||||
require('../bunny.png')
|
||||
];
|
||||
|
||||
module.exports = AnExChained;
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule AnExScroll
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
Animated,
|
||||
Image,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
class AnExScroll extends React.Component<$FlowFixMeProps, any> {
|
||||
state: any = { scrollX: new Animated.Value(0) };
|
||||
|
||||
render() {
|
||||
var width = this.props.panelWidth;
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ScrollView
|
||||
automaticallyAdjustContentInsets={false}
|
||||
scrollEventThrottle={16 /* get all events */ }
|
||||
onScroll={Animated.event(
|
||||
[{nativeEvent: {contentOffset: {x: this.state.scrollX}}}] // nested event mapping
|
||||
)}
|
||||
contentContainerStyle={{flex: 1, padding: 10}}
|
||||
pagingEnabled={true}
|
||||
horizontal={true}>
|
||||
<View style={[styles.page, {width}]}>
|
||||
<Image
|
||||
style={{width: 180, height: 180}}
|
||||
source={HAWK_PIC}
|
||||
/>
|
||||
<Text style={styles.text}>
|
||||
{'I\'ll find something to put here.'}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={[styles.page, {width}]}>
|
||||
<Text style={styles.text}>{'And here.'}</Text>
|
||||
</View>
|
||||
<View style={[styles.page, {width}]}>
|
||||
<Text>{'But not here.'}</Text>
|
||||
</View>
|
||||
</ScrollView>
|
||||
<Animated.Image
|
||||
pointerEvents="none"
|
||||
style={[styles.bunny, {transform: [
|
||||
{
|
||||
translateX: this.state.scrollX.interpolate({
|
||||
inputRange: [0, width, 2 * width],
|
||||
outputRange: [0, 0, width / 3],
|
||||
extrapolate: 'clamp',
|
||||
}),
|
||||
},
|
||||
{
|
||||
translateY: this.state.scrollX.interpolate({
|
||||
inputRange: [0, width, 2 * width],
|
||||
outputRange: [0, -200, -260],
|
||||
extrapolate: 'clamp',
|
||||
}),
|
||||
},
|
||||
{
|
||||
scale: this.state.scrollX.interpolate({
|
||||
inputRange: [0, width, 2 * width],
|
||||
outputRange: [0.5, 0.5, 2],
|
||||
extrapolate: 'clamp',
|
||||
}),
|
||||
},
|
||||
]}]}
|
||||
source={BUNNY_PIC}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: 'transparent',
|
||||
flex: 1,
|
||||
},
|
||||
text: {
|
||||
padding: 4,
|
||||
paddingBottom: 10,
|
||||
fontWeight: 'bold',
|
||||
fontSize: 18,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
bunny: {
|
||||
backgroundColor: 'transparent',
|
||||
position: 'absolute',
|
||||
height: 160,
|
||||
width: 160,
|
||||
},
|
||||
page: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
});
|
||||
|
||||
var HAWK_PIC = {uri: 'https://scontent-sea1-1.xx.fbcdn.net/hphotos-xfa1/t39.1997-6/10734304_1562225620659674_837511701_n.png'};
|
||||
var BUNNY_PIC = {uri: 'https://scontent-sea1-1.xx.fbcdn.net/hphotos-xaf1/t39.1997-6/851564_531111380292237_1898871086_n.png'};
|
||||
|
||||
module.exports = AnExScroll;
|
||||
147
packages/examples/src/RNTester/AnimatedGratuitousApp/AnExSet.js
Normal file
147
packages/examples/src/RNTester/AnimatedGratuitousApp/AnExSet.js
Normal file
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule AnExSet
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
Animated,
|
||||
PanResponder,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
var AnExBobble = require('./AnExBobble');
|
||||
var AnExChained = require('./AnExChained');
|
||||
var AnExScroll = require('./AnExScroll');
|
||||
var AnExTilt = require('./AnExTilt');
|
||||
|
||||
class AnExSet extends React.Component<Object, any> {
|
||||
constructor(props: Object) {
|
||||
super(props);
|
||||
function randColor() {
|
||||
var colors = [0,1,2].map(() => Math.floor(Math.random() * 150 + 100));
|
||||
return 'rgb(' + colors.join(',') + ')';
|
||||
}
|
||||
this.state = {
|
||||
closeColor: randColor(),
|
||||
openColor: randColor(),
|
||||
};
|
||||
}
|
||||
render(): React.Node {
|
||||
var backgroundColor = this.props.openVal ?
|
||||
this.props.openVal.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [
|
||||
this.state.closeColor, // interpolates color strings
|
||||
this.state.openColor
|
||||
],
|
||||
}) :
|
||||
this.state.closeColor;
|
||||
var panelWidth = this.props.containerLayout && this.props.containerLayout.width || 320;
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Animated.View
|
||||
style={[styles.header, { backgroundColor }]}
|
||||
{...this.state.dismissResponder.panHandlers}>
|
||||
<Text style={[styles.text, styles.headerText]}>
|
||||
{this.props.id}
|
||||
</Text>
|
||||
</Animated.View>
|
||||
{this.props.isActive &&
|
||||
<View style={styles.stream}>
|
||||
<View style={styles.card}>
|
||||
<Text style={styles.text}>
|
||||
July 2nd
|
||||
</Text>
|
||||
<AnExTilt isActive={this.props.isActive} />
|
||||
<AnExBobble />
|
||||
</View>
|
||||
<AnExScroll panelWidth={panelWidth}/>
|
||||
<AnExChained />
|
||||
</View>
|
||||
}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
this.state.dismissY = new Animated.Value(0);
|
||||
this.state.dismissResponder = PanResponder.create({
|
||||
onStartShouldSetPanResponder: () => this.props.isActive,
|
||||
onPanResponderGrant: () => {
|
||||
Animated.spring(this.props.openVal, { // Animated value passed in.
|
||||
toValue: this.state.dismissY.interpolate({ // Track dismiss gesture
|
||||
inputRange: [0, 300], // and interpolate pixel distance
|
||||
outputRange: [1, 0], // to a fraction.
|
||||
}),
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
},
|
||||
onPanResponderMove: Animated.event(
|
||||
[null, {dy: this.state.dismissY}], // track pan gesture
|
||||
{useNativeDriver: true}
|
||||
),
|
||||
onPanResponderRelease: (e, gestureState) => {
|
||||
if (gestureState.dy > 100) {
|
||||
this.props.onDismiss(gestureState.vy); // delegates dismiss action to parent
|
||||
} else {
|
||||
Animated.spring(this.props.openVal, {
|
||||
toValue: 1, // animate back open if released early
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
header: {
|
||||
alignItems: 'center',
|
||||
paddingTop: 18,
|
||||
height: 90,
|
||||
},
|
||||
stream: {
|
||||
flex: 1,
|
||||
backgroundColor: 'rgb(230, 230, 230)',
|
||||
},
|
||||
card: {
|
||||
margin: 8,
|
||||
padding: 8,
|
||||
borderRadius: 6,
|
||||
backgroundColor: 'white',
|
||||
shadowRadius: 2,
|
||||
shadowColor: 'black',
|
||||
shadowOpacity: 0.2,
|
||||
shadowOffset: {height: 0.5},
|
||||
},
|
||||
text: {
|
||||
padding: 4,
|
||||
paddingBottom: 10,
|
||||
fontWeight: 'bold',
|
||||
fontSize: 18,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
headerText: {
|
||||
fontSize: 25,
|
||||
color: 'white',
|
||||
shadowRadius: 3,
|
||||
shadowColor: 'black',
|
||||
shadowOpacity: 1,
|
||||
shadowOffset: {height: 1},
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = AnExSet;
|
||||
@@ -0,0 +1,107 @@
|
||||
<br /><br />
|
||||
# React Native: Animated
|
||||
|
||||
ReactEurope 2015, Paris - Spencer Ahrens - Facebook
|
||||
|
||||
<br /><br />
|
||||
|
||||
## Fluid Interactions
|
||||
|
||||
- People expect smooth, delightful experiences
|
||||
- Complex interactions are hard
|
||||
- Common patterns can be optimized
|
||||
|
||||
<br /><br />
|
||||
|
||||
|
||||
## Declarative Interactions
|
||||
|
||||
- Wire up inputs (events) to outputs (props) + transforms (springs, easing, etc.)
|
||||
- Arbitrary code can define/update this config
|
||||
- Config can be serialized -> native/main thread
|
||||
- No refs or lifecycle to worry about
|
||||
|
||||
<br /><br />
|
||||
|
||||
|
||||
## var { Animated } = require('react-native');
|
||||
|
||||
- New library soon to be released for React Native
|
||||
- 100% JS implementation -> X-Platform
|
||||
- Per-platform native optimizations planned
|
||||
- This talk -> usage examples, not implementation
|
||||
|
||||
<br /><br />
|
||||
|
||||
|
||||
## Gratuitous Animation Demo App
|
||||
|
||||
- Layout uses `flexWrap: 'wrap'`
|
||||
- longPress -> drag to reorder
|
||||
- Tap to open example sets
|
||||
|
||||
<br /><br />
|
||||
|
||||
## Gratuitous Animation Codez
|
||||
|
||||
- Step 1: 2D tracking pan gesture
|
||||
- Step 2: Simple pop-out spring on select
|
||||
- Step 3: Animate grid reordering with `LayoutAnimation`
|
||||
- Step 4: Opening animation
|
||||
|
||||
<br /><br />
|
||||
|
||||
## Animation Example Set
|
||||
|
||||
- `Animated.Value` `this.props.open` passed in from parent
|
||||
- `interpolate` works with string "shapes," e.g. `'rgb(0, 0, 255)'`, `'45deg'`
|
||||
- Examples easily composed as separate components
|
||||
- Dismissing tracks interpolated gesture
|
||||
- Custom release logic
|
||||
|
||||
<br /><br />
|
||||
|
||||
|
||||
## Tilting Photo
|
||||
|
||||
- Pan -> translateX * 2, rotate, opacity (via tracking)
|
||||
- Gesture release triggers separate animations
|
||||
- `addListener` for async, arbitrary logic on animation progress
|
||||
- `interpolate` easily creates parallax and other effects
|
||||
|
||||
<br /><br />
|
||||
|
||||
## Bobbles
|
||||
|
||||
- Static positions defined
|
||||
- Listens to events to maybe change selection
|
||||
- Springs previous selection back
|
||||
- New selection tracks selector
|
||||
- `getTranslateTransform` adds convenience
|
||||
|
||||
<br /><br />
|
||||
|
||||
## Chained
|
||||
|
||||
- Classic "Chat Heads" animation
|
||||
- Each sticker tracks the one before it with a soft spring
|
||||
- `decay` maintains gesture velocity, followed by `spring` to home
|
||||
- `stopAnimation` provides the last value for `setOffset`
|
||||
|
||||
<br /><br />
|
||||
|
||||
## Scrolling
|
||||
|
||||
- `Animated.event` can track all sorts of stuff
|
||||
- Multi-part ranges and extrapolation options
|
||||
- Transforms decompose into ordered components
|
||||
|
||||
<br /><br />
|
||||
|
||||
# React Native: Animated
|
||||
|
||||
- Landing soon in master (days)
|
||||
- GitHub: @vjeux, @sahrens
|
||||
- Questions?
|
||||
|
||||
<br />
|
||||
129
packages/examples/src/RNTester/AnimatedGratuitousApp/AnExTilt.js
Normal file
129
packages/examples/src/RNTester/AnimatedGratuitousApp/AnExTilt.js
Normal file
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule AnExTilt
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
Animated,
|
||||
PanResponder,
|
||||
StyleSheet,
|
||||
} = ReactNative;
|
||||
|
||||
class AnExTilt extends React.Component<Object, any> {
|
||||
constructor(props: Object) {
|
||||
super(props);
|
||||
this.state = {
|
||||
panX: new Animated.Value(0),
|
||||
opacity: new Animated.Value(1),
|
||||
burns: new Animated.Value(1.15),
|
||||
};
|
||||
this.state.tiltPanResponder = PanResponder.create({
|
||||
onStartShouldSetPanResponder: () => true,
|
||||
onPanResponderGrant: () => {
|
||||
Animated.timing(this.state.opacity, {
|
||||
toValue: this.state.panX.interpolate({
|
||||
inputRange: [-300, 0, 300], // pan is in pixels
|
||||
outputRange: [0, 1, 0], // goes to zero at both edges
|
||||
}),
|
||||
duration: 0, // direct tracking
|
||||
}).start();
|
||||
},
|
||||
onPanResponderMove: Animated.event(
|
||||
[null, {dx: this.state.panX}] // panX is linked to the gesture
|
||||
),
|
||||
onPanResponderRelease: (e, gestureState) => {
|
||||
var toValue = 0;
|
||||
if (gestureState.dx > 100) {
|
||||
toValue = 500;
|
||||
} else if (gestureState.dx < -100) {
|
||||
toValue = -500;
|
||||
}
|
||||
Animated.spring(this.state.panX, {
|
||||
toValue, // animate back to center or off screen
|
||||
velocity: gestureState.vx, // maintain gesture velocity
|
||||
tension: 10,
|
||||
friction: 3,
|
||||
}).start();
|
||||
this.state.panX.removeAllListeners();
|
||||
var id = this.state.panX.addListener(({value}) => { // listen until offscreen
|
||||
if (Math.abs(value) > 400) {
|
||||
this.state.panX.removeListener(id); // offscreen, so stop listening
|
||||
Animated.timing(this.state.opacity, {
|
||||
toValue: 1, // Fade back in. This unlinks it from tracking this.state.panX
|
||||
}).start();
|
||||
this.state.panX.setValue(0); // Note: stops the spring animation
|
||||
toValue !== 0 && this._startBurnsZoom();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
_startBurnsZoom() {
|
||||
this.state.burns.setValue(1); // reset to beginning
|
||||
Animated.decay(this.state.burns, {
|
||||
velocity: 1, // sublte zoom
|
||||
deceleration: 0.9999, // slow decay
|
||||
}).start();
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
this._startBurnsZoom();
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
return (
|
||||
<Animated.View
|
||||
{...this.state.tiltPanResponder.panHandlers}
|
||||
style={[styles.tilt, {
|
||||
opacity: this.state.opacity,
|
||||
transform: [
|
||||
{rotate: this.state.panX.interpolate({
|
||||
inputRange: [-320, 320],
|
||||
outputRange: ['-15deg', '15deg']})}, // interpolate string "shapes"
|
||||
{translateX: this.state.panX},
|
||||
],
|
||||
}]}>
|
||||
<Animated.Image
|
||||
pointerEvents="none"
|
||||
style={{
|
||||
flex: 1,
|
||||
transform: [
|
||||
{translateX: this.state.panX.interpolate({
|
||||
inputRange: [-3, 3], // small range is extended by default
|
||||
outputRange: [2, -2]}) // parallax
|
||||
},
|
||||
{scale: this.state.burns.interpolate({
|
||||
inputRange: [1, 3000],
|
||||
outputRange: [1, 1.25]}) // simple multiplier
|
||||
},
|
||||
],
|
||||
}}
|
||||
source={require('./trees.jpg')}
|
||||
/>
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
tilt: {
|
||||
overflow: 'hidden',
|
||||
height: 200,
|
||||
marginBottom: 4,
|
||||
backgroundColor: 'rgb(130, 130, 255)',
|
||||
borderColor: 'rgba(0, 0, 0, 0.2)',
|
||||
borderWidth: 1,
|
||||
borderRadius: 20,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = AnExTilt;
|
||||
BIN
packages/examples/src/RNTester/AnimatedGratuitousApp/trees.jpg
Normal file
BIN
packages/examples/src/RNTester/AnimatedGratuitousApp/trees.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 256 KiB |
96
packages/examples/src/RNTester/AppStateExample.js
Normal file
96
packages/examples/src/RNTester/AppStateExample.js
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule AppStateExample
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const {
|
||||
AppState,
|
||||
Text,
|
||||
View
|
||||
} = ReactNative;
|
||||
|
||||
class AppStateSubscription extends React.Component<$FlowFixMeProps, $FlowFixMeState> {
|
||||
state = {
|
||||
appState: AppState.currentState,
|
||||
previousAppStates: [],
|
||||
memoryWarnings: 0,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
AppState.addEventListener('change', this._handleAppStateChange);
|
||||
AppState.addEventListener('memoryWarning', this._handleMemoryWarning);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
AppState.removeEventListener('change', this._handleAppStateChange);
|
||||
AppState.removeEventListener('memoryWarning', this._handleMemoryWarning);
|
||||
}
|
||||
|
||||
_handleMemoryWarning = () => {
|
||||
this.setState({memoryWarnings: this.state.memoryWarnings + 1});
|
||||
};
|
||||
|
||||
_handleAppStateChange = (appState) => {
|
||||
var previousAppStates = this.state.previousAppStates.slice();
|
||||
previousAppStates.push(this.state.appState);
|
||||
this.setState({
|
||||
appState,
|
||||
previousAppStates,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.props.showMemoryWarnings) {
|
||||
return (
|
||||
<View>
|
||||
<Text>{this.state.memoryWarnings}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
if (this.props.showCurrentOnly) {
|
||||
return (
|
||||
<View>
|
||||
<Text>{this.state.appState}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<View>
|
||||
<Text>{JSON.stringify(this.state.previousAppStates)}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
exports.title = 'AppState';
|
||||
exports.description = 'app background status';
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'AppState.currentState',
|
||||
description: 'Can be null on app initialization',
|
||||
render() { return <Text>{AppState.currentState}</Text>; }
|
||||
},
|
||||
{
|
||||
title: 'Subscribed AppState:',
|
||||
description: 'This changes according to the current state, so you can only ever see it rendered as "active"',
|
||||
render(): React.Element<any> { return <AppStateSubscription showCurrentOnly={true} />; }
|
||||
},
|
||||
{
|
||||
title: 'Previous states:',
|
||||
render(): React.Element<any> { return <AppStateSubscription showCurrentOnly={false} />; }
|
||||
},
|
||||
{
|
||||
platform: 'ios',
|
||||
title: 'Memory Warnings',
|
||||
description: 'In the IOS simulator, hit Shift+Command+M to simulate a memory warning.',
|
||||
render(): React.Element<any> { return <AppStateSubscription showMemoryWarnings={true} />; }
|
||||
},
|
||||
];
|
||||
90
packages/examples/src/RNTester/AssetScaledImageExample.js
Normal file
90
packages/examples/src/RNTester/AssetScaledImageExample.js
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule AssetScaledImageExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
Image,
|
||||
StyleSheet,
|
||||
View,
|
||||
ScrollView
|
||||
} = ReactNative;
|
||||
|
||||
class AssetScaledImageExample extends React.Component<$FlowFixMeProps, $FlowFixMeState> {
|
||||
state = {
|
||||
asset: this.props.asset
|
||||
};
|
||||
|
||||
render() {
|
||||
var image = this.state.asset.node.image;
|
||||
return (
|
||||
<ScrollView>
|
||||
<View style={styles.row}>
|
||||
<Image source={image} style={styles.imageWide}/>
|
||||
</View>
|
||||
<View style={styles.row}>
|
||||
<Image source={image} style={styles.imageThumb}/>
|
||||
<Image source={image} style={styles.imageThumb}/>
|
||||
<Image source={image} style={styles.imageThumb}/>
|
||||
</View>
|
||||
<View style={styles.row}>
|
||||
<Image source={image} style={styles.imageT1}/>
|
||||
<Image source={image} style={styles.imageT2}/>
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
row: {
|
||||
padding: 5,
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignSelf: 'center',
|
||||
},
|
||||
textColumn: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
},
|
||||
imageWide: {
|
||||
borderWidth: 1,
|
||||
borderColor: 'black',
|
||||
width: 320,
|
||||
height: 240,
|
||||
margin: 5,
|
||||
},
|
||||
imageThumb: {
|
||||
borderWidth: 1,
|
||||
borderColor: 'black',
|
||||
width: 100,
|
||||
height: 100,
|
||||
margin: 5,
|
||||
},
|
||||
imageT1: {
|
||||
borderWidth: 1,
|
||||
borderColor: 'black',
|
||||
width: 212,
|
||||
height: 320,
|
||||
margin: 5,
|
||||
},
|
||||
imageT2: {
|
||||
borderWidth: 1,
|
||||
borderColor: 'black',
|
||||
width: 100,
|
||||
height: 320,
|
||||
margin: 5,
|
||||
},
|
||||
});
|
||||
|
||||
exports.title = '<AssetScaledImageExample>';
|
||||
exports.description = 'Example component that displays the automatic scaling capabilities of the <Image /> tag';
|
||||
module.exports = AssetScaledImageExample;
|
||||
112
packages/examples/src/RNTester/AsyncStorageExample.js
Normal file
112
packages/examples/src/RNTester/AsyncStorageExample.js
Normal file
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule AsyncStorageExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
AsyncStorage,
|
||||
PickerIOS,
|
||||
Text,
|
||||
View
|
||||
} = ReactNative;
|
||||
var PickerItemIOS = PickerIOS.Item;
|
||||
|
||||
var STORAGE_KEY = '@AsyncStorageExample:key';
|
||||
var COLORS = ['red', 'orange', 'yellow', 'green', 'blue'];
|
||||
|
||||
class BasicStorageExample extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
selectedValue: COLORS[0],
|
||||
messages: [],
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this._loadInitialState()//.done();
|
||||
}
|
||||
|
||||
_loadInitialState = async () => {
|
||||
try {
|
||||
var value = await AsyncStorage.getItem(STORAGE_KEY);
|
||||
if (value !== null){
|
||||
this.setState({selectedValue: value});
|
||||
this._appendMessage('Recovered selection from disk: ' + value);
|
||||
} else {
|
||||
this._appendMessage('Initialized with no selection on disk.');
|
||||
}
|
||||
} catch (error) {
|
||||
this._appendMessage('AsyncStorage error: ' + error.message);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
var color = this.state.selectedValue;
|
||||
return (
|
||||
<View>
|
||||
<PickerIOS
|
||||
selectedValue={color}
|
||||
onValueChange={this._onValueChange}>
|
||||
{COLORS.map((value) => (
|
||||
<PickerItemIOS
|
||||
key={value}
|
||||
value={value}
|
||||
label={value}
|
||||
/>
|
||||
))}
|
||||
</PickerIOS>
|
||||
<Text>
|
||||
{'Selected: '}
|
||||
<Text style={{color}}>
|
||||
{this.state.selectedValue}
|
||||
</Text>
|
||||
</Text>
|
||||
<Text>{' '}</Text>
|
||||
<Text onPress={this._removeStorage}>
|
||||
Press here to remove from storage.
|
||||
</Text>
|
||||
<Text>{' '}</Text>
|
||||
<Text>Messages:</Text>
|
||||
{this.state.messages.map((m) => <Text key={m}>{m}</Text>)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_onValueChange = async (selectedValue) => {
|
||||
this.setState({selectedValue});
|
||||
try {
|
||||
await AsyncStorage.setItem(STORAGE_KEY, selectedValue);
|
||||
this._appendMessage('Saved selection to disk: ' + selectedValue);
|
||||
} catch (error) {
|
||||
this._appendMessage('AsyncStorage error: ' + error.message);
|
||||
}
|
||||
};
|
||||
|
||||
_removeStorage = async () => {
|
||||
try {
|
||||
await AsyncStorage.removeItem(STORAGE_KEY);
|
||||
this._appendMessage('Selection removed from disk.');
|
||||
} catch (error) {
|
||||
this._appendMessage('AsyncStorage error: ' + error.message);
|
||||
}
|
||||
};
|
||||
|
||||
_appendMessage = (message) => {
|
||||
this.setState({messages: this.state.messages.concat(message)});
|
||||
};
|
||||
}
|
||||
|
||||
exports.title = 'AsyncStorage';
|
||||
exports.description = 'Asynchronous local disk storage.';
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'Basics - getItem, setItem, removeItem',
|
||||
render(): React.Element<any> { return <BasicStorageExample />; }
|
||||
},
|
||||
];
|
||||
288
packages/examples/src/RNTester/BorderExample.js
Normal file
288
packages/examples/src/RNTester/BorderExample.js
Normal file
@@ -0,0 +1,288 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule BorderExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
StyleSheet,
|
||||
View
|
||||
} = ReactNative;
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
box: {
|
||||
width: 100,
|
||||
height: 100,
|
||||
},
|
||||
border1: {
|
||||
borderWidth: 10,
|
||||
borderColor: 'brown',
|
||||
},
|
||||
borderRadius: {
|
||||
borderWidth: 10,
|
||||
borderRadius: 10,
|
||||
borderColor: 'cyan',
|
||||
},
|
||||
border2: {
|
||||
borderWidth: 10,
|
||||
borderTopColor: 'red',
|
||||
borderRightColor: 'yellow',
|
||||
borderBottomColor: 'green',
|
||||
borderLeftColor: 'blue',
|
||||
},
|
||||
border3: {
|
||||
borderColor: 'purple',
|
||||
borderTopWidth: 10,
|
||||
borderRightWidth: 20,
|
||||
borderBottomWidth: 30,
|
||||
borderLeftWidth: 40,
|
||||
},
|
||||
border4: {
|
||||
borderTopWidth: 10,
|
||||
borderTopColor: 'red',
|
||||
borderRightWidth: 20,
|
||||
borderRightColor: 'yellow',
|
||||
borderBottomWidth: 30,
|
||||
borderBottomColor: 'green',
|
||||
borderLeftWidth: 40,
|
||||
borderLeftColor: 'blue',
|
||||
},
|
||||
border5: {
|
||||
borderRadius: 50,
|
||||
borderTopWidth: 10,
|
||||
borderTopColor: 'red',
|
||||
borderRightWidth: 20,
|
||||
borderRightColor: 'yellow',
|
||||
borderBottomWidth: 30,
|
||||
borderBottomColor: 'green',
|
||||
borderLeftWidth: 40,
|
||||
borderLeftColor: 'blue',
|
||||
},
|
||||
border6: {
|
||||
borderTopWidth: 10,
|
||||
borderTopColor: 'red',
|
||||
borderRightWidth: 20,
|
||||
borderRightColor: 'yellow',
|
||||
borderBottomWidth: 30,
|
||||
borderBottomColor: 'green',
|
||||
borderLeftWidth: 40,
|
||||
borderLeftColor: 'blue',
|
||||
|
||||
borderTopLeftRadius: 100,
|
||||
},
|
||||
border7: {
|
||||
borderWidth: 10,
|
||||
borderColor: '#f007',
|
||||
borderRadius: 30,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
border7_inner: {
|
||||
backgroundColor: 'blue',
|
||||
width: 100,
|
||||
height: 100
|
||||
},
|
||||
border8: {
|
||||
width: 60,
|
||||
height: 60,
|
||||
borderColor: 'black',
|
||||
marginRight: 10,
|
||||
backgroundColor: 'lightgrey',
|
||||
},
|
||||
border9: {
|
||||
borderWidth: 10,
|
||||
borderTopLeftRadius: 10,
|
||||
borderBottomRightRadius: 20,
|
||||
borderColor: 'black',
|
||||
},
|
||||
border10: {
|
||||
borderWidth: 10,
|
||||
backgroundColor: 'white',
|
||||
borderTopLeftRadius: 10,
|
||||
borderBottomRightRadius: 20,
|
||||
borderColor: 'black',
|
||||
elevation: 10,
|
||||
},
|
||||
border11: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
borderStyle: 'solid',
|
||||
overflow: 'hidden',
|
||||
borderTopWidth: 50,
|
||||
borderRightWidth: 0,
|
||||
borderBottomWidth: 50,
|
||||
borderLeftWidth: 100,
|
||||
borderTopColor: 'transparent',
|
||||
borderRightColor: 'transparent',
|
||||
borderBottomColor: 'transparent',
|
||||
borderLeftColor: 'red',
|
||||
},
|
||||
border12: {
|
||||
borderStyle: 'solid',
|
||||
overflow: 'hidden',
|
||||
borderTopWidth: 10,
|
||||
borderRightWidth: 20,
|
||||
borderBottomWidth: 30,
|
||||
borderLeftWidth: 40,
|
||||
borderRadius: 20,
|
||||
},
|
||||
border13: {
|
||||
borderStyle: 'solid',
|
||||
overflow: 'hidden',
|
||||
borderTopWidth: 10,
|
||||
borderRightWidth: 20,
|
||||
borderBottomWidth: 30,
|
||||
borderLeftWidth: 40,
|
||||
borderTopColor: 'red',
|
||||
borderRightColor: 'green',
|
||||
borderBottomColor: 'blue',
|
||||
borderLeftColor: 'magenta',
|
||||
borderRadius: 20,
|
||||
},
|
||||
border14: {
|
||||
borderStyle: 'solid',
|
||||
overflow: 'hidden',
|
||||
borderTopWidth: 10,
|
||||
borderRightWidth: 20,
|
||||
borderBottomWidth: 30,
|
||||
borderLeftWidth: 40,
|
||||
borderTopColor: 'red',
|
||||
borderRightColor: 'green',
|
||||
borderBottomColor: 'blue',
|
||||
borderLeftColor: 'magenta',
|
||||
borderTopLeftRadius: 10,
|
||||
borderTopRightRadius: 40,
|
||||
borderBottomRightRadius: 30,
|
||||
borderBottomLeftRadius: 40,
|
||||
}
|
||||
});
|
||||
|
||||
exports.title = 'Border';
|
||||
exports.description = 'Demonstrates some of the border styles available to Views.';
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'Equal-Width / Same-Color',
|
||||
description: 'borderWidth & borderColor',
|
||||
render() {
|
||||
return <View style={[styles.box, styles.border1]} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Equal-Width / Same-Color',
|
||||
description: 'borderWidth & borderColor & borderRadius',
|
||||
render() {
|
||||
return <View style={[styles.box, styles.borderRadius]} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Equal-Width Borders',
|
||||
description: 'borderWidth & border*Color',
|
||||
render() {
|
||||
return <View style={[styles.box, styles.border2]} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Same-Color Borders',
|
||||
description: 'border*Width & borderColor',
|
||||
render() {
|
||||
return <View style={[styles.box, styles.border3]} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Custom Borders',
|
||||
description: 'border*Width & border*Color',
|
||||
render() {
|
||||
return <View style={[styles.box, styles.border4]} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Custom Borders',
|
||||
description: 'border*Width & border*Color',
|
||||
// platform: 'ios',
|
||||
render() {
|
||||
return <View style={[styles.box, styles.border5]} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Custom Borders',
|
||||
description: 'border*Width & border*Color',
|
||||
// platform: 'ios',
|
||||
render() {
|
||||
return <View style={[styles.box, styles.border6]} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Custom Borders',
|
||||
description: 'borderRadius & clipping',
|
||||
// platform: 'ios',
|
||||
render() {
|
||||
return (
|
||||
<View style={[styles.box, styles.border7]}>
|
||||
<View style={styles.border7_inner} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Single Borders',
|
||||
description: 'top, left, bottom right',
|
||||
render() {
|
||||
return (
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<View style={[styles.box, styles.border8, {borderTopWidth: 5}]} />
|
||||
<View style={[styles.box, styles.border8, {borderLeftWidth: 5}]} />
|
||||
<View style={[styles.box, styles.border8, {borderBottomWidth: 5}]} />
|
||||
<View style={[styles.box, styles.border8, {borderRightWidth: 5}]} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Corner Radii',
|
||||
description: 'borderTopLeftRadius & borderBottomRightRadius',
|
||||
render() {
|
||||
return <View style={[styles.box, styles.border9]} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Corner Radii / Elevation',
|
||||
description: 'borderTopLeftRadius & borderBottomRightRadius & elevation',
|
||||
platform: 'android',
|
||||
render() {
|
||||
return <View style={[styles.box, styles.border10]} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'CSS Trick - Triangle',
|
||||
description: 'create a triangle by manipulating border colors and widths',
|
||||
render() {
|
||||
return <View style={[styles.border11]} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Curved border(Left|Right|Bottom|Top)Width',
|
||||
description: 'Make a non-uniform width curved border',
|
||||
render() {
|
||||
return <View style={[styles.box, styles.border12]} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Curved border(Left|Right|Bottom|Top)Color',
|
||||
description: 'Make a non-uniform color curved border',
|
||||
render() {
|
||||
return <View style={[styles.box, styles.border13]} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Curved border(Top|Bottom)(Left|Right)Radius',
|
||||
description: 'Make a non-uniform radius curved border',
|
||||
render() {
|
||||
return <View style={[styles.box, styles.border14]} />;
|
||||
}
|
||||
}
|
||||
];
|
||||
81
packages/examples/src/RNTester/BoxShadowExample.js
Normal file
81
packages/examples/src/RNTester/BoxShadowExample.js
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule BoxShadowExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
Image,
|
||||
StyleSheet,
|
||||
View
|
||||
} = ReactNative;
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
box: {
|
||||
width: 100,
|
||||
height: 100,
|
||||
borderWidth: 2,
|
||||
},
|
||||
shadow1: {
|
||||
shadowOpacity: 0.5,
|
||||
shadowRadius: 3,
|
||||
shadowOffset: {width: 2, height: 2},
|
||||
},
|
||||
shadow2: {
|
||||
shadowOpacity: 1.0,
|
||||
shadowColor: 'red',
|
||||
shadowRadius: 0,
|
||||
shadowOffset: {width: 3, height: 3},
|
||||
},
|
||||
});
|
||||
|
||||
exports.title = 'Box Shadow';
|
||||
exports.description = 'Demonstrates some of the shadow styles available to Views.';
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'Basic shadow',
|
||||
description: 'shadowOpacity: 0.5, shadowOffset: {2, 2}',
|
||||
render() {
|
||||
return <View style={[styles.box, styles.shadow1]} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Colored shadow',
|
||||
description: 'shadowColor: \'red\', shadowRadius: 0',
|
||||
render() {
|
||||
return <View style={[styles.box, styles.shadow2]} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Shaped shadow',
|
||||
description: 'borderRadius: 50',
|
||||
render() {
|
||||
return <View style={[styles.box, styles.shadow1, {borderRadius: 50}]} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Image shadow',
|
||||
description: 'Image shadows are derived exactly from the pixels.',
|
||||
render() {
|
||||
return <Image
|
||||
source={require('./hawk.png')}
|
||||
style={[styles.box, styles.shadow1, {borderWidth: 0, overflow: 'visible'}]}
|
||||
/>;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Child shadow',
|
||||
description: 'For views without an opaque background color, shadow will be derived from the subviews.',
|
||||
render() {
|
||||
return <View style={[styles.box, styles.shadow1, {backgroundColor: 'transparent'}]}>
|
||||
<View style={[styles.box, {width: 80, height: 80, borderRadius: 40, margin: 8, backgroundColor: 'red'}]}/>
|
||||
</View>;
|
||||
}
|
||||
},
|
||||
];
|
||||
97
packages/examples/src/RNTester/ButtonExample.js
Normal file
97
packages/examples/src/RNTester/ButtonExample.js
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule ButtonExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const {
|
||||
Alert,
|
||||
Button,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
const onButtonPress = () => {
|
||||
Alert.alert('Button has been pressed!');
|
||||
};
|
||||
|
||||
exports.displayName = 'ButtonExample';
|
||||
exports.framework = 'React';
|
||||
exports.title = '<Button>';
|
||||
exports.description = 'Simple React Native button component.';
|
||||
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'Simple Button',
|
||||
description: 'The title and onPress handler are required. It is ' +
|
||||
'recommended to set accessibilityLabel to help make your app usable by ' +
|
||||
'everyone.',
|
||||
render: function() {
|
||||
return (
|
||||
<Button
|
||||
onPress={onButtonPress}
|
||||
title="Press Me"
|
||||
accessibilityLabel="See an informative alert"
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Adjusted color',
|
||||
description: 'Adjusts the color in a way that looks standard on each ' +
|
||||
'platform. On iOS, the color prop controls the color of the text. On ' +
|
||||
'Android, the color adjusts the background color of the button.',
|
||||
render: function() {
|
||||
return (
|
||||
<Button
|
||||
onPress={onButtonPress}
|
||||
title="Press Purple"
|
||||
color="#841584"
|
||||
accessibilityLabel="Learn more about purple"
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Fit to text layout',
|
||||
description: 'This layout strategy lets the title define the width of ' +
|
||||
'the button',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={{flexDirection: 'row', justifyContent: 'space-between'}}>
|
||||
<Button
|
||||
onPress={onButtonPress}
|
||||
title="This looks great!"
|
||||
accessibilityLabel="This sounds great!"
|
||||
/>
|
||||
<Button
|
||||
onPress={onButtonPress}
|
||||
title="Ok!"
|
||||
color="#841584"
|
||||
accessibilityLabel="Ok, Great!"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Disabled Button',
|
||||
description: 'All interactions for the component are disabled.',
|
||||
render: function() {
|
||||
return (
|
||||
<Button
|
||||
disabled
|
||||
onPress={onButtonPress}
|
||||
title="I Am Disabled"
|
||||
accessibilityLabel="See an informative alert"
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
143
packages/examples/src/RNTester/CameraRollExample.js
Normal file
143
packages/examples/src/RNTester/CameraRollExample.js
Normal file
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule CameraRollExample
|
||||
* @format
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const {
|
||||
CameraRoll,
|
||||
Image,
|
||||
Slider,
|
||||
StyleSheet,
|
||||
Switch,
|
||||
Text,
|
||||
View,
|
||||
TouchableOpacity,
|
||||
} = ReactNative;
|
||||
|
||||
const invariant = require('fbjs/lib/invariant');
|
||||
|
||||
const CameraRollView = require('./CameraRollView');
|
||||
|
||||
const AssetScaledImageExampleView = require('./AssetScaledImageExample');
|
||||
|
||||
class CameraRollExample extends React.Component<
|
||||
$FlowFixMeProps,
|
||||
$FlowFixMeState,
|
||||
> {
|
||||
state = {
|
||||
groupTypes: 'SavedPhotos',
|
||||
sliderValue: 1,
|
||||
bigImages: true,
|
||||
};
|
||||
_cameraRollView: ?CameraRollView;
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Switch
|
||||
onValueChange={this._onSwitchChange}
|
||||
value={this.state.bigImages}
|
||||
/>
|
||||
<Text>{(this.state.bigImages ? 'Big' : 'Small') + ' Images'}</Text>
|
||||
<Slider
|
||||
value={this.state.sliderValue}
|
||||
onValueChange={this._onSliderChange}
|
||||
/>
|
||||
<Text>{'Group Type: ' + this.state.groupTypes}</Text>
|
||||
<CameraRollView
|
||||
ref={ref => {
|
||||
this._cameraRollView = ref;
|
||||
}}
|
||||
batchSize={20}
|
||||
groupTypes={this.state.groupTypes}
|
||||
renderImage={this._renderImage}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
loadAsset(asset) {
|
||||
if (this.props.navigator) {
|
||||
this.props.navigator.push({
|
||||
title: 'Camera Roll Image',
|
||||
component: AssetScaledImageExampleView,
|
||||
backButtonTitle: 'Back',
|
||||
passProps: {asset: asset},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_renderImage = asset => {
|
||||
const imageSize = this.state.bigImages ? 150 : 75;
|
||||
const imageStyle = [styles.image, {width: imageSize, height: imageSize}];
|
||||
const {location} = asset.node;
|
||||
const locationStr = location
|
||||
? JSON.stringify(location)
|
||||
: 'Unknown location';
|
||||
return (
|
||||
<TouchableOpacity key={asset} onPress={this.loadAsset.bind(this, asset)}>
|
||||
<View style={styles.row}>
|
||||
<Image source={asset.node.image} style={imageStyle} />
|
||||
<View style={styles.info}>
|
||||
<Text style={styles.url}>{asset.node.image.uri}</Text>
|
||||
<Text>{locationStr}</Text>
|
||||
<Text>{asset.node.group_name}</Text>
|
||||
<Text>{new Date(asset.node.timestamp).toString()}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
_onSliderChange = value => {
|
||||
const options = Object.keys(CameraRoll.GroupTypesOptions);
|
||||
const index = Math.floor(value * options.length * 0.99);
|
||||
const groupTypes = options[index];
|
||||
if (groupTypes !== this.state.groupTypes) {
|
||||
this.setState({groupTypes: groupTypes});
|
||||
}
|
||||
};
|
||||
|
||||
_onSwitchChange = value => {
|
||||
invariant(this._cameraRollView, 'ref should be set');
|
||||
this._cameraRollView.rendererChanged();
|
||||
this.setState({bigImages: value});
|
||||
};
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
flex: 1,
|
||||
},
|
||||
url: {
|
||||
fontSize: 9,
|
||||
marginBottom: 14,
|
||||
},
|
||||
image: {
|
||||
margin: 4,
|
||||
},
|
||||
info: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
exports.title = 'Camera Roll';
|
||||
exports.description =
|
||||
"Example component that uses CameraRoll to list user's photos";
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'Photos',
|
||||
render(): React.Element<any> {
|
||||
return <CameraRollExample />;
|
||||
},
|
||||
},
|
||||
];
|
||||
281
packages/examples/src/RNTester/CameraRollView.js
Normal file
281
packages/examples/src/RNTester/CameraRollView.js
Normal file
@@ -0,0 +1,281 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule CameraRollView
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var createReactClass = require('create-react-class');
|
||||
const PropTypes = require('prop-types');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
ActivityIndicator,
|
||||
Alert,
|
||||
CameraRoll,
|
||||
Image,
|
||||
ListView,
|
||||
PermissionsAndroid,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
var groupByEveryN = require('groupByEveryN');
|
||||
var logError = require('logError');
|
||||
|
||||
var propTypes = {
|
||||
/**
|
||||
* The group where the photos will be fetched from. Possible
|
||||
* values are 'Album', 'All', 'Event', 'Faces', 'Library', 'PhotoStream'
|
||||
* and SavedPhotos.
|
||||
*/
|
||||
groupTypes: PropTypes.oneOf([
|
||||
'Album',
|
||||
'All',
|
||||
'Event',
|
||||
'Faces',
|
||||
'Library',
|
||||
'PhotoStream',
|
||||
'SavedPhotos',
|
||||
]),
|
||||
|
||||
/**
|
||||
* Number of images that will be fetched in one page.
|
||||
*/
|
||||
batchSize: PropTypes.number,
|
||||
|
||||
/**
|
||||
* A function that takes a single image as a parameter and renders it.
|
||||
*/
|
||||
renderImage: PropTypes.func,
|
||||
|
||||
/**
|
||||
* imagesPerRow: Number of images to be shown in each row.
|
||||
*/
|
||||
imagesPerRow: PropTypes.number,
|
||||
|
||||
/**
|
||||
* The asset type, one of 'Photos', 'Videos' or 'All'
|
||||
*/
|
||||
assetType: PropTypes.oneOf([
|
||||
'Photos',
|
||||
'Videos',
|
||||
'All',
|
||||
]),
|
||||
|
||||
};
|
||||
|
||||
var CameraRollView = createReactClass({
|
||||
displayName: 'CameraRollView',
|
||||
propTypes: propTypes,
|
||||
|
||||
getDefaultProps: function(): Object {
|
||||
return {
|
||||
groupTypes: 'SavedPhotos',
|
||||
batchSize: 5,
|
||||
imagesPerRow: 1,
|
||||
assetType: 'Photos',
|
||||
renderImage: function(asset) {
|
||||
var imageSize = 150;
|
||||
var imageStyle = [styles.image, {width: imageSize, height: imageSize}];
|
||||
return (
|
||||
<Image
|
||||
source={asset.node.image}
|
||||
style={imageStyle}
|
||||
/>
|
||||
);
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
var ds = new ListView.DataSource({rowHasChanged: this._rowHasChanged});
|
||||
|
||||
return {
|
||||
assets: ([]: Array<Image>),
|
||||
groupTypes: this.props.groupTypes,
|
||||
lastCursor: (null : ?string),
|
||||
assetType: this.props.assetType,
|
||||
noMore: false,
|
||||
loadingMore: false,
|
||||
dataSource: ds,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* This should be called when the image renderer is changed to tell the
|
||||
* component to re-render its assets.
|
||||
*/
|
||||
rendererChanged: function() {
|
||||
var ds = new ListView.DataSource({rowHasChanged: this._rowHasChanged});
|
||||
this.state.dataSource = ds.cloneWithRows(
|
||||
// $FlowFixMe(>=0.41.0)
|
||||
groupByEveryN(this.state.assets, this.props.imagesPerRow)
|
||||
);
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.fetch();
|
||||
},
|
||||
|
||||
UNSAFE_componentWillReceiveProps: function(nextProps: {groupTypes?: string}) {
|
||||
if (this.props.groupTypes !== nextProps.groupTypes) {
|
||||
this.fetch(true);
|
||||
}
|
||||
},
|
||||
|
||||
_fetch: async function(clear?: boolean) {
|
||||
if (clear) {
|
||||
this.setState(this.getInitialState(), this.fetch);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
const result = await PermissionsAndroid.request(
|
||||
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
|
||||
{
|
||||
title: 'Permission Explanation',
|
||||
message: 'RNTester would like to access your pictures.',
|
||||
},
|
||||
);
|
||||
if (result !== 'granted') {
|
||||
Alert.alert('Access to pictures was denied.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const fetchParams: Object = {
|
||||
first: this.props.batchSize,
|
||||
groupTypes: this.props.groupTypes,
|
||||
assetType: this.props.assetType,
|
||||
};
|
||||
if (Platform.OS === 'android') {
|
||||
// not supported in android
|
||||
delete fetchParams.groupTypes;
|
||||
}
|
||||
if (this.state.lastCursor) {
|
||||
fetchParams.after = this.state.lastCursor;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await CameraRoll.getPhotos(fetchParams);
|
||||
this._appendAssets(data);
|
||||
} catch (e) {
|
||||
logError(e);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetches more images from the camera roll. If clear is set to true, it will
|
||||
* set the component to its initial state and re-fetch the images.
|
||||
*/
|
||||
fetch: function(clear?: boolean) {
|
||||
if (!this.state.loadingMore) {
|
||||
this.setState({loadingMore: true}, () => { this._fetch(clear); });
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<ListView
|
||||
renderRow={this._renderRow}
|
||||
renderFooter={this._renderFooterSpinner}
|
||||
onEndReached={this._onEndReached}
|
||||
style={styles.container}
|
||||
dataSource={this.state.dataSource}
|
||||
enableEmptySections
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
_rowHasChanged: function(r1: Array<Image>, r2: Array<Image>): boolean {
|
||||
if (r1.length !== r2.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (var i = 0; i < r1.length; i++) {
|
||||
if (r1[i] !== r2[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_renderFooterSpinner: function() {
|
||||
if (!this.state.noMore) {
|
||||
return <ActivityIndicator />;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
// rowData is an array of images
|
||||
_renderRow: function(rowData: Array<Image>, sectionID: string, rowID: string) {
|
||||
var images = rowData.map((image) => {
|
||||
if (image === null) {
|
||||
return null;
|
||||
}
|
||||
// $FlowFixMe(>=0.41.0)
|
||||
return this.props.renderImage(image);
|
||||
});
|
||||
|
||||
return (
|
||||
<View style={styles.row}>
|
||||
{images}
|
||||
</View>
|
||||
);
|
||||
},
|
||||
|
||||
_appendAssets: function(data: Object) {
|
||||
var assets = data.edges;
|
||||
var newState: Object = { loadingMore: false };
|
||||
|
||||
if (!data.page_info.has_next_page) {
|
||||
newState.noMore = true;
|
||||
}
|
||||
|
||||
if (assets.length > 0) {
|
||||
newState.lastCursor = data.page_info.end_cursor;
|
||||
newState.assets = this.state.assets.concat(assets);
|
||||
newState.dataSource = this.state.dataSource.cloneWithRows(
|
||||
// $FlowFixMe(>=0.41.0)
|
||||
groupByEveryN(newState.assets, this.props.imagesPerRow)
|
||||
);
|
||||
}
|
||||
|
||||
this.setState(newState);
|
||||
},
|
||||
|
||||
_onEndReached: function() {
|
||||
if (!this.state.noMore) {
|
||||
this.fetch();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
flex: 1,
|
||||
},
|
||||
url: {
|
||||
fontSize: 9,
|
||||
marginBottom: 14,
|
||||
},
|
||||
image: {
|
||||
margin: 4,
|
||||
},
|
||||
info: {
|
||||
flex: 1,
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = CameraRollView;
|
||||
125
packages/examples/src/RNTester/CheckBoxExample.js
Normal file
125
packages/examples/src/RNTester/CheckBoxExample.js
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule CheckBoxExample
|
||||
* @format
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const {CheckBox, Text, View} = ReactNative;
|
||||
|
||||
class BasicCheckBoxExample extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
trueCheckBoxIsOn: true,
|
||||
falseCheckBoxIsOn: false,
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<CheckBox
|
||||
onValueChange={value => this.setState({falseCheckBoxIsOn: value})}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.falseCheckBoxIsOn}
|
||||
/>
|
||||
<CheckBox
|
||||
onValueChange={value => this.setState({trueCheckBoxIsOn: value})}
|
||||
value={this.state.trueCheckBoxIsOn}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisabledCheckBoxExample extends React.Component<{}, $FlowFixMeState> {
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<CheckBox disabled={true} style={{marginBottom: 10}} value={true} />
|
||||
<CheckBox disabled={true} value={false} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EventCheckBoxExample extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
eventCheckBoxIsOn: false,
|
||||
eventCheckBoxRegressionIsOn: true,
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={{flexDirection: 'row', justifyContent: 'space-around'}}>
|
||||
<View>
|
||||
<CheckBox
|
||||
onValueChange={value => this.setState({eventCheckBoxIsOn: value})}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.eventCheckBoxIsOn}
|
||||
/>
|
||||
<CheckBox
|
||||
onValueChange={value => this.setState({eventCheckBoxIsOn: value})}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.eventCheckBoxIsOn}
|
||||
/>
|
||||
<Text>{this.state.eventCheckBoxIsOn ? 'On' : 'Off'}</Text>
|
||||
</View>
|
||||
<View>
|
||||
<CheckBox
|
||||
onValueChange={value =>
|
||||
this.setState({eventCheckBoxRegressionIsOn: value})
|
||||
}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.eventCheckBoxRegressionIsOn}
|
||||
/>
|
||||
<CheckBox
|
||||
onValueChange={value =>
|
||||
this.setState({eventCheckBoxRegressionIsOn: value})
|
||||
}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.eventCheckBoxRegressionIsOn}
|
||||
/>
|
||||
<Text>{this.state.eventCheckBoxRegressionIsOn ? 'On' : 'Off'}</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let examples = [
|
||||
{
|
||||
title: 'CheckBoxes can be set to true or false',
|
||||
render(): React.Element<any> {
|
||||
return <BasicCheckBoxExample />;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'CheckBoxes can be disabled',
|
||||
render(): React.Element<any> {
|
||||
return <DisabledCheckBoxExample />;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Change events can be detected',
|
||||
render(): React.Element<any> {
|
||||
return <EventCheckBoxExample />;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'CheckBoxes are controlled components',
|
||||
render(): React.Element<any> {
|
||||
return <CheckBox />;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
exports.title = '<CheckBox>';
|
||||
exports.displayName = 'CheckBoxExample';
|
||||
exports.description = 'Native boolean input';
|
||||
exports.examples = examples;
|
||||
58
packages/examples/src/RNTester/ClipboardExample.js
Normal file
58
packages/examples/src/RNTester/ClipboardExample.js
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule ClipboardExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
Clipboard,
|
||||
View,
|
||||
Text,
|
||||
} = ReactNative;
|
||||
|
||||
class ClipboardExample extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
content: 'Content will appear here'
|
||||
};
|
||||
|
||||
_setClipboardContent = async () => {
|
||||
Clipboard.setString('Hello World');
|
||||
try {
|
||||
var content = await Clipboard.getString();
|
||||
this.setState({content});
|
||||
} catch (e) {
|
||||
this.setState({content:e.message});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Text onPress={this._setClipboardContent} style={{color: 'blue'}}>
|
||||
Tap to put "Hello World" in the clipboard
|
||||
</Text>
|
||||
<Text style={{color: 'red', marginTop: 20}}>
|
||||
{this.state.content}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
exports.title = 'Clipboard';
|
||||
exports.description = 'Show Clipboard contents.';
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'Clipboard.setString() and getString()',
|
||||
render() {
|
||||
return <ClipboardExample/>;
|
||||
}
|
||||
}
|
||||
];
|
||||
133
packages/examples/src/RNTester/DatePickerAndroidExample.js
Normal file
133
packages/examples/src/RNTester/DatePickerAndroidExample.js
Normal file
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule DatePickerAndroidExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
DatePickerAndroid,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableWithoutFeedback,
|
||||
} = ReactNative;
|
||||
|
||||
var RNTesterBlock = require('./RNTesterBlock');
|
||||
var RNTesterPage = require('./RNTesterPage');
|
||||
|
||||
class DatePickerAndroidExample extends React.Component {
|
||||
static title = 'DatePickerAndroid';
|
||||
static description = 'Standard Android date picker dialog';
|
||||
|
||||
state = {
|
||||
presetDate: new Date(2020, 4, 5),
|
||||
simpleDate: new Date(2020, 4, 5),
|
||||
spinnerDate: new Date(2020, 4, 5),
|
||||
calendarDate: new Date(2020, 4, 5),
|
||||
defaultDate: new Date(2020, 4, 5),
|
||||
allDate: new Date(2020, 4, 5),
|
||||
simpleText: 'pick a date',
|
||||
spinnerText: 'pick a date',
|
||||
calendarText: 'pick a date',
|
||||
defaultText: 'pick a date',
|
||||
minText: 'pick a date, no earlier than today',
|
||||
maxText: 'pick a date, no later than today',
|
||||
presetText: 'pick a date, preset to 2020/5/5',
|
||||
allText: 'pick a date between 2020/5/1 and 2020/5/10',
|
||||
};
|
||||
|
||||
showPicker = async (stateKey, options) => {
|
||||
try {
|
||||
var newState = {};
|
||||
const {action, year, month, day} = await DatePickerAndroid.open(options);
|
||||
if (action === DatePickerAndroid.dismissedAction) {
|
||||
newState[stateKey + 'Text'] = 'dismissed';
|
||||
} else {
|
||||
var date = new Date(year, month, day);
|
||||
newState[stateKey + 'Text'] = date.toLocaleDateString();
|
||||
newState[stateKey + 'Date'] = date;
|
||||
}
|
||||
this.setState(newState);
|
||||
} catch ({code, message}) {
|
||||
console.warn(`Error in example '${stateKey}': `, message);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<RNTesterPage title="DatePickerAndroid">
|
||||
<RNTesterBlock title="Simple date picker">
|
||||
<TouchableWithoutFeedback
|
||||
onPress={this.showPicker.bind(this, 'simple', {date: this.state.simpleDate})}>
|
||||
<Text style={styles.text}>{this.state.simpleText}</Text>
|
||||
</TouchableWithoutFeedback>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title="Simple spinner date picker">
|
||||
<TouchableWithoutFeedback
|
||||
onPress={this.showPicker.bind(this, 'spinner', {date: this.state.spinnerDate, mode: 'spinner'})}>
|
||||
<Text style={styles.text}>{this.state.spinnerText}</Text>
|
||||
</TouchableWithoutFeedback>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title="Simple calendar date picker">
|
||||
<TouchableWithoutFeedback
|
||||
onPress={this.showPicker.bind(this, 'calendar', {date: this.state.calendarDate, mode: 'calendar'})}>
|
||||
<Text style={styles.text}>{this.state.calendarText}</Text>
|
||||
</TouchableWithoutFeedback>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title="Simple default date picker">
|
||||
<TouchableWithoutFeedback
|
||||
onPress={this.showPicker.bind(this, 'default', {date: this.state.defaultDate, mode: 'default'})}>
|
||||
<Text style={styles.text}>{this.state.defaultText}</Text>
|
||||
</TouchableWithoutFeedback>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title="Date picker with pre-set date">
|
||||
<TouchableWithoutFeedback
|
||||
onPress={this.showPicker.bind(this, 'preset', {date: this.state.presetDate})}>
|
||||
<Text style={styles.text}>{this.state.presetText}</Text>
|
||||
</TouchableWithoutFeedback>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title="Date picker with minDate">
|
||||
<TouchableWithoutFeedback
|
||||
onPress={this.showPicker.bind(this, 'min', {
|
||||
date: this.state.minDate,
|
||||
minDate: new Date(),
|
||||
})}>
|
||||
<Text style={styles.text}>{this.state.minText}</Text>
|
||||
</TouchableWithoutFeedback>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title="Date picker with maxDate">
|
||||
<TouchableWithoutFeedback
|
||||
onPress={this.showPicker.bind(this, 'max', {
|
||||
date: this.state.maxDate,
|
||||
maxDate: new Date(),
|
||||
})}>
|
||||
<Text style={styles.text}>{this.state.maxText}</Text>
|
||||
</TouchableWithoutFeedback>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title="Date picker with all options">
|
||||
<TouchableWithoutFeedback
|
||||
onPress={this.showPicker.bind(this, 'all', {
|
||||
date: this.state.allDate,
|
||||
minDate: new Date(2020, 4, 1),
|
||||
maxDate: new Date(2020, 4, 10),
|
||||
})}>
|
||||
<Text style={styles.text}>{this.state.allText}</Text>
|
||||
</TouchableWithoutFeedback>
|
||||
</RNTesterBlock>
|
||||
</RNTesterPage>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
text: {
|
||||
color: 'black',
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = DatePickerAndroidExample;
|
||||
159
packages/examples/src/RNTester/DatePickerIOSExample.js
Normal file
159
packages/examples/src/RNTester/DatePickerIOSExample.js
Normal file
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule DatePickerIOSExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
DatePickerIOS,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
class DatePickerExample extends React.Component<$FlowFixMeProps, $FlowFixMeState> {
|
||||
static defaultProps = {
|
||||
date: new Date(),
|
||||
timeZoneOffsetInHours: (-1) * (new Date()).getTimezoneOffset() / 60,
|
||||
};
|
||||
|
||||
state = {
|
||||
date: this.props.date,
|
||||
timeZoneOffsetInHours: this.props.timeZoneOffsetInHours,
|
||||
};
|
||||
|
||||
onDateChange = (date) => {
|
||||
this.setState({date: date});
|
||||
};
|
||||
|
||||
onTimezoneChange = (event) => {
|
||||
var offset = parseInt(event.nativeEvent.text, 10);
|
||||
if (isNaN(offset)) {
|
||||
return;
|
||||
}
|
||||
this.setState({timeZoneOffsetInHours: offset});
|
||||
};
|
||||
|
||||
render() {
|
||||
// Ideally, the timezone input would be a picker rather than a
|
||||
// text input, but we don't have any pickers yet :(
|
||||
return (
|
||||
<View>
|
||||
<WithLabel label="Value:">
|
||||
<Text>{
|
||||
this.state.date.toLocaleDateString() +
|
||||
' ' +
|
||||
this.state.date.toLocaleTimeString()
|
||||
}</Text>
|
||||
</WithLabel>
|
||||
<WithLabel label="Timezone:">
|
||||
<TextInput
|
||||
onChange={this.onTimezoneChange}
|
||||
style={styles.textinput}
|
||||
value={this.state.timeZoneOffsetInHours.toString()}
|
||||
/>
|
||||
<Text> hours from UTC</Text>
|
||||
</WithLabel>
|
||||
<Heading label="Date + time picker" />
|
||||
<DatePickerIOS
|
||||
date={this.state.date}
|
||||
mode="datetime"
|
||||
timeZoneOffsetInMinutes={this.state.timeZoneOffsetInHours * 60}
|
||||
onDateChange={this.onDateChange}
|
||||
/>
|
||||
<Heading label="Date picker" />
|
||||
<DatePickerIOS
|
||||
date={this.state.date}
|
||||
mode="date"
|
||||
timeZoneOffsetInMinutes={this.state.timeZoneOffsetInHours * 60}
|
||||
onDateChange={this.onDateChange}
|
||||
/>
|
||||
<Heading label="Time picker, 10-minute interval" />
|
||||
<DatePickerIOS
|
||||
date={this.state.date}
|
||||
mode="time"
|
||||
timeZoneOffsetInMinutes={this.state.timeZoneOffsetInHours * 60}
|
||||
onDateChange={this.onDateChange}
|
||||
minuteInterval={10}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class WithLabel extends React.Component<$FlowFixMeProps> {
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.labelContainer}>
|
||||
<View style={styles.labelView}>
|
||||
<Text style={styles.label}>
|
||||
{this.props.label}
|
||||
</Text>
|
||||
</View>
|
||||
{this.props.children}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Heading extends React.Component<$FlowFixMeProps> {
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.headingContainer}>
|
||||
<Text style={styles.heading}>
|
||||
{this.props.label}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
exports.displayName = (undefined: ?string);
|
||||
exports.title = '<DatePickerIOS>';
|
||||
exports.description = 'Select dates and times using the native UIDatePicker.';
|
||||
exports.examples = [
|
||||
{
|
||||
title: '<DatePickerIOS>',
|
||||
render: function(): React.Element<any> {
|
||||
return <DatePickerExample />;
|
||||
},
|
||||
}];
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
textinput: {
|
||||
height: 26,
|
||||
width: 50,
|
||||
borderWidth: 0.5,
|
||||
borderColor: '#0f0f0f',
|
||||
padding: 4,
|
||||
fontSize: 13,
|
||||
},
|
||||
labelContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginVertical: 2,
|
||||
},
|
||||
labelView: {
|
||||
marginRight: 10,
|
||||
paddingVertical: 2,
|
||||
},
|
||||
label: {
|
||||
fontWeight: '500',
|
||||
},
|
||||
headingContainer: {
|
||||
padding: 4,
|
||||
backgroundColor: '#f6f7f8',
|
||||
},
|
||||
heading: {
|
||||
fontWeight: '500',
|
||||
fontSize: 14,
|
||||
},
|
||||
});
|
||||
59
packages/examples/src/RNTester/DimensionsExample.js
Normal file
59
packages/examples/src/RNTester/DimensionsExample.js
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule DimensionsExample
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const {
|
||||
Dimensions,
|
||||
Text,
|
||||
View
|
||||
} = ReactNative;
|
||||
|
||||
class DimensionsSubscription extends React.Component<{dim: string}, {dims: Object}> {
|
||||
state = {
|
||||
dims: Dimensions.get(this.props.dim),
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
Dimensions.addEventListener('change', this._handleDimensionsChange);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
Dimensions.removeEventListener('change', this._handleDimensionsChange);
|
||||
}
|
||||
|
||||
_handleDimensionsChange = (dimensions) => {
|
||||
this.setState({
|
||||
dims: dimensions[this.props.dim],
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Text>{JSON.stringify(this.state.dims)}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
exports.title = 'Dimensions';
|
||||
exports.description = 'Dimensions of the viewport';
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'window',
|
||||
render(): React.Element<any> { return <DimensionsSubscription dim="window" />; }
|
||||
},
|
||||
{
|
||||
title: 'screen',
|
||||
render(): React.Element<any> { return <DimensionsSubscription dim="screen" />; }
|
||||
},
|
||||
];
|
||||
28
packages/examples/src/RNTester/ExampleTypes.js
Normal file
28
packages/examples/src/RNTester/ExampleTypes.js
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule ExampleTypes
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
/* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This comment
|
||||
* suppresses an error when upgrading Flow's support for React. To see the
|
||||
* error delete this comment and run Flow. */
|
||||
import type React from 'react';
|
||||
|
||||
export type Example = {
|
||||
title: string,
|
||||
render: () => ?React.Element<any>,
|
||||
description?: string,
|
||||
platform?: string,
|
||||
};
|
||||
|
||||
export type ExampleModule = {
|
||||
title: string,
|
||||
description: string,
|
||||
examples: Array<Example>,
|
||||
};
|
||||
224
packages/examples/src/RNTester/FlatListExample.js
Normal file
224
packages/examples/src/RNTester/FlatListExample.js
Normal file
@@ -0,0 +1,224 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule FlatListExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const Alert = require('Alert');
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const {
|
||||
Animated,
|
||||
FlatList,
|
||||
StyleSheet,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
const RNTesterPage = require('./RNTesterPage');
|
||||
|
||||
const infoLog = require('infoLog');
|
||||
|
||||
const {
|
||||
FooterComponent,
|
||||
HeaderComponent,
|
||||
ItemComponent,
|
||||
ItemSeparatorComponent,
|
||||
PlainInput,
|
||||
SeparatorComponent,
|
||||
Spindicator,
|
||||
genItemData,
|
||||
getItemLayout,
|
||||
pressItem,
|
||||
renderSmallSwitchOption,
|
||||
} = require('./ListExampleShared');
|
||||
|
||||
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
|
||||
|
||||
const VIEWABILITY_CONFIG = {
|
||||
minimumViewTime: 3000,
|
||||
viewAreaCoveragePercentThreshold: 100,
|
||||
waitForInteraction: true,
|
||||
};
|
||||
|
||||
class FlatListExample extends React.PureComponent<{}, $FlowFixMeState> {
|
||||
static title = '<FlatList>';
|
||||
static description = 'Performant, scrollable list of data.';
|
||||
|
||||
state = {
|
||||
data: genItemData(100),
|
||||
debug: false,
|
||||
horizontal: false,
|
||||
inverted: false,
|
||||
filterText: '',
|
||||
fixedHeight: true,
|
||||
logViewable: false,
|
||||
virtualized: true,
|
||||
};
|
||||
|
||||
_onChangeFilterText = (filterText) => {
|
||||
this.setState({filterText});
|
||||
};
|
||||
|
||||
_onChangeScrollToIndex = (text) => {
|
||||
this._listRef.getNode().scrollToIndex({viewPosition: 0.5, index: Number(text)});
|
||||
};
|
||||
|
||||
_scrollPos = new Animated.Value(0);
|
||||
_scrollSinkX = Animated.event(
|
||||
[{nativeEvent: { contentOffset: { x: this._scrollPos } }}],
|
||||
{useNativeDriver: true},
|
||||
);
|
||||
_scrollSinkY = Animated.event(
|
||||
[{nativeEvent: { contentOffset: { y: this._scrollPos } }}],
|
||||
{useNativeDriver: true},
|
||||
);
|
||||
|
||||
componentDidUpdate() {
|
||||
this._listRef.getNode().recordInteraction(); // e.g. flipping logViewable switch
|
||||
}
|
||||
|
||||
render() {
|
||||
const filterRegex = new RegExp(String(this.state.filterText), 'i');
|
||||
const filter = (item) => (
|
||||
filterRegex.test(item.text) || filterRegex.test(item.title)
|
||||
);
|
||||
const filteredData = this.state.data.filter(filter);
|
||||
return (
|
||||
<RNTesterPage
|
||||
noSpacer={true}
|
||||
noScroll={true}>
|
||||
<View style={styles.container}>
|
||||
<View style={styles.searchRow}>
|
||||
<View style={styles.options}>
|
||||
<PlainInput
|
||||
onChangeText={this._onChangeFilterText}
|
||||
placeholder="Search..."
|
||||
value={this.state.filterText}
|
||||
/>
|
||||
<PlainInput
|
||||
onChangeText={this._onChangeScrollToIndex}
|
||||
placeholder="scrollToIndex..."
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.options}>
|
||||
{renderSmallSwitchOption(this, 'virtualized')}
|
||||
{renderSmallSwitchOption(this, 'horizontal')}
|
||||
{renderSmallSwitchOption(this, 'fixedHeight')}
|
||||
{renderSmallSwitchOption(this, 'logViewable')}
|
||||
{renderSmallSwitchOption(this, 'inverted')}
|
||||
{renderSmallSwitchOption(this, 'debug')}
|
||||
<Spindicator value={this._scrollPos} />
|
||||
</View>
|
||||
</View>
|
||||
<SeparatorComponent />
|
||||
<AnimatedFlatList
|
||||
ItemSeparatorComponent={ItemSeparatorComponent}
|
||||
ListHeaderComponent={<HeaderComponent />}
|
||||
ListFooterComponent={FooterComponent}
|
||||
data={filteredData}
|
||||
debug={this.state.debug}
|
||||
disableVirtualization={!this.state.virtualized}
|
||||
getItemLayout={this.state.fixedHeight ?
|
||||
this._getItemLayout :
|
||||
undefined
|
||||
}
|
||||
horizontal={this.state.horizontal}
|
||||
inverted={this.state.inverted}
|
||||
key={(this.state.horizontal ? 'h' : 'v') +
|
||||
(this.state.fixedHeight ? 'f' : 'd')
|
||||
}
|
||||
keyboardShouldPersistTaps="always"
|
||||
keyboardDismissMode="on-drag"
|
||||
legacyImplementation={false}
|
||||
numColumns={1}
|
||||
onEndReached={this._onEndReached}
|
||||
onRefresh={this._onRefresh}
|
||||
onScroll={this.state.horizontal ? this._scrollSinkX : this._scrollSinkY}
|
||||
onViewableItemsChanged={this._onViewableItemsChanged}
|
||||
ref={this._captureRef}
|
||||
refreshing={false}
|
||||
renderItem={this._renderItemComponent}
|
||||
contentContainerStyle={styles.list}
|
||||
viewabilityConfig={VIEWABILITY_CONFIG}
|
||||
/>
|
||||
</View>
|
||||
</RNTesterPage>
|
||||
);
|
||||
}
|
||||
_captureRef = (ref) => { this._listRef = ref; };
|
||||
_getItemLayout = (data: any, index: number) => {
|
||||
return getItemLayout(data, index, this.state.horizontal);
|
||||
};
|
||||
_onEndReached = () => {
|
||||
if (this.state.data.length >= 1000) {
|
||||
return;
|
||||
}
|
||||
this.setState((state) => ({
|
||||
data: state.data.concat(genItemData(100, state.data.length)),
|
||||
}));
|
||||
};
|
||||
_onRefresh = () => Alert.alert('onRefresh: nothing to refresh :P');
|
||||
_renderItemComponent = ({item, separators}) => {
|
||||
return (
|
||||
<ItemComponent
|
||||
item={item}
|
||||
horizontal={this.state.horizontal}
|
||||
fixedHeight={this.state.fixedHeight}
|
||||
onPress={this._pressItem}
|
||||
onShowUnderlay={separators.highlight}
|
||||
onHideUnderlay={separators.unhighlight}
|
||||
/>
|
||||
);
|
||||
};
|
||||
// This is called when items change viewability by scrolling into or out of
|
||||
// the viewable area.
|
||||
_onViewableItemsChanged = (info: {
|
||||
changed: Array<{
|
||||
key: string,
|
||||
isViewable: boolean,
|
||||
item: any,
|
||||
index: ?number,
|
||||
section?: any,
|
||||
}>
|
||||
}
|
||||
) => {
|
||||
// Impressions can be logged here
|
||||
if (this.state.logViewable) {
|
||||
infoLog(
|
||||
'onViewableItemsChanged: ',
|
||||
info.changed.map((v) => ({...v, item: '...'})),
|
||||
);
|
||||
}
|
||||
};
|
||||
_pressItem = (key: string) => {
|
||||
this._listRef.getNode().recordInteraction();
|
||||
pressItem(this, key);
|
||||
};
|
||||
_listRef: AnimatedFlatList;
|
||||
}
|
||||
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: 'rgb(239, 239, 244)',
|
||||
flex: 1,
|
||||
},
|
||||
list: {
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
options: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
alignItems: 'center',
|
||||
},
|
||||
searchRow: {
|
||||
paddingHorizontal: 10,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = FlatListExample;
|
||||
81
packages/examples/src/RNTester/GeolocationExample.js
Normal file
81
packages/examples/src/RNTester/GeolocationExample.js
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule GeolocationExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
exports.framework = 'React';
|
||||
exports.title = 'Geolocation';
|
||||
exports.description = 'Examples of using the Geolocation API.';
|
||||
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'navigator.geolocation',
|
||||
render: function(): React.Element<any> {
|
||||
return <GeolocationExample />;
|
||||
},
|
||||
}
|
||||
];
|
||||
|
||||
class GeolocationExample extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
initialPosition: 'unknown',
|
||||
lastPosition: 'unknown',
|
||||
};
|
||||
|
||||
watchID: ?number = null;
|
||||
|
||||
componentDidMount() {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(position) => {
|
||||
var initialPosition = JSON.stringify(position);
|
||||
this.setState({initialPosition});
|
||||
},
|
||||
(error) => alert(JSON.stringify(error)),
|
||||
{enableHighAccuracy: true, timeout: 20000, maximumAge: 1000}
|
||||
);
|
||||
this.watchID = navigator.geolocation.watchPosition((position) => {
|
||||
var lastPosition = JSON.stringify(position);
|
||||
this.setState({lastPosition});
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.watchID != null && navigator.geolocation.clearWatch(this.watchID);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Text>
|
||||
<Text style={styles.title}>Initial position: </Text>
|
||||
{this.state.initialPosition}
|
||||
</Text>
|
||||
<Text>
|
||||
<Text style={styles.title}>Current position: </Text>
|
||||
{this.state.lastPosition}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
title: {
|
||||
fontWeight: '500',
|
||||
},
|
||||
});
|
||||
81
packages/examples/src/RNTester/ImageCapInsetsExample.js
Normal file
81
packages/examples/src/RNTester/ImageCapInsetsExample.js
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule ImageCapInsetsExample
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
|
||||
var nativeImageSource = require('nativeImageSource');
|
||||
var {
|
||||
Image,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
class ImageCapInsetsExample extends React.Component<{}> {
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<View style={styles.background}>
|
||||
<Text>
|
||||
capInsets: none
|
||||
</Text>
|
||||
<Image
|
||||
source={nativeImageSource({
|
||||
ios: 'story-background',
|
||||
width: 60,
|
||||
height: 60
|
||||
})}
|
||||
style={styles.storyBackground}
|
||||
resizeMode={Image.resizeMode.stretch}
|
||||
capInsets={{left: 0, right: 0, bottom: 0, top: 0}}
|
||||
/>
|
||||
</View>
|
||||
<View style={[styles.background, {paddingTop: 10}]}>
|
||||
<Text>
|
||||
capInsets: 15
|
||||
</Text>
|
||||
<Image
|
||||
source={nativeImageSource({
|
||||
ios: 'story-background',
|
||||
width: 60,
|
||||
height: 60
|
||||
})}
|
||||
style={styles.storyBackground}
|
||||
resizeMode={Image.resizeMode.stretch}
|
||||
capInsets={{left: 15, right: 15, bottom: 15, top: 15}}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
background: {
|
||||
backgroundColor: '#F6F6F6',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
horizontal: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
storyBackground: {
|
||||
width: 250,
|
||||
height: 150,
|
||||
borderWidth: 1,
|
||||
},
|
||||
text: {
|
||||
fontSize: 13.5,
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = ImageCapInsetsExample;
|
||||
9
packages/examples/src/RNTester/ImageEditingExample.js
Normal file
9
packages/examples/src/RNTester/ImageEditingExample.js
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule ImageEditingExample
|
||||
*/
|
||||
787
packages/examples/src/RNTester/ImageExample.js
Normal file
787
packages/examples/src/RNTester/ImageExample.js
Normal file
@@ -0,0 +1,787 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule ImageExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var createReactClass = require('create-react-class');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
ActivityIndicator,
|
||||
Image,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
ImageBackground,
|
||||
} = ReactNative;
|
||||
|
||||
var base64Icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAABLCAQAAACSR7JhAAADtUlEQVR4Ac3YA2Bj6QLH0XPT1Fzbtm29tW3btm3bfLZtv7e2ObZnms7d8Uw098tuetPzrxv8wiISrtVudrG2JXQZ4VOv+qUfmqCGGl1mqLhoA52oZlb0mrjsnhKpgeUNEs91Z0pd1kvihA3ULGVHiQO2narKSHKkEMulm9VgUyE60s1aWoMQUbpZOWE+kaqs4eLEjdIlZTcFZB0ndc1+lhB1lZrIuk5P2aib1NBpZaL+JaOGIt0ls47SKzLC7CqrlGF6RZ09HGoNy1lYl2aRSWL5GuzqWU1KafRdoRp0iOQEiDzgZPnG6DbldcomadViflnl/cL93tOoVbsOLVM2jylvdWjXolWX1hmfZbGR/wjypDjFLSZIRov09BgYmtUqPQPlQrPapecLgTIy0jMgPKtTeob2zWtrGH3xvjUkPCtNg/tm1rjwrMa+mdUkPd3hWbH0jArPGiU9ufCsNNWFZ40wpwn+62/66R2RUtoso1OB34tnLOcy7YB1fUdc9e0q3yru8PGM773vXsuZ5YIZX+5xmHwHGVvlrGPN6ZSiP1smOsMMde40wKv2VmwPPVXNut4sVpUreZiLBHi0qln/VQeI/LTMYXpsJtFiclUN+5HVZazim+Ky+7sAvxWnvjXrJFneVtLWLyPJu9K3cXLWeOlbMTlrIelbMDlrLenrjEQOtIF+fuI9xRp9ZBFp6+b6WT8RrxEpdK64BuvHgDk+vUy+b5hYk6zfyfs051gRoNO1usU12WWRWL73/MMEy9pMi9qIrR4ZpV16Rrvduxazmy1FSvuFXRkqTnE7m2kdb5U8xGjLw/spRr1uTov4uOgQE+0N/DvFrG/Jt7i/FzwxbA9kDanhf2w+t4V97G8lrT7wc08aA2QNUkuTfW/KimT01wdlfK4yEw030VfT0RtZbzjeMprNq8m8tnSTASrTLti64oBNdpmMQm0eEwvfPwRbUBywG5TzjPCsdwk3IeAXjQblLCoXnDVeoAz6SfJNk5TTzytCNZk/POtTSV40NwOFWzw86wNJRpubpXsn60NJFlHeqlYRbslqZm2jnEZ3qcSKgm0kTli3zZVS7y/iivZTweYXJ26Y+RTbV1zh3hYkgyFGSTKPfRVbRqWWVReaxYeSLarYv1Qqsmh1s95S7G+eEWK0f3jYKTbV6bOwepjfhtafsvUsqrQvrGC8YhmnO9cSCk3yuY984F1vesdHYhWJ5FvASlacshUsajFt2mUM9pqzvKGcyNJW0arTKN1GGGzQlH0tXwLDgQTurS8eIQAAAABJRU5ErkJggg==';
|
||||
|
||||
var ImageCapInsetsExample = require('./ImageCapInsetsExample');
|
||||
const IMAGE_PREFETCH_URL = 'http://origami.design/public/images/bird-logo.png?r=1&t=' + Date.now();
|
||||
var prefetchTask = Image.prefetch(IMAGE_PREFETCH_URL);
|
||||
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an error
|
||||
* found when Flow v0.63 was deployed. To see the error delete this comment and
|
||||
* run Flow. */
|
||||
var NetworkImageCallbackExample = createReactClass({
|
||||
displayName: 'NetworkImageCallbackExample',
|
||||
getInitialState: function() {
|
||||
return {
|
||||
events: [],
|
||||
startLoadPrefetched: false,
|
||||
mountTime: new Date(),
|
||||
};
|
||||
},
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
this.setState({mountTime: new Date()});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var { mountTime } = this.state;
|
||||
|
||||
return (
|
||||
<View>
|
||||
<Image
|
||||
source={this.props.source}
|
||||
style={[styles.base, {overflow: 'visible'}]}
|
||||
onLoadStart={() => this._loadEventFired(`✔ onLoadStart (+${new Date() - mountTime}ms)`)}
|
||||
onLoad={(event) => {
|
||||
// Currently this image source feature is only available on iOS.
|
||||
if (event.nativeEvent.source) {
|
||||
const url = event.nativeEvent.source.url;
|
||||
this._loadEventFired(`✔ onLoad (+${new Date() - mountTime}ms) for URL ${url}`);
|
||||
} else {
|
||||
this._loadEventFired(`✔ onLoad (+${new Date() - mountTime}ms)`);
|
||||
}
|
||||
}}
|
||||
onLoadEnd={() => {
|
||||
this._loadEventFired(`✔ onLoadEnd (+${new Date() - mountTime}ms)`);
|
||||
this.setState({startLoadPrefetched: true}, () => {
|
||||
prefetchTask.then(() => {
|
||||
this._loadEventFired(`✔ Prefetch OK (+${new Date() - mountTime}ms)`);
|
||||
}, error => {
|
||||
this._loadEventFired(`✘ Prefetch failed (+${new Date() - mountTime}ms)`);
|
||||
});
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{this.state.startLoadPrefetched ?
|
||||
<Image
|
||||
source={this.props.prefetchedSource}
|
||||
style={[styles.base, {overflow: 'visible'}]}
|
||||
onLoadStart={() => this._loadEventFired(`✔ (prefetched) onLoadStart (+${new Date() - mountTime}ms)`)}
|
||||
onLoad={(event) => {
|
||||
// Currently this image source feature is only available on iOS.
|
||||
if (event.nativeEvent.source) {
|
||||
const url = event.nativeEvent.source.url;
|
||||
this._loadEventFired(`✔ (prefetched) onLoad (+${new Date() - mountTime}ms) for URL ${url}`);
|
||||
} else {
|
||||
this._loadEventFired(`✔ (prefetched) onLoad (+${new Date() - mountTime}ms)`);
|
||||
}
|
||||
}}
|
||||
onLoadEnd={() => this._loadEventFired(`✔ (prefetched) onLoadEnd (+${new Date() - mountTime}ms)`)}
|
||||
/>
|
||||
: null}
|
||||
<Text style={{marginTop: 20}}>
|
||||
{this.state.events.join('\n')}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
|
||||
_loadEventFired(event) {
|
||||
this.setState((state) => {
|
||||
return state.events = [...state.events, event];
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var NetworkImageExample = createReactClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
error: false,
|
||||
loading: false,
|
||||
progress: 0
|
||||
};
|
||||
},
|
||||
render: function() {
|
||||
var loader = this.state.loading ?
|
||||
<View style={styles.progress}>
|
||||
<Text>{this.state.progress}%</Text>
|
||||
<ActivityIndicator style={{marginLeft:5}} />
|
||||
</View> : null;
|
||||
return this.state.error ?
|
||||
<Text>{this.state.error}</Text> :
|
||||
<ImageBackground
|
||||
source={this.props.source}
|
||||
style={[styles.base, {overflow: 'visible'}]}
|
||||
onLoadStart={(e) => this.setState({loading: true})}
|
||||
onError={(e) => this.setState({error: e.nativeEvent.error, loading: false})}
|
||||
onProgress={(e) => this.setState({progress: Math.round(100 * e.nativeEvent.loaded / e.nativeEvent.total)})}
|
||||
onLoad={() => this.setState({loading: false, error: false})}>
|
||||
{loader}
|
||||
</ImageBackground>;
|
||||
}
|
||||
});
|
||||
|
||||
var ImageSizeExample = createReactClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
width: 0,
|
||||
height: 0,
|
||||
};
|
||||
},
|
||||
componentDidMount: function() {
|
||||
Image.getSize(this.props.source.uri, (width, height) => {
|
||||
this.setState({width, height});
|
||||
});
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<Image
|
||||
style={{
|
||||
width: 60,
|
||||
height: 60,
|
||||
backgroundColor: 'transparent',
|
||||
marginRight: 10,
|
||||
}}
|
||||
source={this.props.source} />
|
||||
<Text>
|
||||
Actual dimensions:{'\n'}
|
||||
Width: {this.state.width}, Height: {this.state.height}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var MultipleSourcesExample = createReactClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
width: 30,
|
||||
height: 30,
|
||||
};
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<View style={{flexDirection: 'row', justifyContent: 'space-between'}}>
|
||||
<Text
|
||||
style={styles.touchableText}
|
||||
onPress={this.decreaseImageSize} >
|
||||
Decrease image size
|
||||
</Text>
|
||||
<Text
|
||||
style={styles.touchableText}
|
||||
onPress={this.increaseImageSize} >
|
||||
Increase image size
|
||||
</Text>
|
||||
</View>
|
||||
<Text>Container image size: {this.state.width}x{this.state.height} </Text>
|
||||
<View
|
||||
style={{height: this.state.height, width: this.state.width}} >
|
||||
<Image
|
||||
style={{flex: 1}}
|
||||
source={[
|
||||
{uri: 'https://facebook.github.io/react-native/img/favicon.png', width: 38, height: 38},
|
||||
{uri: 'https://facebook.github.io/react-native/img/favicon.png', width: 76, height: 76},
|
||||
{uri: 'https://facebook.github.io/react-native/img/opengraph.png', width: 400, height: 400}
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
increaseImageSize: function() {
|
||||
if (this.state.width >= 100) {
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
width: this.state.width + 10,
|
||||
height: this.state.height + 10,
|
||||
});
|
||||
},
|
||||
decreaseImageSize: function() {
|
||||
if (this.state.width <= 10) {
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
width: this.state.width - 10,
|
||||
height: this.state.height - 10,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
exports.displayName = (undefined: ?string);
|
||||
exports.framework = 'React';
|
||||
exports.title = '<Image>';
|
||||
exports.description = 'Base component for displaying different types of images.';
|
||||
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'Plain Network Image',
|
||||
description: 'If the `source` prop `uri` property is prefixed with ' +
|
||||
'"http", then it will be downloaded from the network.',
|
||||
render: function() {
|
||||
return (
|
||||
<Image
|
||||
source={fullImage}
|
||||
style={styles.base}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Plain Static Image',
|
||||
description: 'Static assets should be placed in the source code tree, and ' +
|
||||
'required in the same way as JavaScript modules.',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<Image source={require('./uie_thumb_normal.png')} style={styles.icon} />
|
||||
<Image source={require('./uie_thumb_selected.png')} style={styles.icon} />
|
||||
<Image source={require('./uie_comment_normal.png')} style={styles.icon} />
|
||||
<Image source={require('./uie_comment_highlighted.png')} style={styles.icon} />
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Image Loading Events',
|
||||
render: function() {
|
||||
return (
|
||||
<NetworkImageCallbackExample source={{uri: 'http://origami.design/public/images/bird-logo.png?r=1&t=' + Date.now()}}
|
||||
prefetchedSource={{uri: IMAGE_PREFETCH_URL}}/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Error Handler',
|
||||
render: function() {
|
||||
return (
|
||||
<NetworkImageExample source={{uri: 'https://TYPO_ERROR_facebook.github.io/react/logo-og.png'}} />
|
||||
);
|
||||
},
|
||||
// platform: 'ios',
|
||||
},
|
||||
{
|
||||
title: 'Image Download Progress',
|
||||
render: function() {
|
||||
return (
|
||||
<NetworkImageExample source={{uri: 'http://origami.design/public/images/bird-logo.png?r=1'}}/>
|
||||
);
|
||||
},
|
||||
platform: 'ios',
|
||||
},
|
||||
{
|
||||
title: 'defaultSource',
|
||||
description: 'Show a placeholder image when a network image is loading',
|
||||
render: function() {
|
||||
return (
|
||||
<Image
|
||||
defaultSource={require('./bunny.png')}
|
||||
source={{uri: 'https://facebook.github.io/origami/public/images/birds.jpg'}}
|
||||
style={styles.base}
|
||||
/>
|
||||
);
|
||||
},
|
||||
// platform: 'ios',
|
||||
},
|
||||
{
|
||||
title: 'Cache Policy',
|
||||
description: 'First image has never been loaded before and is instructed not to load unless in cache.' +
|
||||
'Placeholder image from above will stay. Second image is the same but forced to load regardless of' +
|
||||
' local cache state.',
|
||||
render: function () {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<Image
|
||||
defaultSource={require('./bunny.png')}
|
||||
source={{
|
||||
uri: smallImage.uri + '?cacheBust=notinCache' + Date.now(),
|
||||
cache: 'only-if-cached'
|
||||
}}
|
||||
style={styles.base}
|
||||
/>
|
||||
<Image
|
||||
defaultSource={require('./bunny.png')}
|
||||
source={{
|
||||
uri: smallImage.uri + '?cacheBust=notinCache' + Date.now(),
|
||||
cache: 'reload'
|
||||
}}
|
||||
style={styles.base}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
platform: 'ios',
|
||||
},
|
||||
{
|
||||
title: 'Border Color',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<Image
|
||||
source={smallImage}
|
||||
style={[
|
||||
styles.base,
|
||||
styles.background,
|
||||
{borderWidth: 3, borderColor: '#f099f0'}
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Border Width',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<Image
|
||||
source={smallImage}
|
||||
style={[
|
||||
styles.base,
|
||||
styles.background,
|
||||
{borderWidth: 5, borderColor: '#f099f0'}
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Border Radius',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<Image
|
||||
style={[styles.base, {borderRadius: 5}]}
|
||||
source={fullImage}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin, {borderRadius: 19}]}
|
||||
source={fullImage}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Background Color',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<Image source={smallImage} style={styles.base} />
|
||||
<Image
|
||||
style={[
|
||||
styles.base,
|
||||
styles.leftMargin,
|
||||
{backgroundColor: 'rgba(0, 0, 100, 0.25)'}
|
||||
]}
|
||||
source={smallImage}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin, {backgroundColor: 'red'}]}
|
||||
source={smallImage}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin, {backgroundColor: 'black'}]}
|
||||
source={smallImage}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Opacity',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<Image
|
||||
style={[styles.base, {opacity: 1}]}
|
||||
source={fullImage}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin, {opacity: 0.8}]}
|
||||
source={fullImage}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin, {opacity: 0.6}]}
|
||||
source={fullImage}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin, {opacity: 0.4}]}
|
||||
source={fullImage}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin, {opacity: 0.2}]}
|
||||
source={fullImage}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin, {opacity: 0}]}
|
||||
source={fullImage}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Nesting content inside <Image> component',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={{width: 60, height: 60}}>
|
||||
<Image
|
||||
style={{...StyleSheet.absoluteFillObject}}
|
||||
source={fullImage}/>
|
||||
<Text style={styles.nestedText}>
|
||||
React
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Nesting content inside <ImageBackground> component',
|
||||
render: function() {
|
||||
return (
|
||||
<ImageBackground
|
||||
style={{width: 60, height: 60, backgroundColor: 'transparent'}}
|
||||
source={fullImage}>
|
||||
<Text style={styles.nestedText}>
|
||||
React
|
||||
</Text>
|
||||
</ImageBackground>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Tint Color',
|
||||
description: 'The `tintColor` style prop changes all the non-alpha ' +
|
||||
'pixels to the tint color.',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<View style={styles.horizontal}>
|
||||
<Image
|
||||
source={require('./uie_thumb_normal.png')}
|
||||
style={[styles.icon, {borderRadius: 5, tintColor: '#5ac8fa' }]}
|
||||
/>
|
||||
<Image
|
||||
source={require('./uie_thumb_normal.png')}
|
||||
style={[styles.icon, styles.leftMargin, {borderRadius: 5, tintColor: '#4cd964' }]}
|
||||
/>
|
||||
<Image
|
||||
source={require('./uie_thumb_normal.png')}
|
||||
style={[styles.icon, styles.leftMargin, {borderRadius: 5, tintColor: '#ff2d55' }]}
|
||||
/>
|
||||
<Image
|
||||
source={require('./uie_thumb_normal.png')}
|
||||
style={[styles.icon, styles.leftMargin, {borderRadius: 5, tintColor: '#8e8e93' }]}
|
||||
/>
|
||||
</View>
|
||||
<Text style={styles.sectionText}>
|
||||
It also works with downloaded images:
|
||||
</Text>
|
||||
<View style={styles.horizontal}>
|
||||
<Image
|
||||
source={smallImage}
|
||||
style={[styles.base, {borderRadius: 5, tintColor: '#5ac8fa' }]}
|
||||
/>
|
||||
<Image
|
||||
source={smallImage}
|
||||
style={[styles.base, styles.leftMargin, {borderRadius: 5, tintColor: '#4cd964' }]}
|
||||
/>
|
||||
<Image
|
||||
source={smallImage}
|
||||
style={[styles.base, styles.leftMargin, {borderRadius: 5, tintColor: '#ff2d55' }]}
|
||||
/>
|
||||
<Image
|
||||
source={smallImage}
|
||||
style={[styles.base, styles.leftMargin, {borderRadius: 5, tintColor: '#8e8e93' }]}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Resize Mode',
|
||||
description: 'The `resizeMode` style prop controls how the image is ' +
|
||||
'rendered within the frame.',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
{[smallImage, fullImage].map((image, index) => {
|
||||
return (
|
||||
<View key={index}>
|
||||
<View style={styles.horizontal}>
|
||||
<View>
|
||||
<Text style={[styles.resizeModeText]}>
|
||||
Contain
|
||||
</Text>
|
||||
<Image
|
||||
style={styles.resizeMode}
|
||||
resizeMode={Image.resizeMode.contain}
|
||||
source={image}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.leftMargin}>
|
||||
<Text style={[styles.resizeModeText]}>
|
||||
Cover
|
||||
</Text>
|
||||
<Image
|
||||
style={styles.resizeMode}
|
||||
resizeMode={Image.resizeMode.cover}
|
||||
source={image}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.horizontal}>
|
||||
<View>
|
||||
<Text style={[styles.resizeModeText]}>
|
||||
Stretch
|
||||
</Text>
|
||||
<Image
|
||||
style={styles.resizeMode}
|
||||
resizeMode={Image.resizeMode.stretch}
|
||||
source={image}
|
||||
/>
|
||||
</View>
|
||||
{ Platform.OS === 'ios' || Platform.OS === 'web' ?
|
||||
<View style={styles.leftMargin}>
|
||||
<Text style={[styles.resizeModeText]}>
|
||||
Repeat
|
||||
</Text>
|
||||
<Image
|
||||
style={styles.resizeMode}
|
||||
resizeMode={Image.resizeMode.repeat}
|
||||
source={image}
|
||||
/>
|
||||
</View>
|
||||
: null }
|
||||
<View style={styles.leftMargin}>
|
||||
<Text style={[styles.resizeModeText]}>
|
||||
Center
|
||||
</Text>
|
||||
<Image
|
||||
style={styles.resizeMode}
|
||||
resizeMode={Image.resizeMode.center}
|
||||
source={image}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Animated GIF',
|
||||
render: function() {
|
||||
return (
|
||||
<Image
|
||||
style={styles.gif}
|
||||
source={{uri: 'https://38.media.tumblr.com/9e9bd08c6e2d10561dd1fb4197df4c4e/tumblr_mfqekpMktw1rn90umo1_500.gif'}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
// platform: 'ios',
|
||||
},
|
||||
{
|
||||
title: 'Base64 image',
|
||||
render: function() {
|
||||
return (
|
||||
<Image
|
||||
style={styles.base64}
|
||||
source={{uri: base64Icon, scale: 3}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
// platform: 'ios',
|
||||
},
|
||||
{
|
||||
title: 'Cap Insets',
|
||||
description:
|
||||
'When the image is resized, the corners of the size specified ' +
|
||||
'by capInsets will stay a fixed size, but the center content and ' +
|
||||
'borders of the image will be stretched. This is useful for creating ' +
|
||||
'resizable rounded buttons, shadows, and other resizable assets.',
|
||||
render: function() {
|
||||
return <ImageCapInsetsExample />;
|
||||
},
|
||||
platform: 'ios',
|
||||
},
|
||||
{
|
||||
title: 'Image Size',
|
||||
render: function() {
|
||||
return <ImageSizeExample source={fullImage} />;
|
||||
},
|
||||
},
|
||||
/*
|
||||
{
|
||||
title: 'MultipleSourcesExample',
|
||||
description:
|
||||
'The `source` prop allows passing in an array of uris, so that native to choose which image ' +
|
||||
'to diplay based on the size of the of the target image',
|
||||
render: function() {
|
||||
return <MultipleSourcesExample />;
|
||||
},
|
||||
},
|
||||
*/
|
||||
{
|
||||
title: 'Legacy local image',
|
||||
description:
|
||||
'Images shipped with the native bundle, but not managed ' +
|
||||
'by the JS packager',
|
||||
render: function() {
|
||||
return (
|
||||
<Image
|
||||
source={{uri: 'legacy_image', width: 120, height: 120}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Bundled images',
|
||||
description:
|
||||
'Images shipped in a separate native bundle',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<Image
|
||||
source={{
|
||||
uri: 'ImageInBundle',
|
||||
bundle: 'RNTesterBundle',
|
||||
width: 100,
|
||||
height: 100,
|
||||
}}
|
||||
style={{borderColor: 'yellow', borderWidth: 4}}
|
||||
/>
|
||||
<Image
|
||||
source={{
|
||||
uri: 'ImageInAssetCatalog',
|
||||
bundle: 'RNTesterBundle',
|
||||
width: 100,
|
||||
height: 100,
|
||||
}}
|
||||
style={{marginLeft: 10, borderColor: 'blue', borderWidth: 4}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
platform: 'ios',
|
||||
},
|
||||
{
|
||||
title: 'Blur Radius',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<Image
|
||||
style={[styles.base,]}
|
||||
source={fullImage}
|
||||
blurRadius={0}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin]}
|
||||
source={fullImage}
|
||||
blurRadius={5}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin]}
|
||||
source={fullImage}
|
||||
blurRadius={10}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin]}
|
||||
source={fullImage}
|
||||
blurRadius={15}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin]}
|
||||
source={fullImage}
|
||||
blurRadius={20}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin]}
|
||||
source={fullImage}
|
||||
blurRadius={25}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
var fullImage = {uri: 'https://facebook.github.io/react-native/img/opengraph.png'};
|
||||
var smallImage = {uri: 'https://facebook.github.io/react-native/img/favicon.png'};
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
base: {
|
||||
width: 38,
|
||||
height: 38,
|
||||
},
|
||||
progress: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
width: 100
|
||||
},
|
||||
leftMargin: {
|
||||
marginLeft: 10,
|
||||
},
|
||||
background: {
|
||||
backgroundColor: '#222222'
|
||||
},
|
||||
sectionText: {
|
||||
marginVertical: 6,
|
||||
},
|
||||
nestedText: {
|
||||
marginLeft: 12,
|
||||
marginTop: 20,
|
||||
backgroundColor: 'transparent',
|
||||
color: 'white'
|
||||
},
|
||||
resizeMode: {
|
||||
width: 90,
|
||||
height: 60,
|
||||
borderWidth: 0.5,
|
||||
borderColor: 'black'
|
||||
},
|
||||
resizeModeText: {
|
||||
fontSize: 11,
|
||||
marginBottom: 3,
|
||||
},
|
||||
icon: {
|
||||
width: 15,
|
||||
height: 15,
|
||||
},
|
||||
horizontal: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
gif: {
|
||||
flex: 1,
|
||||
height: 200,
|
||||
},
|
||||
base64: {
|
||||
flex: 1,
|
||||
height: 50,
|
||||
resizeMode: 'contain',
|
||||
},
|
||||
touchableText: {
|
||||
fontWeight: '500',
|
||||
color: 'blue',
|
||||
},
|
||||
});
|
||||
101
packages/examples/src/RNTester/InputAccessoryViewExample.js
Normal file
101
packages/examples/src/RNTester/InputAccessoryViewExample.js
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule InputAccessoryViewExample
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const Alert = require('Alert');
|
||||
const Button = require('Button');
|
||||
const Dimensions = require('Dimensions');
|
||||
const InputAccessoryView = require('InputAccessoryView');
|
||||
const React = require('React');
|
||||
const ScrollView = require('ScrollView');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const Text = require('Text');
|
||||
const TextInput = require('TextInput');
|
||||
const View = require('View');
|
||||
|
||||
class Message extends React.PureComponent<*> {
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.textBubbleBackground}>
|
||||
<Text style={styles.text}>Text Message</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TextInputBar extends React.PureComponent<*, *> {
|
||||
state = {text: ''};
|
||||
|
||||
render() {
|
||||
const {width} = Dimensions.get('window');
|
||||
return (
|
||||
<View style={[styles.textInputContainer, {width}]}>
|
||||
<TextInput
|
||||
style={styles.textInput}
|
||||
onChangeText={text => {
|
||||
this.setState({text});
|
||||
}}
|
||||
value={this.state.text}
|
||||
placeholder={'Type a message...'}
|
||||
/>
|
||||
<Button
|
||||
onPress={() => {
|
||||
Alert.alert('You tapped the button!');
|
||||
}}
|
||||
title="Send"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class InputAccessoryViewExample extends React.Component<*> {
|
||||
static title = '<InputAccessoryView>';
|
||||
static description = 'Example showing how to use an InputAccessoryView to build an iMessage-like sticky text input';
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<ScrollView keyboardDismissMode="interactive">
|
||||
{Array(15)
|
||||
.fill()
|
||||
.map((_, i) => <Message key={i} />)}
|
||||
</ScrollView>
|
||||
<InputAccessoryView backgroundColor="#fffffff7">
|
||||
<TextInputBar />
|
||||
</InputAccessoryView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
textInputContainer: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
textInput: {
|
||||
flex: 1,
|
||||
paddingLeft: 10,
|
||||
},
|
||||
text: {
|
||||
padding: 10,
|
||||
color: 'white',
|
||||
},
|
||||
textBubbleBackground: {
|
||||
backgroundColor: '#2f7bf6',
|
||||
borderRadius: 20,
|
||||
width: 110,
|
||||
margin: 20,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = InputAccessoryViewExample;
|
||||
105
packages/examples/src/RNTester/KeyboardAvoidingViewExample.js
Normal file
105
packages/examples/src/RNTester/KeyboardAvoidingViewExample.js
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule KeyboardAvoidingViewExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const React = require('React');
|
||||
const ReactNative = require('react-native');
|
||||
const {
|
||||
KeyboardAvoidingView,
|
||||
Modal,
|
||||
SegmentedControlIOS,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableHighlight,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
const RNTesterBlock = require('./RNTesterBlock');
|
||||
const RNTesterPage = require('./RNTesterPage');
|
||||
|
||||
class KeyboardAvoidingViewExample extends React.Component {
|
||||
static title = '<KeyboardAvoidingView>';
|
||||
static description = 'Base component for views that automatically adjust their height or position to move out of the way of the keyboard.';
|
||||
|
||||
state = {
|
||||
behavior: 'padding',
|
||||
modalOpen: false,
|
||||
};
|
||||
|
||||
onSegmentChange = (segment: String) => {
|
||||
this.setState({behavior: segment.toLowerCase()});
|
||||
};
|
||||
|
||||
renderExample = () => {
|
||||
return (
|
||||
<View style={styles.outerContainer}>
|
||||
<Modal animationType="fade" visible={this.state.modalOpen}>
|
||||
<KeyboardAvoidingView behavior={this.state.behavior} style={styles.container}>
|
||||
<SegmentedControlIOS
|
||||
onValueChange={this.onSegmentChange}
|
||||
selectedIndex={this.state.behavior === 'padding' ? 0 : 1}
|
||||
style={styles.segment}
|
||||
values={['Padding', 'Position']} />
|
||||
<TextInput
|
||||
placeholder="<TextInput />"
|
||||
style={styles.textInput} />
|
||||
</KeyboardAvoidingView>
|
||||
<TouchableHighlight
|
||||
onPress={() => this.setState({modalOpen: false})}
|
||||
style={styles.closeButton}>
|
||||
<Text>Close</Text>
|
||||
</TouchableHighlight>
|
||||
</Modal>
|
||||
|
||||
<TouchableHighlight onPress={() => this.setState({modalOpen: true})}>
|
||||
<Text>Open Example</Text>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<RNTesterPage title="Keyboard Avoiding View">
|
||||
<RNTesterBlock title="Keyboard-avoiding views move out of the way of the keyboard.">
|
||||
{this.renderExample()}
|
||||
</RNTesterBlock>
|
||||
</RNTesterPage>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
outerContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 20,
|
||||
paddingTop: 20,
|
||||
},
|
||||
textInput: {
|
||||
borderRadius: 5,
|
||||
borderWidth: 1,
|
||||
height: 44,
|
||||
paddingHorizontal: 10,
|
||||
},
|
||||
segment: {
|
||||
marginBottom: 10,
|
||||
},
|
||||
closeButton: {
|
||||
position: 'absolute',
|
||||
top: 30,
|
||||
left: 10,
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = KeyboardAvoidingViewExample;
|
||||
159
packages/examples/src/RNTester/LayoutAnimationExample.js
Normal file
159
packages/examples/src/RNTester/LayoutAnimationExample.js
Normal file
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule LayoutAnimationExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const {
|
||||
LayoutAnimation,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
TouchableOpacity,
|
||||
} = ReactNative;
|
||||
|
||||
class AddRemoveExample extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
views: [],
|
||||
};
|
||||
|
||||
UNSAFE_componentWillUpdate() {
|
||||
LayoutAnimation.easeInEaseOut();
|
||||
}
|
||||
|
||||
_onPressAddView = () => {
|
||||
this.setState((state) => ({views: [...state.views, {}]}));
|
||||
};
|
||||
|
||||
_onPressRemoveView = () => {
|
||||
this.setState((state) => ({views: state.views.slice(0, -1)}));
|
||||
};
|
||||
|
||||
render() {
|
||||
const views = this.state.views.map((view, i) =>
|
||||
<View key={i} style={styles.view}>
|
||||
<Text>{i}</Text>
|
||||
</View>
|
||||
);
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<TouchableOpacity onPress={this._onPressAddView}>
|
||||
<View style={styles.button}>
|
||||
<Text>Add view</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={this._onPressRemoveView}>
|
||||
<View style={styles.button}>
|
||||
<Text>Remove view</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.viewContainer}>
|
||||
{views}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const GreenSquare = () =>
|
||||
<View style={styles.greenSquare}>
|
||||
<Text>Green square</Text>
|
||||
</View>;
|
||||
|
||||
const BlueSquare = () =>
|
||||
<View style={styles.blueSquare}>
|
||||
<Text>Blue square</Text>
|
||||
</View>;
|
||||
|
||||
class CrossFadeExample extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
toggled: false,
|
||||
};
|
||||
|
||||
_onPressToggle = () => {
|
||||
LayoutAnimation.easeInEaseOut();
|
||||
this.setState((state) => ({toggled: !state.toggled}));
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<TouchableOpacity onPress={this._onPressToggle}>
|
||||
<View style={styles.button}>
|
||||
<Text>Toggle</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.viewContainer}>
|
||||
{
|
||||
this.state.toggled ?
|
||||
<GreenSquare /> :
|
||||
<BlueSquare />
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
button: {
|
||||
borderRadius: 5,
|
||||
backgroundColor: '#eeeeee',
|
||||
padding: 10,
|
||||
marginBottom: 10,
|
||||
},
|
||||
buttonText: {
|
||||
fontSize: 16,
|
||||
},
|
||||
viewContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
view: {
|
||||
height: 54,
|
||||
width: 54,
|
||||
backgroundColor: 'red',
|
||||
margin: 8,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
greenSquare: {
|
||||
width: 150,
|
||||
height: 150,
|
||||
backgroundColor: 'green',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
blueSquare: {
|
||||
width: 150,
|
||||
height: 150,
|
||||
backgroundColor: 'blue',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
exports.title = 'Layout Animation';
|
||||
exports.description = 'Layout animation';
|
||||
exports.examples = [{
|
||||
title: 'Add and remove views',
|
||||
render(): React.Element<any> {
|
||||
return <AddRemoveExample />;
|
||||
},
|
||||
}, {
|
||||
title: 'Cross fade views',
|
||||
render(): React.Element<any> {
|
||||
return <CrossFadeExample />;
|
||||
},
|
||||
}];
|
||||
152
packages/examples/src/RNTester/LayoutEventsExample.js
Normal file
152
packages/examples/src/RNTester/LayoutEventsExample.js
Normal file
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule LayoutEventsExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
Image,
|
||||
LayoutAnimation,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
import type {ViewLayout, ViewLayoutEvent} from 'ViewPropTypes';
|
||||
|
||||
type State = {
|
||||
containerStyle?: { width: number },
|
||||
extraText?: string,
|
||||
imageLayout?: ViewLayout,
|
||||
textLayout?: ViewLayout,
|
||||
viewLayout?: ViewLayout,
|
||||
viewStyle: { margin: number },
|
||||
};
|
||||
|
||||
class LayoutEventExample extends React.Component<{}, State> {
|
||||
state: State = {
|
||||
viewStyle: {
|
||||
margin: 20,
|
||||
},
|
||||
};
|
||||
|
||||
animateViewLayout = () => {
|
||||
LayoutAnimation.configureNext(
|
||||
LayoutAnimation.Presets.spring,
|
||||
() => {
|
||||
console.log('layout animation done.');
|
||||
this.addWrapText();
|
||||
}
|
||||
);
|
||||
this.setState({
|
||||
viewStyle: {
|
||||
margin: this.state.viewStyle.margin > 20 ? 20 : 60,
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
addWrapText = () => {
|
||||
this.setState(
|
||||
{extraText: ' And a bunch more text to wrap around a few lines.'},
|
||||
this.changeContainer
|
||||
);
|
||||
};
|
||||
|
||||
changeContainer = () => {
|
||||
this.setState({containerStyle: {width: 280}});
|
||||
};
|
||||
|
||||
onViewLayout = (e: ViewLayoutEvent) => {
|
||||
console.log('received view layout event\n', e.nativeEvent);
|
||||
this.setState({viewLayout: e.nativeEvent.layout});
|
||||
};
|
||||
|
||||
onTextLayout = (e: ViewLayoutEvent) => {
|
||||
console.log('received text layout event\n', e.nativeEvent);
|
||||
this.setState({textLayout: e.nativeEvent.layout});
|
||||
};
|
||||
|
||||
onImageLayout = (e: ViewLayoutEvent) => {
|
||||
console.log('received image layout event\n', e.nativeEvent);
|
||||
this.setState({imageLayout: e.nativeEvent.layout});
|
||||
};
|
||||
|
||||
render() {
|
||||
var viewStyle = [styles.view, this.state.viewStyle];
|
||||
var textLayout = this.state.textLayout || {width: '?', height: '?'};
|
||||
var imageLayout = this.state.imageLayout || {x: '?', y: '?'};
|
||||
return (
|
||||
<View style={this.state.containerStyle}>
|
||||
<Text>
|
||||
layout events are called on mount and whenever layout is recalculated. Note that the layout event will typically be received <Text style={styles.italicText}>before</Text> the layout has updated on screen, especially when using layout animations.{' '}
|
||||
<Text style={styles.pressText} onPress={this.animateViewLayout}>
|
||||
Press here to change layout.
|
||||
</Text>
|
||||
</Text>
|
||||
<View ref="view" onLayout={this.onViewLayout} style={viewStyle}>
|
||||
<Image
|
||||
ref="img"
|
||||
onLayout={this.onImageLayout}
|
||||
style={styles.image}
|
||||
source={{uri: 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851561_767334496626293_1958532586_n.png'}}
|
||||
/>
|
||||
<Text>
|
||||
ViewLayout: {JSON.stringify(this.state.viewLayout, null, ' ') + '\n\n'}
|
||||
</Text>
|
||||
<Text ref="txt" onLayout={this.onTextLayout} style={styles.text}>
|
||||
A simple piece of text.{this.state.extraText}
|
||||
</Text>
|
||||
<Text>
|
||||
{'\n'}
|
||||
Text w/h: {textLayout.width}/{textLayout.height + '\n'}
|
||||
Image x/y: {imageLayout.x}/{imageLayout.y}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
view: {
|
||||
padding: 12,
|
||||
borderColor: 'black',
|
||||
borderWidth: 0.5,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
text: {
|
||||
alignSelf: 'flex-start',
|
||||
borderColor: 'rgba(0, 0, 255, 0.2)',
|
||||
borderWidth: 0.5,
|
||||
},
|
||||
image: {
|
||||
width: 50,
|
||||
height: 50,
|
||||
marginBottom: 10,
|
||||
alignSelf: 'center',
|
||||
},
|
||||
pressText: {
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
italicText: {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
});
|
||||
|
||||
exports.title = 'Layout Events';
|
||||
exports.description = 'Examples that show how Layout events can be used to ' +
|
||||
'measure view size and position.';
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'LayoutEventExample',
|
||||
render: function(): React.Element<any> {
|
||||
return <LayoutEventExample />;
|
||||
},
|
||||
}];
|
||||
167
packages/examples/src/RNTester/LayoutExample.js
Normal file
167
packages/examples/src/RNTester/LayoutExample.js
Normal file
@@ -0,0 +1,167 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule LayoutExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
var RNTesterBlock = require('./RNTesterBlock');
|
||||
var RNTesterPage = require('./RNTesterPage');
|
||||
|
||||
class Circle extends React.Component<$FlowFixMeProps> {
|
||||
render() {
|
||||
var size = this.props.size || 20;
|
||||
var backgroundColor = this.props.bgColor || '#527fe4';
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
borderRadius: size / 2,
|
||||
backgroundColor: backgroundColor,
|
||||
width: size,
|
||||
height: size,
|
||||
margin: 1,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CircleBlock extends React.Component<$FlowFixMeProps> {
|
||||
render() {
|
||||
var circleStyle = {
|
||||
flexDirection: 'row',
|
||||
backgroundColor: '#f6f7f8',
|
||||
borderWidth: 0.5,
|
||||
borderColor: '#d6d7da',
|
||||
marginBottom: 2,
|
||||
};
|
||||
return (
|
||||
<View style={[circleStyle, this.props.style]}>
|
||||
{this.props.children}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LayoutExample extends React.Component<$FlowFixMeProps> {
|
||||
static title = 'Layout - Flexbox';
|
||||
static description = 'Examples of using the flexbox API to layout views.';
|
||||
static displayName = 'LayoutExample';
|
||||
|
||||
render() {
|
||||
var fiveColoredCircles = [
|
||||
<Circle bgColor="#527fe4" key="blue" />,
|
||||
<Circle bgColor="#D443E3" key="violet" />,
|
||||
<Circle bgColor="#FF9049" key="orange" />,
|
||||
<Circle bgColor="#FFE649" key="yellow" />,
|
||||
<Circle bgColor="#7FE040" key="green" />
|
||||
];
|
||||
|
||||
return (
|
||||
<RNTesterPage title={this.props.navigator ? null : 'Layout'}>
|
||||
<RNTesterBlock title="Flex Direction">
|
||||
<Text>row</Text>
|
||||
<CircleBlock style={{flexDirection: 'row'}}>
|
||||
{fiveColoredCircles}
|
||||
</CircleBlock>
|
||||
<Text>row-reverse</Text>
|
||||
<CircleBlock style={{flexDirection: 'row-reverse'}}>
|
||||
{fiveColoredCircles}
|
||||
</CircleBlock>
|
||||
<Text>column</Text>
|
||||
<CircleBlock style={{flexDirection: 'column'}}>
|
||||
{fiveColoredCircles}
|
||||
</CircleBlock>
|
||||
<Text>column-reverse</Text>
|
||||
<CircleBlock style={{flexDirection: 'column-reverse'}}>
|
||||
{fiveColoredCircles}
|
||||
</CircleBlock>
|
||||
<View style={[styles.overlay, {position: 'absolute', top: 15, left: 160}]}>
|
||||
<Text>{'top: 15, left: 160'}</Text>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
|
||||
<RNTesterBlock title="Justify Content - Main Direction">
|
||||
<Text>flex-start</Text>
|
||||
<CircleBlock style={{justifyContent: 'flex-start'}}>
|
||||
{fiveColoredCircles}
|
||||
</CircleBlock>
|
||||
<Text>center</Text>
|
||||
<CircleBlock style={{justifyContent: 'center'}}>
|
||||
{fiveColoredCircles}
|
||||
</CircleBlock>
|
||||
<Text>flex-end</Text>
|
||||
<CircleBlock style={{justifyContent: 'flex-end'}}>
|
||||
{fiveColoredCircles}
|
||||
</CircleBlock>
|
||||
<Text>space-between</Text>
|
||||
<CircleBlock style={{justifyContent: 'space-between'}}>
|
||||
{fiveColoredCircles}
|
||||
</CircleBlock>
|
||||
<Text>space-around</Text>
|
||||
<CircleBlock style={{justifyContent: 'space-around'}}>
|
||||
{fiveColoredCircles}
|
||||
</CircleBlock>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title="Align Items - Other Direction">
|
||||
<Text>flex-start</Text>
|
||||
<CircleBlock style={{alignItems: 'flex-start', height: 30}}>
|
||||
<Circle size={15} /><Circle size={10} /><Circle size={20} />
|
||||
<Circle size={17} /><Circle size={12} /><Circle size={15} />
|
||||
<Circle size={10} /><Circle size={20} /><Circle size={17} />
|
||||
<Circle size={12} /><Circle size={15} /><Circle size={10} />
|
||||
<Circle size={20} /><Circle size={17} /><Circle size={12} />
|
||||
<Circle size={15} /><Circle size={8} />
|
||||
</CircleBlock>
|
||||
<Text>center</Text>
|
||||
<CircleBlock style={{alignItems: 'center', height: 30}}>
|
||||
<Circle size={15} /><Circle size={10} /><Circle size={20} />
|
||||
<Circle size={17} /><Circle size={12} /><Circle size={15} />
|
||||
<Circle size={10} /><Circle size={20} /><Circle size={17} />
|
||||
<Circle size={12} /><Circle size={15} /><Circle size={10} />
|
||||
<Circle size={20} /><Circle size={17} /><Circle size={12} />
|
||||
<Circle size={15} /><Circle size={8} />
|
||||
</CircleBlock>
|
||||
<Text>flex-end</Text>
|
||||
<CircleBlock style={{alignItems: 'flex-end', height: 30}}>
|
||||
<Circle size={15} /><Circle size={10} /><Circle size={20} />
|
||||
<Circle size={17} /><Circle size={12} /><Circle size={15} />
|
||||
<Circle size={10} /><Circle size={20} /><Circle size={17} />
|
||||
<Circle size={12} /><Circle size={15} /><Circle size={10} />
|
||||
<Circle size={20} /><Circle size={17} /><Circle size={12} />
|
||||
<Circle size={15} /><Circle size={8} />
|
||||
</CircleBlock>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title="Flex Wrap">
|
||||
<CircleBlock style={{flexWrap: 'wrap'}}>
|
||||
{'oooooooooooooooo'.split('').map((char, i) => <Circle key={i} />)}
|
||||
</CircleBlock>
|
||||
</RNTesterBlock>
|
||||
</RNTesterPage>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
overlay: {
|
||||
backgroundColor: '#aaccff',
|
||||
borderRadius: 10,
|
||||
borderWidth: 0.5,
|
||||
opacity: 0.5,
|
||||
padding: 5,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = LayoutExample;
|
||||
85
packages/examples/src/RNTester/LinkingExample.js
Normal file
85
packages/examples/src/RNTester/LinkingExample.js
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule LinkingExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var PropTypes = require('prop-types');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
Linking,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} = ReactNative;
|
||||
var RNTesterBlock = require('./RNTesterBlock');
|
||||
|
||||
class OpenURLButton extends React.Component {
|
||||
static propTypes = {
|
||||
url: PropTypes.string,
|
||||
};
|
||||
|
||||
handleClick = () => {
|
||||
Linking.canOpenURL(this.props.url).then(supported => {
|
||||
if (supported) {
|
||||
Linking.openURL(this.props.url);
|
||||
} else {
|
||||
console.log('Don\'t know how to open URI: ' + this.props.url);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={this.handleClick}>
|
||||
<View style={styles.button}>
|
||||
<Text style={styles.text}>Open {this.props.url}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class IntentAndroidExample extends React.Component {
|
||||
static title = 'Linking';
|
||||
static description = 'Shows how to use Linking to open URLs.';
|
||||
|
||||
render() {
|
||||
return (
|
||||
<RNTesterBlock title="Open external URLs">
|
||||
<OpenURLButton url={'https://www.facebook.com'} />
|
||||
<OpenURLButton url={'http://www.facebook.com'} />
|
||||
<OpenURLButton url={'http://facebook.com'} />
|
||||
<OpenURLButton url={'fb://notifications'} />
|
||||
<OpenURLButton url={'geo:37.484847,-122.148386'} />
|
||||
<OpenURLButton url={'tel:9876543210'} />
|
||||
</RNTesterBlock>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: 'white',
|
||||
padding: 10,
|
||||
paddingTop: 30,
|
||||
},
|
||||
button: {
|
||||
padding: 10,
|
||||
backgroundColor: '#3B5998',
|
||||
marginBottom: 10,
|
||||
},
|
||||
text: {
|
||||
color: 'white',
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = IntentAndroidExample;
|
||||
330
packages/examples/src/RNTester/ListExampleShared.js
Normal file
330
packages/examples/src/RNTester/ListExampleShared.js
Normal file
@@ -0,0 +1,330 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule ListExampleShared
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const {
|
||||
Animated,
|
||||
Image,
|
||||
Platform,
|
||||
TouchableHighlight,
|
||||
StyleSheet,
|
||||
Switch,
|
||||
Text,
|
||||
TextInput,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
type Item = {title: string, text: string, key: string, pressed: boolean, noImage?: ?boolean};
|
||||
|
||||
function genItemData(count: number, start: number = 0): Array<Item> {
|
||||
const dataBlob = [];
|
||||
for (let ii = start; ii < count + start; ii++) {
|
||||
const itemHash = Math.abs(hashCode('Item ' + ii));
|
||||
dataBlob.push({
|
||||
title: 'Item ' + ii,
|
||||
text: LOREM_IPSUM.substr(0, itemHash % 301 + 20),
|
||||
key: String(ii),
|
||||
pressed: false,
|
||||
});
|
||||
}
|
||||
return dataBlob;
|
||||
}
|
||||
|
||||
const HORIZ_WIDTH = 200;
|
||||
const ITEM_HEIGHT = 72;
|
||||
|
||||
class ItemComponent extends React.PureComponent<{
|
||||
fixedHeight?: ?boolean,
|
||||
horizontal?: ?boolean,
|
||||
item: Item,
|
||||
onPress: (key: string) => void,
|
||||
onShowUnderlay?: () => void,
|
||||
onHideUnderlay?: () => void,
|
||||
}> {
|
||||
_onPress = () => {
|
||||
this.props.onPress(this.props.item.key);
|
||||
};
|
||||
render() {
|
||||
const {fixedHeight, horizontal, item} = this.props;
|
||||
const itemHash = Math.abs(hashCode(item.title));
|
||||
const imgSource = THUMB_URLS[itemHash % THUMB_URLS.length];
|
||||
return (
|
||||
<TouchableHighlight
|
||||
onPress={this._onPress}
|
||||
onShowUnderlay={this.props.onShowUnderlay}
|
||||
onHideUnderlay={this.props.onHideUnderlay}
|
||||
tvParallaxProperties={{
|
||||
pressMagnification: 1.1,
|
||||
}}
|
||||
style={horizontal ? styles.horizItem : styles.item}>
|
||||
<View style={[
|
||||
styles.row, horizontal && {width: HORIZ_WIDTH}, fixedHeight && {height: ITEM_HEIGHT}]}>
|
||||
{!item.noImage && <Image style={styles.thumb} source={imgSource} />}
|
||||
<Text
|
||||
style={styles.text}
|
||||
numberOfLines={(horizontal || fixedHeight) ? 3 : undefined}>
|
||||
{item.title} - {item.text}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const renderStackedItem = ({item}: {item: Item}) => {
|
||||
const itemHash = Math.abs(hashCode(item.title));
|
||||
const imgSource = THUMB_URLS[itemHash % THUMB_URLS.length];
|
||||
return (
|
||||
<View style={styles.stacked}>
|
||||
<Text style={styles.stackedText}>{item.title} - {item.text}</Text>
|
||||
<Image style={styles.thumb} source={imgSource} />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
class FooterComponent extends React.PureComponent<{}> {
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.headerFooterContainer}>
|
||||
<SeparatorComponent />
|
||||
<View style={styles.headerFooter}>
|
||||
<Text>LIST FOOTER</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HeaderComponent extends React.PureComponent<{}> {
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.headerFooterContainer}>
|
||||
<View style={styles.headerFooter}>
|
||||
<Text>LIST HEADER</Text>
|
||||
</View>
|
||||
<SeparatorComponent />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SeparatorComponent extends React.PureComponent<{}> {
|
||||
render() {
|
||||
return <View style={styles.separator} />;
|
||||
}
|
||||
}
|
||||
|
||||
class ItemSeparatorComponent extends React.PureComponent<$FlowFixMeProps> {
|
||||
render() {
|
||||
const style = this.props.highlighted
|
||||
? [styles.itemSeparator, {marginLeft: 0, backgroundColor: 'rgb(217, 217, 217)'}]
|
||||
: styles.itemSeparator;
|
||||
return <View style={style} />;
|
||||
}
|
||||
}
|
||||
|
||||
class Spindicator extends React.PureComponent<$FlowFixMeProps> {
|
||||
render() {
|
||||
return (
|
||||
<Animated.View style={[styles.spindicator, {
|
||||
transform: [
|
||||
{rotate: this.props.value.interpolate({
|
||||
inputRange: [0, 5000],
|
||||
outputRange: ['0deg', '360deg'],
|
||||
extrapolate: 'extend',
|
||||
})}
|
||||
]
|
||||
}]} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const THUMB_URLS = [
|
||||
require('./Thumbnails/like.png'),
|
||||
require('./Thumbnails/dislike.png'),
|
||||
require('./Thumbnails/call.png'),
|
||||
require('./Thumbnails/fist.png'),
|
||||
require('./Thumbnails/bandaged.png'),
|
||||
require('./Thumbnails/flowers.png'),
|
||||
require('./Thumbnails/heart.png'),
|
||||
require('./Thumbnails/liking.png'),
|
||||
require('./Thumbnails/party.png'),
|
||||
require('./Thumbnails/poke.png'),
|
||||
require('./Thumbnails/superlike.png'),
|
||||
require('./Thumbnails/victory.png'),
|
||||
];
|
||||
|
||||
const LOREM_IPSUM = 'Lorem ipsum dolor sit amet, ius ad pertinax oportere accommodare, an vix \
|
||||
civibus corrumpit referrentur. Te nam case ludus inciderint, te mea facilisi adipiscing. Sea id \
|
||||
integre luptatum. In tota sale consequuntur nec. Erat ocurreret mei ei. Eu paulo sapientem \
|
||||
vulputate est, vel an accusam intellegam interesset. Nam eu stet pericula reprimique, ea vim illud \
|
||||
modus, putant invidunt reprehendunt ne qui.';
|
||||
|
||||
/* eslint no-bitwise: 0 */
|
||||
function hashCode(str: string): number {
|
||||
let hash = 15;
|
||||
for (let ii = str.length - 1; ii >= 0; ii--) {
|
||||
hash = ((hash << 5) - hash) + str.charCodeAt(ii);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
const HEADER = {height: 30, width: 100};
|
||||
const SEPARATOR_HEIGHT = StyleSheet.hairlineWidth;
|
||||
|
||||
function getItemLayout(data: any, index: number, horizontal?: boolean) {
|
||||
const [length, separator, header] = horizontal ?
|
||||
[HORIZ_WIDTH, 0, HEADER.width] : [ITEM_HEIGHT, SEPARATOR_HEIGHT, HEADER.height];
|
||||
return {length, offset: (length + separator) * index + header, index};
|
||||
}
|
||||
|
||||
function pressItem(context: Object, key: string) {
|
||||
const index = Number(key);
|
||||
const pressed = !context.state.data[index].pressed;
|
||||
context.setState((state) => {
|
||||
const newData = [...state.data];
|
||||
newData[index] = {
|
||||
...state.data[index],
|
||||
pressed,
|
||||
title: 'Item ' + key + (pressed ? ' (pressed)' : ''),
|
||||
};
|
||||
return {data: newData};
|
||||
});
|
||||
}
|
||||
|
||||
function renderSmallSwitchOption(context: Object, key: string) {
|
||||
if (Platform.isTVOS) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<View style={styles.option}>
|
||||
<Text>{key}:</Text>
|
||||
<Switch
|
||||
style={styles.smallSwitch}
|
||||
value={context.state[key]}
|
||||
onValueChange={(value) => context.setState({[key]: value})}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function PlainInput(props: Object) {
|
||||
return (
|
||||
<TextInput
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
clearButtonMode="always"
|
||||
underlineColorAndroid="transparent"
|
||||
style={styles.searchTextInput}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
headerFooter: {
|
||||
...HEADER,
|
||||
alignSelf: 'center',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
headerFooterContainer: {
|
||||
backgroundColor: 'rgb(239, 239, 244)',
|
||||
},
|
||||
horizItem: {
|
||||
alignSelf: 'flex-start', // Necessary for touch highlight
|
||||
},
|
||||
item: {
|
||||
flex: 1,
|
||||
},
|
||||
itemSeparator: {
|
||||
height: SEPARATOR_HEIGHT,
|
||||
backgroundColor: 'rgb(200, 199, 204)',
|
||||
marginLeft: 60,
|
||||
},
|
||||
option: {
|
||||
flexDirection: 'row',
|
||||
padding: 8,
|
||||
paddingRight: 0,
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
padding: 10,
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
searchTextInput: {
|
||||
backgroundColor: 'white',
|
||||
borderColor: '#cccccc',
|
||||
borderRadius: 3,
|
||||
borderWidth: 1,
|
||||
paddingLeft: 8,
|
||||
paddingVertical: 0,
|
||||
height: 26,
|
||||
fontSize: 14,
|
||||
flexGrow: 1,
|
||||
},
|
||||
separator: {
|
||||
height: SEPARATOR_HEIGHT,
|
||||
backgroundColor: 'rgb(200, 199, 204)',
|
||||
},
|
||||
smallSwitch: Platform.select({
|
||||
android: {
|
||||
top: 1,
|
||||
margin: -6,
|
||||
transform: [{scale: 0.7}],
|
||||
},
|
||||
ios: {
|
||||
top: 4,
|
||||
margin: -10,
|
||||
transform: [{scale: 0.5}],
|
||||
},
|
||||
}),
|
||||
stacked: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'white',
|
||||
padding: 10,
|
||||
},
|
||||
thumb: {
|
||||
width: 50,
|
||||
height: 50,
|
||||
left: -5,
|
||||
},
|
||||
spindicator: {
|
||||
marginLeft: 'auto',
|
||||
marginTop: 8,
|
||||
width: 2,
|
||||
height: 16,
|
||||
backgroundColor: 'darkgray',
|
||||
},
|
||||
stackedText: {
|
||||
padding: 4,
|
||||
fontSize: 18,
|
||||
},
|
||||
text: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
FooterComponent,
|
||||
HeaderComponent,
|
||||
ItemComponent,
|
||||
ItemSeparatorComponent,
|
||||
PlainInput,
|
||||
SeparatorComponent,
|
||||
Spindicator,
|
||||
genItemData,
|
||||
getItemLayout,
|
||||
pressItem,
|
||||
renderSmallSwitchOption,
|
||||
renderStackedItem,
|
||||
};
|
||||
151
packages/examples/src/RNTester/ListViewExample.js
Normal file
151
packages/examples/src/RNTester/ListViewExample.js
Normal file
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule ListViewExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var createReactClass = require('create-react-class');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
Image,
|
||||
ListView,
|
||||
TouchableHighlight,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
var RNTesterPage = require('./RNTesterPage');
|
||||
|
||||
var ListViewSimpleExample = createReactClass({
|
||||
displayName: 'ListViewSimpleExample',
|
||||
statics: {
|
||||
title: '<ListView>',
|
||||
description: 'Performant, scrollable list of data.'
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
|
||||
return {
|
||||
dataSource: ds.cloneWithRows(this._genRows({})),
|
||||
};
|
||||
},
|
||||
|
||||
_pressData: ({}: {[key: number]: boolean}),
|
||||
|
||||
UNSAFE_componentWillMount: function() {
|
||||
this._pressData = {};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<RNTesterPage
|
||||
title={this.props.navigator ? null : '<ListView>'}
|
||||
noSpacer={true}
|
||||
noScroll={true}>
|
||||
<ListView
|
||||
dataSource={this.state.dataSource}
|
||||
renderRow={this._renderRow}
|
||||
renderSeparator={this._renderSeparator}
|
||||
/>
|
||||
</RNTesterPage>
|
||||
);
|
||||
},
|
||||
|
||||
_renderRow: function(rowData: string, sectionID: number, rowID: number, highlightRow: (sectionID: number, rowID: number) => void) {
|
||||
var rowHash = Math.abs(hashCode(rowData));
|
||||
var imgSource = THUMB_URLS[rowHash % THUMB_URLS.length];
|
||||
return (
|
||||
<TouchableHighlight onPress={() => {
|
||||
this._pressRow(rowID);
|
||||
highlightRow(sectionID, rowID);
|
||||
}}>
|
||||
<View>
|
||||
<View style={styles.row}>
|
||||
<Image style={styles.thumb} source={imgSource} />
|
||||
<Text style={styles.text}>
|
||||
{rowData + ' - ' + LOREM_IPSUM.substr(0, rowHash % 301 + 10)}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
},
|
||||
|
||||
_genRows: function(pressData: {[key: number]: boolean}): Array<string> {
|
||||
var dataBlob = [];
|
||||
for (var ii = 0; ii < 100; ii++) {
|
||||
var pressedText = pressData[ii] ? ' (pressed)' : '';
|
||||
dataBlob.push('Row ' + ii + pressedText);
|
||||
}
|
||||
return dataBlob;
|
||||
},
|
||||
|
||||
_pressRow: function(rowID: number) {
|
||||
this._pressData[rowID] = !this._pressData[rowID];
|
||||
this.setState({dataSource: this.state.dataSource.cloneWithRows(
|
||||
this._genRows(this._pressData)
|
||||
)});
|
||||
},
|
||||
|
||||
_renderSeparator: function(sectionID: number, rowID: number, adjacentRowHighlighted: bool) {
|
||||
return (
|
||||
<View
|
||||
key={`${sectionID}-${rowID}`}
|
||||
style={{
|
||||
height: adjacentRowHighlighted ? 4 : 1,
|
||||
backgroundColor: adjacentRowHighlighted ? '#3B5998' : '#CCCCCC',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var THUMB_URLS = [
|
||||
require('./Thumbnails/like.png'),
|
||||
require('./Thumbnails/dislike.png'),
|
||||
require('./Thumbnails/call.png'),
|
||||
require('./Thumbnails/fist.png'),
|
||||
require('./Thumbnails/bandaged.png'),
|
||||
require('./Thumbnails/flowers.png'),
|
||||
require('./Thumbnails/heart.png'),
|
||||
require('./Thumbnails/liking.png'),
|
||||
require('./Thumbnails/party.png'),
|
||||
require('./Thumbnails/poke.png'),
|
||||
require('./Thumbnails/superlike.png'),
|
||||
require('./Thumbnails/victory.png'),
|
||||
];
|
||||
var LOREM_IPSUM = 'Lorem ipsum dolor sit amet, ius ad pertinax oportere accommodare, an vix civibus corrumpit referrentur. Te nam case ludus inciderint, te mea facilisi adipiscing. Sea id integre luptatum. In tota sale consequuntur nec. Erat ocurreret mei ei. Eu paulo sapientem vulputate est, vel an accusam intellegam interesset. Nam eu stet pericula reprimique, ea vim illud modus, putant invidunt reprehendunt ne qui.';
|
||||
|
||||
/* eslint no-bitwise: 0 */
|
||||
var hashCode = function(str) {
|
||||
var hash = 15;
|
||||
for (var ii = str.length - 1; ii >= 0; ii--) {
|
||||
hash = ((hash << 5) - hash) + str.charCodeAt(ii);
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
padding: 10,
|
||||
backgroundColor: '#F6F6F6',
|
||||
},
|
||||
thumb: {
|
||||
width: 64,
|
||||
height: 64,
|
||||
},
|
||||
text: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = ListViewSimpleExample;
|
||||
148
packages/examples/src/RNTester/ListViewGridLayoutExample.js
Normal file
148
packages/examples/src/RNTester/ListViewGridLayoutExample.js
Normal file
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule ListViewGridLayoutExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var createReactClass = require('create-react-class');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
Image,
|
||||
ListView,
|
||||
TouchableHighlight,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
var THUMB_URLS = [
|
||||
require('./Thumbnails/like.png'),
|
||||
require('./Thumbnails/dislike.png'),
|
||||
require('./Thumbnails/call.png'),
|
||||
require('./Thumbnails/fist.png'),
|
||||
require('./Thumbnails/bandaged.png'),
|
||||
require('./Thumbnails/flowers.png'),
|
||||
require('./Thumbnails/heart.png'),
|
||||
require('./Thumbnails/liking.png'),
|
||||
require('./Thumbnails/party.png'),
|
||||
require('./Thumbnails/poke.png'),
|
||||
require('./Thumbnails/superlike.png'),
|
||||
require('./Thumbnails/victory.png'),
|
||||
];
|
||||
|
||||
var ListViewGridLayoutExample = createReactClass({
|
||||
displayName: 'ListViewGridLayoutExample',
|
||||
|
||||
statics: {
|
||||
title: '<ListView> - Grid Layout',
|
||||
description: 'Flexbox grid layout.'
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
|
||||
return {
|
||||
dataSource: ds.cloneWithRows(this._genRows({})),
|
||||
};
|
||||
},
|
||||
|
||||
_pressData: ({}: {[key: number]: boolean}),
|
||||
|
||||
UNSAFE_componentWillMount: function() {
|
||||
this._pressData = {};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
// ListView wraps ScrollView and so takes on its properties.
|
||||
// With that in mind you can use the ScrollView's contentContainerStyle prop to style the items.
|
||||
<ListView
|
||||
contentContainerStyle={styles.list}
|
||||
dataSource={this.state.dataSource}
|
||||
initialListSize={21}
|
||||
pageSize={3} // should be a multiple of the no. of visible cells per row
|
||||
scrollRenderAheadDistance={500}
|
||||
renderRow={this._renderRow}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
_renderRow: function(rowData: string, sectionID: number, rowID: number) {
|
||||
var rowHash = Math.abs(hashCode(rowData));
|
||||
var imgSource = THUMB_URLS[rowHash % THUMB_URLS.length];
|
||||
return (
|
||||
<TouchableHighlight onPress={() => this._pressRow(rowID)} underlayColor="transparent">
|
||||
<View>
|
||||
<View style={styles.row}>
|
||||
<Image style={styles.thumb} source={imgSource} />
|
||||
<Text style={styles.text}>
|
||||
{rowData}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
},
|
||||
|
||||
_genRows: function(pressData: {[key: number]: boolean}): Array<string> {
|
||||
var dataBlob = [];
|
||||
for (var ii = 0; ii < 100; ii++) {
|
||||
var pressedText = pressData[ii] ? ' (X)' : '';
|
||||
dataBlob.push('Cell ' + ii + pressedText);
|
||||
}
|
||||
return dataBlob;
|
||||
},
|
||||
|
||||
_pressRow: function(rowID: number) {
|
||||
this._pressData[rowID] = !this._pressData[rowID];
|
||||
this.setState({dataSource: this.state.dataSource.cloneWithRows(
|
||||
this._genRows(this._pressData)
|
||||
)});
|
||||
},
|
||||
});
|
||||
|
||||
/* eslint no-bitwise: 0 */
|
||||
var hashCode = function(str) {
|
||||
var hash = 15;
|
||||
for (var ii = str.length - 1; ii >= 0; ii--) {
|
||||
hash = ((hash << 5) - hash) + str.charCodeAt(ii);
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
list: {
|
||||
justifyContent: 'space-around',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
alignItems: 'flex-start'
|
||||
},
|
||||
row: {
|
||||
justifyContent: 'center',
|
||||
padding: 5,
|
||||
margin: 3,
|
||||
width: 100,
|
||||
height: 100,
|
||||
backgroundColor: '#F6F6F6',
|
||||
alignItems: 'center',
|
||||
borderWidth: 1,
|
||||
borderRadius: 5,
|
||||
borderColor: '#CCC'
|
||||
},
|
||||
thumb: {
|
||||
width: 64,
|
||||
height: 64
|
||||
},
|
||||
text: {
|
||||
flex: 1,
|
||||
marginTop: 5,
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = ListViewGridLayoutExample;
|
||||
276
packages/examples/src/RNTester/ListViewPagingExample.js
Normal file
276
packages/examples/src/RNTester/ListViewPagingExample.js
Normal file
@@ -0,0 +1,276 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule ListViewPagingExample
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
Image,
|
||||
LayoutAnimation,
|
||||
ListView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
var NativeModules = require('NativeModules');
|
||||
var {
|
||||
UIManager,
|
||||
} = NativeModules;
|
||||
|
||||
var THUMB_URLS = [
|
||||
require('./Thumbnails/like.png'),
|
||||
require('./Thumbnails/dislike.png'),
|
||||
require('./Thumbnails/call.png'),
|
||||
require('./Thumbnails/fist.png'),
|
||||
require('./Thumbnails/bandaged.png'),
|
||||
require('./Thumbnails/flowers.png'),
|
||||
require('./Thumbnails/heart.png'),
|
||||
require('./Thumbnails/liking.png'),
|
||||
require('./Thumbnails/party.png'),
|
||||
require('./Thumbnails/poke.png'),
|
||||
require('./Thumbnails/superlike.png'),
|
||||
require('./Thumbnails/victory.png'),
|
||||
];
|
||||
var NUM_SECTIONS = 100;
|
||||
var NUM_ROWS_PER_SECTION = 10;
|
||||
|
||||
class Thumb extends React.Component<{}, $FlowFixMeState> {
|
||||
UNSAFE_componentWillMount() {
|
||||
UIManager.setLayoutAnimationEnabledExperimental &&
|
||||
UIManager.setLayoutAnimationEnabledExperimental(true);
|
||||
}
|
||||
|
||||
_getThumbIdx = () => {
|
||||
return Math.floor(Math.random() * THUMB_URLS.length);
|
||||
};
|
||||
|
||||
_onPressThumb = () => {
|
||||
var config = layoutAnimationConfigs[this.state.thumbIndex % layoutAnimationConfigs.length];
|
||||
LayoutAnimation.configureNext(config);
|
||||
this.setState({
|
||||
thumbIndex: this._getThumbIdx(),
|
||||
dir: this.state.dir === 'row' ? 'column' : 'row',
|
||||
});
|
||||
};
|
||||
|
||||
state = {thumbIndex: this._getThumbIdx(), dir: 'row'};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={this._onPressThumb}
|
||||
style={[styles.buttonContents, {flexDirection: this.state.dir}]}>
|
||||
<Image style={styles.img} source={THUMB_URLS[this.state.thumbIndex]} />
|
||||
<Image style={styles.img} source={THUMB_URLS[this.state.thumbIndex]} />
|
||||
<Image style={styles.img} source={THUMB_URLS[this.state.thumbIndex]} />
|
||||
{this.state.dir === 'column' ?
|
||||
<Text>
|
||||
Oooo, look at this new text! So awesome it may just be crazy.
|
||||
Let me keep typing here so it wraps at least one line.
|
||||
</Text> :
|
||||
<Text />
|
||||
}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ListViewPagingExample extends React.Component<$FlowFixMeProps, *> {
|
||||
static title = '<ListView> - Paging';
|
||||
static description = 'Floating headers & layout animations.';
|
||||
|
||||
// $FlowFixMe found when converting React.createClass to ES6
|
||||
constructor(props) {
|
||||
super(props);
|
||||
var getSectionData = (dataBlob, sectionID) => {
|
||||
return dataBlob[sectionID];
|
||||
};
|
||||
var getRowData = (dataBlob, sectionID, rowID) => {
|
||||
return dataBlob[rowID];
|
||||
};
|
||||
|
||||
var dataSource = new ListView.DataSource({
|
||||
getRowData: getRowData,
|
||||
getSectionHeaderData: getSectionData,
|
||||
rowHasChanged: (row1, row2) => row1 !== row2,
|
||||
sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
|
||||
});
|
||||
|
||||
var dataBlob = {};
|
||||
var sectionIDs = [];
|
||||
var rowIDs = [];
|
||||
for (var ii = 0; ii < NUM_SECTIONS; ii++) {
|
||||
var sectionName = 'Section ' + ii;
|
||||
sectionIDs.push(sectionName);
|
||||
dataBlob[sectionName] = sectionName;
|
||||
rowIDs[ii] = [];
|
||||
|
||||
for (var jj = 0; jj < NUM_ROWS_PER_SECTION; jj++) {
|
||||
var rowName = 'S' + ii + ', R' + jj;
|
||||
rowIDs[ii].push(rowName);
|
||||
dataBlob[rowName] = rowName;
|
||||
}
|
||||
}
|
||||
|
||||
this.state = {
|
||||
dataSource: dataSource.cloneWithRowsAndSections(dataBlob, sectionIDs, rowIDs),
|
||||
headerPressCount: 0,
|
||||
};
|
||||
}
|
||||
|
||||
renderRow = (rowData: string, sectionID: string, rowID: string): React.Element<any> => {
|
||||
return (<Thumb text={rowData}/>);
|
||||
};
|
||||
|
||||
renderSectionHeader = (sectionData: string, sectionID: string) => {
|
||||
return (
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.text}>
|
||||
{sectionData}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
renderHeader = () => {
|
||||
var headerLikeText = this.state.headerPressCount % 2 ?
|
||||
<View><Text style={styles.text}>1 Like</Text></View> :
|
||||
null;
|
||||
return (
|
||||
<TouchableOpacity onPress={this._onPressHeader} style={styles.header}>
|
||||
{headerLikeText}
|
||||
<View>
|
||||
<Text style={styles.text}>
|
||||
Table Header (click me)
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
renderFooter = () => {
|
||||
return (
|
||||
<View style={styles.header}>
|
||||
<Text onPress={() => console.log('Footer!')} style={styles.text}>
|
||||
Table Footer
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ListView
|
||||
style={styles.listview}
|
||||
dataSource={this.state.dataSource}
|
||||
onChangeVisibleRows={(visibleRows, changedRows) => console.log({visibleRows, changedRows})}
|
||||
renderHeader={this.renderHeader}
|
||||
renderFooter={this.renderFooter}
|
||||
renderSectionHeader={this.renderSectionHeader}
|
||||
renderRow={this.renderRow}
|
||||
initialListSize={10}
|
||||
pageSize={4}
|
||||
scrollRenderAheadDistance={500}
|
||||
stickySectionHeadersEnabled
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_onPressHeader = () => {
|
||||
var config = layoutAnimationConfigs[Math.floor(this.state.headerPressCount / 2) % layoutAnimationConfigs.length];
|
||||
LayoutAnimation.configureNext(config);
|
||||
this.setState({headerPressCount: this.state.headerPressCount + 1});
|
||||
};
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
listview: {
|
||||
backgroundColor: '#B0C4DE',
|
||||
},
|
||||
header: {
|
||||
height: 40,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#3B5998',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
text: {
|
||||
color: 'white',
|
||||
paddingHorizontal: 8,
|
||||
},
|
||||
rowText: {
|
||||
color: '#888888',
|
||||
},
|
||||
thumbText: {
|
||||
fontSize: 20,
|
||||
color: '#888888',
|
||||
},
|
||||
buttonContents: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginHorizontal: 5,
|
||||
marginVertical: 3,
|
||||
padding: 5,
|
||||
backgroundColor: '#EAEAEA',
|
||||
borderRadius: 3,
|
||||
paddingVertical: 10,
|
||||
},
|
||||
img: {
|
||||
width: 64,
|
||||
height: 64,
|
||||
marginHorizontal: 10,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
section: {
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'flex-start',
|
||||
padding: 6,
|
||||
backgroundColor: '#5890ff',
|
||||
},
|
||||
});
|
||||
|
||||
var animations = {
|
||||
layout: {
|
||||
spring: {
|
||||
duration: 750,
|
||||
create: {
|
||||
duration: 300,
|
||||
type: LayoutAnimation.Types.easeInEaseOut,
|
||||
property: LayoutAnimation.Properties.opacity,
|
||||
},
|
||||
update: {
|
||||
type: LayoutAnimation.Types.spring,
|
||||
springDamping: 0.4,
|
||||
},
|
||||
},
|
||||
easeInEaseOut: {
|
||||
duration: 300,
|
||||
create: {
|
||||
type: LayoutAnimation.Types.easeInEaseOut,
|
||||
property: LayoutAnimation.Properties.scaleXY,
|
||||
},
|
||||
update: {
|
||||
delay: 100,
|
||||
type: LayoutAnimation.Types.easeInEaseOut,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var layoutAnimationConfigs = [
|
||||
animations.layout.spring,
|
||||
animations.layout.easeInEaseOut,
|
||||
];
|
||||
|
||||
module.exports = ListViewPagingExample;
|
||||
194
packages/examples/src/RNTester/MaskedViewExample.js
Normal file
194
packages/examples/src/RNTester/MaskedViewExample.js
Normal file
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule MaskedViewExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const RNTesterBlock = require('RNTesterBlock');
|
||||
const RNTesterPage = require('RNTesterPage');
|
||||
const {
|
||||
Animated,
|
||||
Image,
|
||||
MaskedViewIOS,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = require('react-native');
|
||||
|
||||
class MaskedViewExample extends React.Component<{}, $FlowFixMeState> {
|
||||
static title = '<MaskedViewIOS>';
|
||||
static description = 'Renders the child view with a mask specified in the `renderMask` prop.';
|
||||
|
||||
state = {
|
||||
alternateChildren: true,
|
||||
};
|
||||
|
||||
_maskRotateAnimatedValue = new Animated.Value(0);
|
||||
_maskScaleAnimatedValue = new Animated.Value(1);
|
||||
|
||||
componentDidMount() {
|
||||
setInterval(() => {
|
||||
this.setState(state => ({
|
||||
alternateChildren: !state.alternateChildren,
|
||||
}));
|
||||
}, 1000);
|
||||
|
||||
Animated.loop(
|
||||
Animated.sequence([
|
||||
Animated.timing(this._maskScaleAnimatedValue, {
|
||||
toValue: 1.3,
|
||||
timing: 750,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
Animated.timing(this._maskScaleAnimatedValue, {
|
||||
toValue: 1,
|
||||
timing: 750,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
])
|
||||
).start();
|
||||
|
||||
Animated.loop(
|
||||
Animated.timing(this._maskRotateAnimatedValue, {
|
||||
toValue: 360,
|
||||
timing: 2000,
|
||||
useNativeDriver: true,
|
||||
})
|
||||
).start();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<RNTesterPage title="<MaskedViewIOS>">
|
||||
<RNTesterBlock title="Basic Mask">
|
||||
<View style={{ width: 300, height: 300, alignSelf: 'center' }}>
|
||||
<MaskedViewIOS
|
||||
style={{ flex: 1 }}
|
||||
maskElement={
|
||||
<View style={styles.maskContainerStyle}>
|
||||
<Text style={styles.maskTextStyle}>
|
||||
Basic Mask
|
||||
</Text>
|
||||
</View>
|
||||
}>
|
||||
<View style={{ flex: 1, backgroundColor: 'blue' }} />
|
||||
<View style={{ flex: 1, backgroundColor: 'red' }} />
|
||||
</MaskedViewIOS>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title="Image Mask">
|
||||
<View
|
||||
style={{
|
||||
width: 300,
|
||||
height: 300,
|
||||
alignSelf: 'center',
|
||||
backgroundColor: '#eeeeee',
|
||||
}}>
|
||||
<MaskedViewIOS
|
||||
style={{ flex: 1 }}
|
||||
maskElement={
|
||||
<View style={styles.maskContainerStyle}>
|
||||
<Image
|
||||
style={{ height: 200, width: 200 }}
|
||||
source={require('./imageMask.png')}
|
||||
/>
|
||||
</View>
|
||||
}>
|
||||
<View style={styles.maskContainerStyle}>
|
||||
<Image
|
||||
resizeMode="cover"
|
||||
style={{ width: 200, height: 200 }}
|
||||
source={{
|
||||
uri:
|
||||
'https://38.media.tumblr.com/9e9bd08c6e2d10561dd1fb4197df4c4e/tumblr_mfqekpMktw1rn90umo1_500.gif',
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</MaskedViewIOS>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title="Animated Mask">
|
||||
<View style={{ width: 300, height: 300, alignSelf: 'center' }}>
|
||||
<MaskedViewIOS
|
||||
style={{ flex: 1 }}
|
||||
maskElement={
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.maskContainerStyle,
|
||||
{ transform: [{ scale: this._maskScaleAnimatedValue }] },
|
||||
]}>
|
||||
<Text style={styles.maskTextStyle}>
|
||||
Basic Mask
|
||||
</Text>
|
||||
</Animated.View>
|
||||
}>
|
||||
<Animated.View
|
||||
style={{
|
||||
flex: 1,
|
||||
transform: [
|
||||
{
|
||||
rotate: this._maskRotateAnimatedValue.interpolate({
|
||||
inputRange: [0, 360],
|
||||
outputRange: ['0deg', '360deg'],
|
||||
}),
|
||||
},
|
||||
],
|
||||
}}>
|
||||
<View style={{ flex: 1, backgroundColor: 'blue' }} />
|
||||
<View style={{ flex: 1, backgroundColor: 'red' }} />
|
||||
</Animated.View>
|
||||
</MaskedViewIOS>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title="Mask w/ Changing Children">
|
||||
<View style={{ width: 300, height: 300, alignSelf: 'center' }}>
|
||||
<MaskedViewIOS
|
||||
style={{ flex: 1 }}
|
||||
maskElement={
|
||||
<View style={styles.maskContainerStyle}>
|
||||
<Text style={styles.maskTextStyle}>
|
||||
Basic Mask
|
||||
</Text>
|
||||
</View>
|
||||
}>
|
||||
{this.state.alternateChildren
|
||||
? [
|
||||
<View
|
||||
key={1}
|
||||
style={{ flex: 1, backgroundColor: 'blue' }}
|
||||
/>,
|
||||
<View
|
||||
key={2}
|
||||
style={{ flex: 1, backgroundColor: 'red' }}
|
||||
/>,
|
||||
]
|
||||
: null}
|
||||
</MaskedViewIOS>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
</RNTesterPage>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
maskContainerStyle: {
|
||||
flex: 1,
|
||||
backgroundColor: 'transparent',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
maskTextStyle: {
|
||||
backgroundColor: 'transparent',
|
||||
fontSize: 40,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = MaskedViewExample;
|
||||
249
packages/examples/src/RNTester/ModalExample.js
Normal file
249
packages/examples/src/RNTester/ModalExample.js
Normal file
@@ -0,0 +1,249 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule ModalExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
Modal,
|
||||
Picker,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
Switch,
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
const Item = Picker.Item;
|
||||
|
||||
exports.displayName = (undefined: ?string);
|
||||
exports.framework = 'React';
|
||||
exports.title = '<Modal>';
|
||||
exports.description = 'Component for presenting modal views.';
|
||||
|
||||
class Button extends React.Component<$FlowFixMeProps, $FlowFixMeState> {
|
||||
state = {
|
||||
active: false,
|
||||
};
|
||||
|
||||
_onHighlight = () => {
|
||||
this.setState({active: true});
|
||||
};
|
||||
|
||||
_onUnhighlight = () => {
|
||||
this.setState({active: false});
|
||||
};
|
||||
|
||||
render() {
|
||||
var colorStyle = {
|
||||
color: this.state.active ? '#fff' : '#000',
|
||||
};
|
||||
return (
|
||||
<TouchableHighlight
|
||||
onHideUnderlay={this._onUnhighlight}
|
||||
onPress={this.props.onPress}
|
||||
onShowUnderlay={this._onHighlight}
|
||||
style={[styles.button, this.props.style]}
|
||||
underlayColor="#a9d9d4">
|
||||
<Text style={[styles.buttonText, colorStyle]}>{this.props.children}</Text>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const supportedOrientationsPickerValues = [
|
||||
['portrait'],
|
||||
['landscape'],
|
||||
['landscape-left'],
|
||||
['portrait', 'landscape-right'],
|
||||
['portrait', 'landscape'],
|
||||
[],
|
||||
];
|
||||
|
||||
class ModalExample extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
animationType: 'none',
|
||||
modalVisible: false,
|
||||
transparent: false,
|
||||
presentationStyle: 'fullScreen',
|
||||
selectedSupportedOrientation: 0,
|
||||
currentOrientation: 'unknown',
|
||||
};
|
||||
|
||||
_setModalVisible = (visible) => {
|
||||
this.setState({modalVisible: visible});
|
||||
};
|
||||
|
||||
_setAnimationType = (type) => {
|
||||
this.setState({animationType: type});
|
||||
};
|
||||
|
||||
_toggleTransparent = () => {
|
||||
this.setState({transparent: !this.state.transparent});
|
||||
};
|
||||
|
||||
renderSwitch() {
|
||||
if (Platform.isTVOS) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Switch value={this.state.transparent} onValueChange={this._toggleTransparent} />
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
var modalBackgroundStyle = {
|
||||
backgroundColor: this.state.transparent ? 'rgba(0, 0, 0, 0.5)' : '#f5fcff',
|
||||
};
|
||||
var innerContainerTransparentStyle = this.state.transparent
|
||||
? {backgroundColor: '#fff', padding: 20}
|
||||
: null;
|
||||
var activeButtonStyle = {
|
||||
backgroundColor: '#ddd'
|
||||
};
|
||||
|
||||
return (
|
||||
<View>
|
||||
<Modal
|
||||
animationType={this.state.animationType}
|
||||
presentationStyle={this.state.presentationStyle}
|
||||
transparent={this.state.transparent}
|
||||
visible={this.state.modalVisible}
|
||||
onRequestClose={() => this._setModalVisible(false)}
|
||||
supportedOrientations={supportedOrientationsPickerValues[this.state.selectedSupportedOrientation]}
|
||||
onOrientationChange={evt => this.setState({currentOrientation: evt.nativeEvent.orientation})}
|
||||
>
|
||||
<View style={[styles.container, modalBackgroundStyle]}>
|
||||
<View style={[styles.innerContainer, innerContainerTransparentStyle]}>
|
||||
<Text>This modal was presented {this.state.animationType === 'none' ? 'without' : 'with'} animation.</Text>
|
||||
<Text>It is currently displayed in {this.state.currentOrientation} mode.</Text>
|
||||
<Button
|
||||
onPress={this._setModalVisible.bind(this, false)}
|
||||
style={styles.modalButton}>
|
||||
Close
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.rowTitle}>Animation Type</Text>
|
||||
<Button onPress={this._setAnimationType.bind(this, 'none')} style={this.state.animationType === 'none' ? activeButtonStyle : {}}>
|
||||
none
|
||||
</Button>
|
||||
<Button onPress={this._setAnimationType.bind(this, 'slide')} style={this.state.animationType === 'slide' ? activeButtonStyle : {}}>
|
||||
slide
|
||||
</Button>
|
||||
<Button onPress={this._setAnimationType.bind(this, 'fade')} style={this.state.animationType === 'fade' ? activeButtonStyle : {}}>
|
||||
fade
|
||||
</Button>
|
||||
</View>
|
||||
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.rowTitle}>Transparent</Text>
|
||||
{this.renderSwitch()}
|
||||
</View>
|
||||
{this.renderPickers()}
|
||||
<Button onPress={this._setModalVisible.bind(this, true)}>
|
||||
Present
|
||||
</Button>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
renderPickers() {
|
||||
if (Platform.isTVOS) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<View>
|
||||
<View>
|
||||
<Text style={styles.rowTitle}>Presentation style</Text>
|
||||
<Picker
|
||||
selectedValue={this.state.presentationStyle}
|
||||
onValueChange={(presentationStyle) => this.setState({presentationStyle})}
|
||||
itemStyle={styles.pickerItem}
|
||||
>
|
||||
<Item label="Full Screen" value="fullScreen" />
|
||||
<Item label="Page Sheet" value="pageSheet" />
|
||||
<Item label="Form Sheet" value="formSheet" />
|
||||
<Item label="Over Full Screen" value="overFullScreen" />
|
||||
<Item label="Default presentationStyle" value={null} />
|
||||
</Picker>
|
||||
</View>
|
||||
|
||||
<View>
|
||||
<Text style={styles.rowTitle}>Supported orientations</Text>
|
||||
<Picker
|
||||
selectedValue={this.state.selectedSupportedOrientation}
|
||||
onValueChange={(_, i) => this.setState({selectedSupportedOrientation: i})}
|
||||
itemStyle={styles.pickerItem}
|
||||
>
|
||||
<Item label="Portrait" value={0} />
|
||||
<Item label="Landscape" value={1} />
|
||||
<Item label="Landscape left" value={2} />
|
||||
<Item label="Portrait and landscape right" value={3} />
|
||||
<Item label="Portrait and landscape" value={4} />
|
||||
<Item label="Default supportedOrientations" value={5} />
|
||||
</Picker>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'Modal Presentation',
|
||||
description: 'Modals can be presented with or without animation',
|
||||
render: () => <ModalExample />,
|
||||
},
|
||||
];
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
padding: 20,
|
||||
},
|
||||
innerContainer: {
|
||||
borderRadius: 10,
|
||||
alignItems: 'center',
|
||||
},
|
||||
row: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
marginBottom: 20,
|
||||
},
|
||||
rowTitle: {
|
||||
flex: 1,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
button: {
|
||||
borderRadius: 5,
|
||||
flexGrow: 1,
|
||||
height: 44,
|
||||
alignSelf: 'stretch',
|
||||
justifyContent: 'center',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
buttonText: {
|
||||
fontSize: 18,
|
||||
margin: 5,
|
||||
textAlign: 'center',
|
||||
},
|
||||
modalButton: {
|
||||
marginTop: 10,
|
||||
},
|
||||
pickerItem: {
|
||||
fontSize: 16,
|
||||
},
|
||||
});
|
||||
154
packages/examples/src/RNTester/MultiColumnExample.js
Normal file
154
packages/examples/src/RNTester/MultiColumnExample.js
Normal file
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule MultiColumnExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const {
|
||||
FlatList,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
const RNTesterPage = require('./RNTesterPage');
|
||||
|
||||
const infoLog = require('infoLog');
|
||||
|
||||
const {
|
||||
FooterComponent,
|
||||
HeaderComponent,
|
||||
ItemComponent,
|
||||
PlainInput,
|
||||
SeparatorComponent,
|
||||
genItemData,
|
||||
getItemLayout,
|
||||
pressItem,
|
||||
renderSmallSwitchOption,
|
||||
} = require('./ListExampleShared');
|
||||
|
||||
class MultiColumnExample extends React.PureComponent<$FlowFixMeProps, $FlowFixMeState> {
|
||||
static title = '<FlatList> - MultiColumn';
|
||||
static description = 'Performant, scrollable grid of data.';
|
||||
|
||||
state = {
|
||||
data: genItemData(1000),
|
||||
filterText: '',
|
||||
fixedHeight: true,
|
||||
logViewable: false,
|
||||
numColumns: 2,
|
||||
virtualized: true,
|
||||
};
|
||||
_onChangeFilterText = (filterText) => {
|
||||
this.setState(() => ({filterText}));
|
||||
};
|
||||
_onChangeNumColumns = (numColumns) => {
|
||||
this.setState(() => ({numColumns: Number(numColumns)}));
|
||||
};
|
||||
render() {
|
||||
const filterRegex = new RegExp(String(this.state.filterText), 'i');
|
||||
const filter = (item) => (filterRegex.test(item.text) || filterRegex.test(item.title));
|
||||
const filteredData = this.state.data.filter(filter);
|
||||
return (
|
||||
<RNTesterPage
|
||||
title={this.props.navigator ? null : '<FlatList> - MultiColumn'}
|
||||
noSpacer={true}
|
||||
noScroll={true}>
|
||||
<View style={styles.searchRow}>
|
||||
<View style={styles.row}>
|
||||
<PlainInput
|
||||
onChangeText={this._onChangeFilterText}
|
||||
placeholder="Search..."
|
||||
value={this.state.filterText}
|
||||
/>
|
||||
<Text> numColumns: </Text>
|
||||
<PlainInput
|
||||
clearButtonMode="never"
|
||||
onChangeText={this._onChangeNumColumns}
|
||||
value={this.state.numColumns ? String(this.state.numColumns) : ''}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.row}>
|
||||
{renderSmallSwitchOption(this, 'virtualized')}
|
||||
{renderSmallSwitchOption(this, 'fixedHeight')}
|
||||
{renderSmallSwitchOption(this, 'logViewable')}
|
||||
</View>
|
||||
</View>
|
||||
<SeparatorComponent />
|
||||
<FlatList
|
||||
ListFooterComponent={FooterComponent}
|
||||
ListHeaderComponent={HeaderComponent}
|
||||
getItemLayout={this.state.fixedHeight ? this._getItemLayout : undefined}
|
||||
data={filteredData}
|
||||
key={this.state.numColumns + (this.state.fixedHeight ? 'f' : 'v')}
|
||||
numColumns={this.state.numColumns || 1}
|
||||
onRefresh={() => alert('onRefresh: nothing to refresh :P')}
|
||||
refreshing={false}
|
||||
renderItem={this._renderItemComponent}
|
||||
disableVirtualization={!this.state.virtualized}
|
||||
onViewableItemsChanged={this._onViewableItemsChanged}
|
||||
legacyImplementation={false}
|
||||
/>
|
||||
</RNTesterPage>
|
||||
);
|
||||
}
|
||||
_getItemLayout(data: any, index: number): {length: number, offset: number, index: number} {
|
||||
const length = getItemLayout(data, index).length + 2 * (CARD_MARGIN + BORDER_WIDTH);
|
||||
return {length, offset: length * index, index};
|
||||
}
|
||||
_renderItemComponent = ({item}) => {
|
||||
return (
|
||||
<View style={styles.card}>
|
||||
<ItemComponent
|
||||
item={item}
|
||||
fixedHeight={this.state.fixedHeight}
|
||||
onPress={this._pressItem}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
// This is called when items change viewability by scrolling into or out of the viewable area.
|
||||
_onViewableItemsChanged = (info: {
|
||||
changed: Array<{
|
||||
key: string, isViewable: boolean, item: {columns: Array<*>}, index: ?number, section?: any
|
||||
}>},
|
||||
) => {
|
||||
// Impressions can be logged here
|
||||
if (this.state.logViewable) {
|
||||
infoLog('onViewableItemsChanged: ', info.changed.map((v) => ({...v, item: '...'})));
|
||||
}
|
||||
};
|
||||
_pressItem = (key: string) => {
|
||||
pressItem(this, key);
|
||||
};
|
||||
}
|
||||
|
||||
const CARD_MARGIN = 4;
|
||||
const BORDER_WIDTH = 1;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
card: {
|
||||
margin: CARD_MARGIN,
|
||||
borderRadius: 10,
|
||||
flex: 1,
|
||||
overflow: 'hidden',
|
||||
borderColor: 'lightgray',
|
||||
borderWidth: BORDER_WIDTH,
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
searchRow: {
|
||||
padding: 10,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = MultiColumnExample;
|
||||
622
packages/examples/src/RNTester/NativeAnimationsExample.js
Normal file
622
packages/examples/src/RNTester/NativeAnimationsExample.js
Normal file
@@ -0,0 +1,622 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule NativeAnimationsExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const {
|
||||
View,
|
||||
Text,
|
||||
Animated,
|
||||
StyleSheet,
|
||||
TouchableWithoutFeedback,
|
||||
Slider,
|
||||
} = ReactNative;
|
||||
|
||||
var AnimatedSlider = Animated.createAnimatedComponent(Slider);
|
||||
|
||||
class Tester extends React.Component<$FlowFixMeProps, $FlowFixMeState> {
|
||||
state = {
|
||||
native: new Animated.Value(0),
|
||||
js: new Animated.Value(0),
|
||||
};
|
||||
|
||||
current = 0;
|
||||
|
||||
onPress = () => {
|
||||
const animConfig = this.current && this.props.reverseConfig
|
||||
? this.props.reverseConfig
|
||||
: this.props.config;
|
||||
this.current = this.current ? 0 : 1;
|
||||
const config: Object = {
|
||||
...animConfig,
|
||||
toValue: this.current,
|
||||
};
|
||||
|
||||
Animated[this.props.type](this.state.native, {
|
||||
...config,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
Animated[this.props.type](this.state.js, {
|
||||
...config,
|
||||
useNativeDriver: false,
|
||||
}).start();
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TouchableWithoutFeedback onPress={this.onPress}>
|
||||
<View>
|
||||
<View>
|
||||
<Text>Native:</Text>
|
||||
</View>
|
||||
<View style={styles.row}>
|
||||
{this.props.children(this.state.native)}
|
||||
</View>
|
||||
<View>
|
||||
<Text>JavaScript:</Text>
|
||||
</View>
|
||||
<View style={styles.row}>
|
||||
{this.props.children(this.state.js)}
|
||||
</View>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ValueListenerExample extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
anim: new Animated.Value(0),
|
||||
progress: 0,
|
||||
};
|
||||
_current = 0;
|
||||
|
||||
componentDidMount() {
|
||||
this.state.anim.addListener(e => this.setState({progress: e.value}));
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.state.anim.removeAllListeners();
|
||||
}
|
||||
|
||||
_onPress = () => {
|
||||
this._current = this._current ? 0 : 1;
|
||||
const config = {
|
||||
duration: 1000,
|
||||
toValue: this._current,
|
||||
};
|
||||
|
||||
Animated.timing(this.state.anim, {
|
||||
...config,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TouchableWithoutFeedback onPress={this._onPress}>
|
||||
<View>
|
||||
<View style={styles.row}>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.block,
|
||||
{
|
||||
opacity: this.state.anim,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
<Text>Value: {this.state.progress}</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LoopExample extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
value: new Animated.Value(0),
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
Animated.loop(
|
||||
Animated.timing(this.state.value, {
|
||||
toValue: 1,
|
||||
duration: 5000,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
).start();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.row}>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.block,
|
||||
{
|
||||
opacity: this.state.value.interpolate({
|
||||
inputRange: [0, 0.5, 1],
|
||||
outputRange: [0, 1, 0],
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const RNTesterSettingSwitchRow = require('RNTesterSettingSwitchRow');
|
||||
class InternalSettings extends React.Component<{}, {busyTime: number | string, filteredStall: number}> {
|
||||
_stallInterval: ?number;
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<RNTesterSettingSwitchRow
|
||||
initialValue={false}
|
||||
label="Force JS Stalls"
|
||||
onEnable={() => {
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment
|
||||
* suppresses an error found when Flow v0.63 was deployed. To see
|
||||
* the error delete this comment and run Flow. */
|
||||
this._stallInterval = setInterval(() => {
|
||||
const start = Date.now();
|
||||
console.warn('burn CPU');
|
||||
while (Date.now() - start < 100) {
|
||||
}
|
||||
}, 300);
|
||||
}}
|
||||
onDisable={() => {
|
||||
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment
|
||||
* suppresses an error found when Flow v0.63 was deployed. To see
|
||||
* the error delete this comment and run Flow. */
|
||||
clearInterval(this._stallInterval || 0);
|
||||
}}
|
||||
/>
|
||||
<RNTesterSettingSwitchRow
|
||||
initialValue={false}
|
||||
label="Track JS Stalls"
|
||||
onEnable={() => {
|
||||
require('JSEventLoopWatchdog').install({thresholdMS: 25});
|
||||
this.setState({busyTime: '<none>'});
|
||||
require('JSEventLoopWatchdog').addHandler({
|
||||
onStall: ({busyTime}) =>
|
||||
this.setState(state => ({
|
||||
busyTime,
|
||||
filteredStall: (state.filteredStall || 0) * 0.97 +
|
||||
busyTime * 0.03,
|
||||
})),
|
||||
});
|
||||
}}
|
||||
onDisable={() => {
|
||||
console.warn('Cannot disable yet....');
|
||||
}}
|
||||
/>
|
||||
{this.state &&
|
||||
<Text>
|
||||
{`JS Stall filtered: ${Math.round(this.state.filteredStall)}, `}
|
||||
{`last: ${this.state.busyTime}`}
|
||||
</Text>}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EventExample extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
scrollX: new Animated.Value(0),
|
||||
};
|
||||
|
||||
render() {
|
||||
const opacity = this.state.scrollX.interpolate({
|
||||
inputRange: [0, 200],
|
||||
outputRange: [1, 0],
|
||||
});
|
||||
return (
|
||||
<View>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.block,
|
||||
{
|
||||
opacity,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Animated.ScrollView
|
||||
horizontal
|
||||
style={{height: 100, marginTop: 16}}
|
||||
scrollEventThrottle={16}
|
||||
onScroll={Animated.event(
|
||||
[{nativeEvent: {contentOffset: {x: this.state.scrollX}}}],
|
||||
{useNativeDriver: true},
|
||||
)}>
|
||||
<View
|
||||
style={{
|
||||
width: 600,
|
||||
backgroundColor: '#eee',
|
||||
justifyContent: 'center',
|
||||
}}>
|
||||
<Text>Scroll me!</Text>
|
||||
</View>
|
||||
</Animated.ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TrackingExample extends React.Component<$FlowFixMeProps, $FlowFixMeState> {
|
||||
state = {
|
||||
native: new Animated.Value(0),
|
||||
toNative: new Animated.Value(0),
|
||||
js: new Animated.Value(0),
|
||||
toJS: new Animated.Value(0),
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
// we configure spring to take a bit of time to settle so that the user
|
||||
// have time to click many times and see "toValue" getting updated and
|
||||
const longSettlingSpring = {
|
||||
tension: 20,
|
||||
friction: 0.5,
|
||||
};
|
||||
Animated.spring(this.state.native, {
|
||||
...longSettlingSpring,
|
||||
toValue: this.state.toNative,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
Animated.spring(this.state.js, {
|
||||
...longSettlingSpring,
|
||||
toValue: this.state.toJS,
|
||||
useNativeDriver: false,
|
||||
}).start();
|
||||
}
|
||||
|
||||
onPress = () => {
|
||||
// select next value to be tracked by random
|
||||
const nextValue = Math.random() * 200;
|
||||
this.state.toNative.setValue(nextValue);
|
||||
this.state.toJS.setValue(nextValue);
|
||||
};
|
||||
|
||||
renderBlock = (anim, dest) => [
|
||||
<Animated.View key="line" style={[styles.line, { transform: [{ translateX: dest }]}]}/>,
|
||||
<Animated.View key="block" style={[styles.block, { transform: [{ translateX: anim }]}]}/>,
|
||||
]
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TouchableWithoutFeedback onPress={this.onPress}>
|
||||
<View>
|
||||
<View>
|
||||
<Text>Native:</Text>
|
||||
</View>
|
||||
<View style={styles.row}>
|
||||
{this.renderBlock(this.state.native, this.state.toNative)}
|
||||
</View>
|
||||
<View>
|
||||
<Text>JavaScript:</Text>
|
||||
</View>
|
||||
<View style={styles.row}>
|
||||
{this.renderBlock(this.state.js, this.state.toJS)}
|
||||
</View>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
row: {
|
||||
padding: 10,
|
||||
zIndex: 1,
|
||||
},
|
||||
block: {
|
||||
width: 50,
|
||||
height: 50,
|
||||
backgroundColor: 'blue',
|
||||
},
|
||||
line: {
|
||||
position: 'absolute',
|
||||
left: 35,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
width: 1,
|
||||
backgroundColor: 'red',
|
||||
},
|
||||
});
|
||||
|
||||
exports.framework = 'React';
|
||||
exports.title = 'Native Animated Example';
|
||||
exports.description = 'Test out Native Animations';
|
||||
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'Multistage With Multiply and rotation',
|
||||
render: function() {
|
||||
return (
|
||||
<Tester type="timing" config={{duration: 1000}}>
|
||||
{anim => (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.block,
|
||||
{
|
||||
transform: [
|
||||
{
|
||||
translateX: anim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, 200],
|
||||
}),
|
||||
},
|
||||
{
|
||||
translateY: anim.interpolate({
|
||||
inputRange: [0, 0.5, 1],
|
||||
outputRange: [0, 50, 0],
|
||||
}),
|
||||
},
|
||||
{
|
||||
rotate: anim.interpolate({
|
||||
inputRange: [0, 0.5, 1],
|
||||
outputRange: ['0deg', '90deg', '0deg'],
|
||||
}),
|
||||
},
|
||||
],
|
||||
opacity: Animated.multiply(
|
||||
anim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [1, 0],
|
||||
}),
|
||||
anim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0.25, 1],
|
||||
}),
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</Tester>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Multistage With Multiply',
|
||||
render: function() {
|
||||
return (
|
||||
<Tester type="timing" config={{duration: 1000}}>
|
||||
{anim => (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.block,
|
||||
{
|
||||
transform: [
|
||||
{
|
||||
translateX: anim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, 200],
|
||||
}),
|
||||
},
|
||||
{
|
||||
translateY: anim.interpolate({
|
||||
inputRange: [0, 0.5, 1],
|
||||
outputRange: [0, 50, 0],
|
||||
}),
|
||||
},
|
||||
],
|
||||
opacity: Animated.multiply(
|
||||
anim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [1, 0],
|
||||
}),
|
||||
anim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0.25, 1],
|
||||
}),
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</Tester>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Scale interpolation with clamping',
|
||||
render: function() {
|
||||
return (
|
||||
<Tester type="timing" config={{duration: 1000}}>
|
||||
{anim => (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.block,
|
||||
{
|
||||
transform: [
|
||||
{
|
||||
scale: anim.interpolate({
|
||||
inputRange: [0, 0.5],
|
||||
outputRange: [1, 1.4],
|
||||
extrapolateRight: 'clamp',
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</Tester>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Opacity with delay',
|
||||
render: function() {
|
||||
return (
|
||||
<Tester type="timing" config={{duration: 1000, delay: 1000}}>
|
||||
{anim => (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.block,
|
||||
{
|
||||
opacity: anim,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</Tester>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Rotate interpolation',
|
||||
render: function() {
|
||||
return (
|
||||
<Tester type="timing" config={{duration: 1000}}>
|
||||
{anim => (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.block,
|
||||
{
|
||||
transform: [
|
||||
{
|
||||
rotate: anim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: ['0deg', '90deg'],
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</Tester>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'translateX => Animated.spring (bounciness/speed)',
|
||||
render: function() {
|
||||
return (
|
||||
<Tester type="spring" config={{bounciness: 0}}>
|
||||
{anim => (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.block,
|
||||
{
|
||||
transform: [
|
||||
{
|
||||
translateX: anim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, 100],
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</Tester>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'translateX => Animated.spring (stiffness/damping/mass)',
|
||||
render: function() {
|
||||
return (
|
||||
<Tester type="spring" config={{stiffness: 1000, damping: 500, mass: 3 }}>
|
||||
{anim => (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.block,
|
||||
{
|
||||
transform: [
|
||||
{
|
||||
translateX: anim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, 100],
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</Tester>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'translateX => Animated.decay',
|
||||
render: function() {
|
||||
return (
|
||||
<Tester
|
||||
type="decay"
|
||||
config={{velocity: 0.5}}
|
||||
reverseConfig={{velocity: -0.5}}>
|
||||
{anim => (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.block,
|
||||
{
|
||||
transform: [
|
||||
{
|
||||
translateX: anim,
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</Tester>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Drive custom property',
|
||||
render: function() {
|
||||
return (
|
||||
<Tester type="timing" config={{duration: 1000}}>
|
||||
{anim => <AnimatedSlider style={{}} value={anim} />}
|
||||
</Tester>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Animated value listener',
|
||||
render: function() {
|
||||
return <ValueListenerExample />;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Animated loop',
|
||||
render: function() {
|
||||
return <LoopExample />;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Animated events',
|
||||
render: function() {
|
||||
return <EventExample />;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Animated Tracking - tap me many times',
|
||||
render: function() {
|
||||
return <TrackingExample />;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Internal Settings',
|
||||
render: function() {
|
||||
return <InternalSettings />;
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @providesModule NavigatorIOSBarStyleExample
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
NavigatorIOS,
|
||||
StatusBar,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View
|
||||
} = ReactNative;
|
||||
|
||||
class EmptyPage extends React.Component<{
|
||||
text: string,
|
||||
}> {
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.emptyPage}>
|
||||
<Text style={styles.emptyPageText}>
|
||||
{this.props.text}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NavigatorIOSColors extends React.Component<{}> {
|
||||
static title = '<NavigatorIOS> - Custom Bar Style';
|
||||
static description = 'iOS navigation with custom nav bar colors';
|
||||
|
||||
render() {
|
||||
// Set StatusBar with light contents to get better contrast
|
||||
StatusBar.setBarStyle('light-content');
|
||||
|
||||
return (
|
||||
<NavigatorIOS
|
||||
style={styles.container}
|
||||
initialRoute={{
|
||||
component: EmptyPage,
|
||||
title: '<NavigatorIOS>',
|
||||
rightButtonTitle: 'Done',
|
||||
onRightButtonPress: () => {
|
||||
StatusBar.setBarStyle('default');
|
||||
this.props.onExampleExit();
|
||||
},
|
||||
passProps: {
|
||||
text: 'The nav bar is black with barStyle prop.',
|
||||
},
|
||||
}}
|
||||
barStyle="black"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
emptyPage: {
|
||||
flex: 1,
|
||||
paddingTop: 64,
|
||||
},
|
||||
emptyPageText: {
|
||||
margin: 10,
|
||||
},
|
||||
});
|
||||
|
||||
NavigatorIOSColors.external = true;
|
||||
|
||||
module.exports = NavigatorIOSColors;
|
||||
81
packages/examples/src/RNTester/NavigatorIOSColorsExample.js
Normal file
81
packages/examples/src/RNTester/NavigatorIOSColorsExample.js
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule NavigatorIOSColorsExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
NavigatorIOS,
|
||||
StatusBar,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View
|
||||
} = ReactNative;
|
||||
|
||||
class EmptyPage extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.emptyPage}>
|
||||
<Text style={styles.emptyPageText}>
|
||||
{this.props.text}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NavigatorIOSColors extends React.Component {
|
||||
static title = '<NavigatorIOS> - Custom Colors';
|
||||
static description = 'iOS navigation with custom nav bar colors';
|
||||
|
||||
render() {
|
||||
// Set StatusBar with light contents to get better contrast
|
||||
StatusBar.setBarStyle('light-content');
|
||||
|
||||
return (
|
||||
<NavigatorIOS
|
||||
style={styles.container}
|
||||
initialRoute={{
|
||||
component: EmptyPage,
|
||||
title: '<NavigatorIOS>',
|
||||
rightButtonTitle: 'Done',
|
||||
onRightButtonPress: () => {
|
||||
StatusBar.setBarStyle('default');
|
||||
this.props.onExampleExit();
|
||||
},
|
||||
passProps: {
|
||||
text: 'The nav bar has custom colors with tintColor, ' +
|
||||
'barTintColor and titleTextColor props.',
|
||||
},
|
||||
}}
|
||||
tintColor="#FFFFFF"
|
||||
barTintColor="#183E63"
|
||||
titleTextColor="#FFFFFF"
|
||||
translucent={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
emptyPage: {
|
||||
flex: 1,
|
||||
paddingTop: 64,
|
||||
},
|
||||
emptyPageText: {
|
||||
margin: 10,
|
||||
},
|
||||
});
|
||||
|
||||
NavigatorIOSColors.external = true;
|
||||
|
||||
module.exports = NavigatorIOSColors;
|
||||
300
packages/examples/src/RNTester/NavigatorIOSExample.js
Normal file
300
packages/examples/src/RNTester/NavigatorIOSExample.js
Normal file
@@ -0,0 +1,300 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule NavigatorIOSExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const ViewExample = require('./ViewExample');
|
||||
|
||||
const createExamplePage = require('./createExamplePage');
|
||||
const nativeImageSource = require('nativeImageSource');
|
||||
const {
|
||||
AlertIOS,
|
||||
NavigatorIOS,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
class EmptyPage extends React.Component<$FlowFixMeProps> {
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.emptyPage}>
|
||||
<Text style={styles.emptyPageText}>
|
||||
{this.props.text}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NavigatorIOSExamplePage extends React.Component<$FlowFixMeProps> {
|
||||
render() {
|
||||
var recurseTitle = 'Recurse Navigation';
|
||||
if (!this.props.depth || this.props.depth === 1) {
|
||||
recurseTitle += ' - more examples here';
|
||||
}
|
||||
return (
|
||||
<ScrollView style={styles.list}>
|
||||
<View style={styles.line}/>
|
||||
<View style={styles.group}>
|
||||
{this._renderRow(recurseTitle, () => {
|
||||
this.props.navigator.push({
|
||||
title: NavigatorIOSExample.title,
|
||||
component: NavigatorIOSExamplePage,
|
||||
backButtonTitle: 'Custom Back',
|
||||
passProps: {depth: this.props.depth ? this.props.depth + 1 : 1},
|
||||
});
|
||||
})}
|
||||
{this._renderRow('Push View Example', () => {
|
||||
this.props.navigator.push({
|
||||
title: 'Very Long Custom View Example Title',
|
||||
component: createExamplePage(null, ViewExample),
|
||||
});
|
||||
})}
|
||||
{this._renderRow('Custom title image Example', () => {
|
||||
this.props.navigator.push({
|
||||
title: 'Custom title image Example',
|
||||
titleImage: require('./relay.png'),
|
||||
component: createExamplePage(null, ViewExample),
|
||||
});
|
||||
})}
|
||||
{this._renderRow('Custom Right Button', () => {
|
||||
this.props.navigator.push({
|
||||
title: NavigatorIOSExample.title,
|
||||
component: EmptyPage,
|
||||
rightButtonTitle: 'Cancel',
|
||||
onRightButtonPress: () => this.props.navigator.pop(),
|
||||
passProps: {
|
||||
text: 'This page has a right button in the nav bar',
|
||||
}
|
||||
});
|
||||
})}
|
||||
{this._renderRow('Custom Right System Button', () => {
|
||||
this.props.navigator.push({
|
||||
title: NavigatorIOSExample.title,
|
||||
component: EmptyPage,
|
||||
rightButtonSystemIcon: 'bookmarks',
|
||||
onRightButtonPress: () => this.props.navigator.pop(),
|
||||
passProps: {
|
||||
text: 'This page has a right system button in the nav bar',
|
||||
}
|
||||
});
|
||||
})}
|
||||
{this._renderRow('Custom Left & Right Icons', () => {
|
||||
this.props.navigator.push({
|
||||
title: NavigatorIOSExample.title,
|
||||
component: EmptyPage,
|
||||
leftButtonTitle: 'Custom Left',
|
||||
onLeftButtonPress: () => this.props.navigator.pop(),
|
||||
rightButtonIcon: nativeImageSource({
|
||||
ios: 'NavBarButtonPlus',
|
||||
width: 17,
|
||||
height: 17
|
||||
}),
|
||||
onRightButtonPress: () => {
|
||||
AlertIOS.alert(
|
||||
'Bar Button Action',
|
||||
'Recognized a tap on the bar button icon',
|
||||
[
|
||||
{
|
||||
text: 'OK',
|
||||
onPress: () => console.log('Tapped OK'),
|
||||
},
|
||||
]
|
||||
);
|
||||
},
|
||||
passProps: {
|
||||
text: 'This page has an icon for the right button in the nav bar',
|
||||
}
|
||||
});
|
||||
})}
|
||||
{this._renderRow('Custom Left & Right System Icons', () => {
|
||||
this.props.navigator.push({
|
||||
title: NavigatorIOSExample.title,
|
||||
component: EmptyPage,
|
||||
leftButtonSystemIcon: 'cancel',
|
||||
onLeftButtonPress: () => this.props.navigator.pop(),
|
||||
rightButtonSystemIcon: 'search',
|
||||
onRightButtonPress: () => {
|
||||
AlertIOS.alert(
|
||||
'Bar Button Action',
|
||||
'Recognized a tap on the bar button icon',
|
||||
[
|
||||
{
|
||||
text: 'OK',
|
||||
onPress: () => console.log('Tapped OK'),
|
||||
},
|
||||
]
|
||||
);
|
||||
},
|
||||
passProps: {
|
||||
text: 'This page has an icon for the right button in the nav bar',
|
||||
}
|
||||
});
|
||||
})}
|
||||
{this._renderRow('Pop', () => {
|
||||
this.props.navigator.pop();
|
||||
})}
|
||||
{this._renderRow('Pop to top', () => {
|
||||
this.props.navigator.popToTop();
|
||||
})}
|
||||
{this._renderReplace()}
|
||||
{this._renderReplacePrevious()}
|
||||
{this._renderReplacePreviousAndPop()}
|
||||
{this._renderRow('Exit NavigatorIOS Example', this.props.onExampleExit)}
|
||||
</View>
|
||||
<View style={styles.line}/>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
_renderReplace = () => {
|
||||
if (!this.props.depth) {
|
||||
// this is to avoid replacing the top of the stack
|
||||
return null;
|
||||
}
|
||||
return this._renderRow('Replace here', () => {
|
||||
var prevRoute = this.props.route;
|
||||
this.props.navigator.replace({
|
||||
title: 'New Navigation',
|
||||
component: EmptyPage,
|
||||
rightButtonTitle: 'Undo',
|
||||
onRightButtonPress: () => this.props.navigator.replace(prevRoute),
|
||||
passProps: {
|
||||
text: 'The component is replaced, but there is currently no ' +
|
||||
'way to change the right button or title of the current route',
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
_renderReplacePrevious = () => {
|
||||
if (!this.props.depth || this.props.depth < 2) {
|
||||
// this is to avoid replacing the top of the stack
|
||||
return null;
|
||||
}
|
||||
return this._renderRow('Replace previous', () => {
|
||||
this.props.navigator.replacePrevious({
|
||||
title: 'Replaced',
|
||||
component: EmptyPage,
|
||||
passProps: {
|
||||
text: 'This is a replaced "previous" page',
|
||||
},
|
||||
wrapperStyle: styles.customWrapperStyle,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
_renderReplacePreviousAndPop = () => {
|
||||
if (!this.props.depth || this.props.depth < 2) {
|
||||
// this is to avoid replacing the top of the stack
|
||||
return null;
|
||||
}
|
||||
return this._renderRow('Replace previous and pop', () => {
|
||||
this.props.navigator.replacePreviousAndPop({
|
||||
title: 'Replaced and Popped',
|
||||
component: EmptyPage,
|
||||
passProps: {
|
||||
text: 'This is a replaced "previous" page',
|
||||
},
|
||||
wrapperStyle: styles.customWrapperStyle,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
_renderRow = (title: string, onPress: Function) => {
|
||||
return (
|
||||
<View>
|
||||
<TouchableHighlight onPress={onPress}>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.rowText}>
|
||||
{title}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<View style={styles.separator} />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
class NavigatorIOSExample extends React.Component<$FlowFixMeProps> {
|
||||
static title = '<NavigatorIOS>';
|
||||
static description = 'iOS navigation capabilities';
|
||||
static external = true;
|
||||
|
||||
render() {
|
||||
const {onExampleExit} = this.props;
|
||||
return (
|
||||
<NavigatorIOS
|
||||
style={styles.container}
|
||||
initialRoute={{
|
||||
title: NavigatorIOSExample.title,
|
||||
component: NavigatorIOSExamplePage,
|
||||
passProps: {onExampleExit},
|
||||
}}
|
||||
tintColor="#008888"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
customWrapperStyle: {
|
||||
backgroundColor: '#bbdddd',
|
||||
},
|
||||
emptyPage: {
|
||||
flex: 1,
|
||||
paddingTop: 64,
|
||||
},
|
||||
emptyPageText: {
|
||||
margin: 10,
|
||||
},
|
||||
list: {
|
||||
backgroundColor: '#eeeeee',
|
||||
marginTop: 10,
|
||||
},
|
||||
group: {
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
groupSpace: {
|
||||
height: 15,
|
||||
},
|
||||
line: {
|
||||
backgroundColor: '#bbbbbb',
|
||||
height: StyleSheet.hairlineWidth,
|
||||
},
|
||||
row: {
|
||||
backgroundColor: 'white',
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 15,
|
||||
paddingVertical: 15,
|
||||
},
|
||||
separator: {
|
||||
height: StyleSheet.hairlineWidth,
|
||||
backgroundColor: '#bbbbbb',
|
||||
marginLeft: 15,
|
||||
},
|
||||
rowNote: {
|
||||
fontSize: 17,
|
||||
},
|
||||
rowText: {
|
||||
fontSize: 17,
|
||||
fontWeight: '500',
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = NavigatorIOSExample;
|
||||
183
packages/examples/src/RNTester/NetInfoExample.js
Normal file
183
packages/examples/src/RNTester/NetInfoExample.js
Normal file
@@ -0,0 +1,183 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule NetInfoExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const {
|
||||
NetInfo,
|
||||
Text,
|
||||
View,
|
||||
TouchableWithoutFeedback,
|
||||
} = ReactNative;
|
||||
|
||||
class ConnectionInfoSubscription extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
connectionInfoHistory: [],
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
NetInfo.addEventListener(
|
||||
'change',
|
||||
this._handleConnectionInfoChange
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
NetInfo.removeEventListener(
|
||||
'change',
|
||||
this._handleConnectionInfoChange
|
||||
);
|
||||
}
|
||||
|
||||
_handleConnectionInfoChange = (connectionInfo) => {
|
||||
const connectionInfoHistory = this.state.connectionInfoHistory.slice();
|
||||
connectionInfoHistory.push(connectionInfo);
|
||||
this.setState({
|
||||
connectionInfoHistory,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Text>{JSON.stringify(this.state.connectionInfoHistory)}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectionInfoCurrent extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
connectionInfo: null,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
NetInfo.addEventListener(
|
||||
'change',
|
||||
this._handleConnectionInfoChange
|
||||
);
|
||||
NetInfo.fetch().done(
|
||||
(connectionInfo) => { this.setState({connectionInfo}); }
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
NetInfo.removeEventListener(
|
||||
'change',
|
||||
this._handleConnectionInfoChange
|
||||
);
|
||||
}
|
||||
|
||||
_handleConnectionInfoChange = (connectionInfo) => {
|
||||
this.setState({
|
||||
connectionInfo,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Text>{this.state.connectionInfo}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class IsConnected extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
isConnected: null,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
NetInfo.isConnected.addEventListener(
|
||||
'change',
|
||||
this._handleConnectivityChange
|
||||
);
|
||||
NetInfo.isConnected.fetch().done(
|
||||
(isConnected) => { this.setState({isConnected}); }
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
NetInfo.isConnected.removeEventListener(
|
||||
'change',
|
||||
this._handleConnectivityChange
|
||||
);
|
||||
}
|
||||
|
||||
_handleConnectivityChange = (isConnected) => {
|
||||
this.setState({
|
||||
isConnected,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Text>{this.state.isConnected ? 'Online' : 'Offline'}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class IsConnectionExpensive extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
isConnectionExpensive: (null : ?boolean),
|
||||
};
|
||||
|
||||
_checkIfExpensive = () => {
|
||||
NetInfo.isConnectionExpensive().then(
|
||||
isConnectionExpensive => { this.setState({isConnectionExpensive}); }
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<TouchableWithoutFeedback onPress={this._checkIfExpensive}>
|
||||
<View>
|
||||
<Text>Click to see if connection is expensive:
|
||||
{this.state.isConnectionExpensive === true ? 'Expensive' :
|
||||
this.state.isConnectionExpensive === false ? 'Not expensive'
|
||||
: 'Unknown'}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
exports.title = 'NetInfo';
|
||||
exports.description = 'Monitor network status';
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'NetInfo.isConnected',
|
||||
description: 'Asynchronously load and observe connectivity',
|
||||
render(): React.Element<any> { return <IsConnected />; }
|
||||
},
|
||||
{
|
||||
title: 'NetInfo.update',
|
||||
description: 'Asynchronously load and observe connectionInfo',
|
||||
render(): React.Element<any> { return <ConnectionInfoCurrent />; }
|
||||
},
|
||||
{
|
||||
title: 'NetInfo.updateHistory',
|
||||
description: 'Observed updates to connectionInfo',
|
||||
render(): React.Element<any> { return <ConnectionInfoSubscription />; }
|
||||
},
|
||||
{
|
||||
platform: 'android',
|
||||
title: 'NetInfo.isConnectionExpensive (Android)',
|
||||
description: 'Asynchronously check isConnectionExpensive',
|
||||
render(): React.Element<any> { return <IsConnectionExpensive />; }
|
||||
},
|
||||
];
|
||||
66
packages/examples/src/RNTester/OrientationChangeExample.js
Normal file
66
packages/examples/src/RNTester/OrientationChangeExample.js
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule OrientationChangeExample
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const {
|
||||
DeviceEventEmitter,
|
||||
Text,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
import type EmitterSubscription from 'EmitterSubscription';
|
||||
|
||||
class OrientationChangeExample extends React.Component<{}, $FlowFixMeState> {
|
||||
_orientationSubscription: EmitterSubscription;
|
||||
|
||||
state = {
|
||||
currentOrientation: '',
|
||||
orientationDegrees: 0,
|
||||
isLandscape: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this._orientationSubscription = DeviceEventEmitter.addListener(
|
||||
'namedOrientationDidChange', this._onOrientationChange,
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._orientationSubscription.remove();
|
||||
}
|
||||
|
||||
_onOrientationChange = (orientation: Object) => {
|
||||
this.setState({
|
||||
currentOrientation: orientation.name,
|
||||
orientationDegrees: orientation.rotationDegrees,
|
||||
isLandscape: orientation.isLandscape,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Text>{JSON.stringify(this.state)}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
exports.title = 'OrientationChangeExample';
|
||||
exports.description = 'listening to orientation changes';
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'OrientationChangeExample',
|
||||
description: 'listening to device orientation changes',
|
||||
render() { return <OrientationChangeExample />; },
|
||||
},
|
||||
];
|
||||
130
packages/examples/src/RNTester/PanResponderExample.js
Normal file
130
packages/examples/src/RNTester/PanResponderExample.js
Normal file
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow weak
|
||||
* @providesModule PanResponderExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var createReactClass = require('create-react-class');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
PanResponder,
|
||||
StyleSheet,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
var CIRCLE_SIZE = 80;
|
||||
|
||||
var PanResponderExample = createReactClass({
|
||||
displayName: 'PanResponderExample',
|
||||
|
||||
statics: {
|
||||
title: 'PanResponder Sample',
|
||||
description: 'Shows the use of PanResponder to provide basic gesture handling.',
|
||||
},
|
||||
|
||||
_panResponder: {},
|
||||
_previousLeft: 0,
|
||||
_previousTop: 0,
|
||||
_circleStyles: {},
|
||||
circle: (null : ?{ setNativeProps(props: Object): void }),
|
||||
|
||||
UNSAFE_componentWillMount: function() {
|
||||
this._panResponder = PanResponder.create({
|
||||
onStartShouldSetPanResponder: this._handleStartShouldSetPanResponder,
|
||||
onMoveShouldSetPanResponder: this._handleMoveShouldSetPanResponder,
|
||||
onPanResponderGrant: this._handlePanResponderGrant,
|
||||
onPanResponderMove: this._handlePanResponderMove,
|
||||
onPanResponderRelease: this._handlePanResponderEnd,
|
||||
onPanResponderTerminate: this._handlePanResponderEnd,
|
||||
});
|
||||
this._previousLeft = 20;
|
||||
this._previousTop = 84;
|
||||
this._circleStyles = {
|
||||
style: {
|
||||
left: this._previousLeft,
|
||||
top: this._previousTop,
|
||||
backgroundColor: 'green',
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this._updateNativeStyles();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<View
|
||||
style={styles.container}>
|
||||
<View
|
||||
ref={(circle) => {
|
||||
this.circle = circle;
|
||||
}}
|
||||
style={styles.circle}
|
||||
{...this._panResponder.panHandlers}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
|
||||
_highlight: function() {
|
||||
this._circleStyles.style.backgroundColor = 'blue';
|
||||
this._updateNativeStyles();
|
||||
},
|
||||
|
||||
_unHighlight: function() {
|
||||
this._circleStyles.style.backgroundColor = 'green';
|
||||
this._updateNativeStyles();
|
||||
},
|
||||
|
||||
_updateNativeStyles: function() {
|
||||
this.circle && this.circle.setNativeProps(this._circleStyles);
|
||||
},
|
||||
|
||||
_handleStartShouldSetPanResponder: function(e: Object, gestureState: Object): boolean {
|
||||
// Should we become active when the user presses down on the circle?
|
||||
return true;
|
||||
},
|
||||
|
||||
_handleMoveShouldSetPanResponder: function(e: Object, gestureState: Object): boolean {
|
||||
// Should we become active when the user moves a touch over the circle?
|
||||
return true;
|
||||
},
|
||||
|
||||
_handlePanResponderGrant: function(e: Object, gestureState: Object) {
|
||||
this._highlight();
|
||||
},
|
||||
_handlePanResponderMove: function(e: Object, gestureState: Object) {
|
||||
this._circleStyles.style.left = this._previousLeft + gestureState.dx;
|
||||
this._circleStyles.style.top = this._previousTop + gestureState.dy;
|
||||
this._updateNativeStyles();
|
||||
},
|
||||
_handlePanResponderEnd: function(e: Object, gestureState: Object) {
|
||||
this._unHighlight();
|
||||
this._previousLeft += gestureState.dx;
|
||||
this._previousTop += gestureState.dy;
|
||||
},
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
circle: {
|
||||
width: CIRCLE_SIZE,
|
||||
height: CIRCLE_SIZE,
|
||||
borderRadius: CIRCLE_SIZE / 2,
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
paddingTop: 64,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = PanResponderExample;
|
||||
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule PermissionsExampleAndroid
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const {
|
||||
PermissionsAndroid,
|
||||
Picker,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
const Item = Picker.Item;
|
||||
|
||||
exports.displayName = (undefined: ?string);
|
||||
exports.framework = 'React';
|
||||
exports.title = 'PermissionsAndroid';
|
||||
exports.description = 'Permissions example for API 23+.';
|
||||
|
||||
class PermissionsExample extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
permission: PermissionsAndroid.PERMISSIONS.CAMERA,
|
||||
hasPermission: 'Not Checked',
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.text}>Permission Name:</Text>
|
||||
<Picker
|
||||
style={styles.picker}
|
||||
selectedValue={this.state.permission}
|
||||
onValueChange={this._onSelectPermission.bind(this)}>
|
||||
<Item label={PermissionsAndroid.PERMISSIONS.CAMERA} value={PermissionsAndroid.PERMISSIONS.CAMERA} />
|
||||
<Item label={PermissionsAndroid.PERMISSIONS.READ_CALENDAR} value={PermissionsAndroid.PERMISSIONS.READ_CALENDAR} />
|
||||
<Item label={PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION} value={PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION} />
|
||||
</Picker>
|
||||
<TouchableWithoutFeedback onPress={this._checkPermission}>
|
||||
<View>
|
||||
<Text style={[styles.touchable, styles.text]}>Check Permission</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
<Text style={styles.text}>Permission Status: {this.state.hasPermission}</Text>
|
||||
<TouchableWithoutFeedback onPress={this._requestPermission}>
|
||||
<View>
|
||||
<Text style={[styles.touchable, styles.text]}>Request Permission</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_onSelectPermission = (permission: string) => {
|
||||
this.setState({
|
||||
permission: permission,
|
||||
});
|
||||
};
|
||||
|
||||
_checkPermission = async () => {
|
||||
let result = await PermissionsAndroid.check(this.state.permission);
|
||||
this.setState({
|
||||
hasPermission: (result ? 'Granted' : 'Revoked') + ' for ' +
|
||||
this.state.permission,
|
||||
});
|
||||
};
|
||||
|
||||
_requestPermission = async () => {
|
||||
let result = await PermissionsAndroid.request(
|
||||
this.state.permission,
|
||||
{
|
||||
title: 'Permission Explanation',
|
||||
message:
|
||||
'The app needs the following permission ' + this.state.permission +
|
||||
' because of reasons. Please approve.'
|
||||
},
|
||||
);
|
||||
|
||||
this.setState({
|
||||
hasPermission: result + ' for ' +
|
||||
this.state.permission,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'Permissions Example',
|
||||
description: 'Short example of how to use the runtime permissions API introduced in Android M.',
|
||||
render: () => <PermissionsExample />,
|
||||
},
|
||||
];
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
singleLine: {
|
||||
fontSize: 16,
|
||||
padding: 4,
|
||||
},
|
||||
text: {
|
||||
margin: 10,
|
||||
},
|
||||
touchable: {
|
||||
color: '#007AFF',
|
||||
},
|
||||
picker: {
|
||||
flex: 1,
|
||||
}
|
||||
});
|
||||
128
packages/examples/src/RNTester/PickerExample.js
Normal file
128
packages/examples/src/RNTester/PickerExample.js
Normal file
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule PickerExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const RNTesterBlock = require('RNTesterBlock');
|
||||
const RNTesterPage = require('RNTesterPage');
|
||||
|
||||
const {
|
||||
Picker,
|
||||
Text,
|
||||
} = ReactNative;
|
||||
|
||||
const Item = Picker.Item;
|
||||
|
||||
class PickerExample extends React.Component<{}, $FlowFixMeState> {
|
||||
static title = '<Picker>';
|
||||
static description = 'Provides multiple options to choose from, using either a dropdown menu or a dialog.';
|
||||
|
||||
state = {
|
||||
selected1: 'key1',
|
||||
selected2: 'key1',
|
||||
selected3: 'key1',
|
||||
color: 'red',
|
||||
mode: Picker.MODE_DIALOG,
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<RNTesterPage title="<Picker>">
|
||||
<RNTesterBlock title="Basic Picker">
|
||||
<Picker
|
||||
style={styles.picker}
|
||||
selectedValue={this.state.selected1}
|
||||
onValueChange={this.onValueChange.bind(this, 'selected1')}>
|
||||
<Item label="hello" value="key0" />
|
||||
<Item label="world" value="key1" />
|
||||
</Picker>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title="Disabled picker">
|
||||
<Picker style={styles.picker} enabled={false} selectedValue={this.state.selected1}>
|
||||
<Item label="hello" value="key0" />
|
||||
<Item label="world" value="key1" />
|
||||
</Picker>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title="Dropdown Picker">
|
||||
<Picker
|
||||
style={styles.picker}
|
||||
selectedValue={this.state.selected2}
|
||||
onValueChange={this.onValueChange.bind(this, 'selected2')}
|
||||
mode="dropdown">
|
||||
<Item label="hello" value="key0" />
|
||||
<Item label="world" value="key1" />
|
||||
</Picker>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title="Picker with prompt message">
|
||||
<Picker
|
||||
style={styles.picker}
|
||||
selectedValue={this.state.selected3}
|
||||
onValueChange={this.onValueChange.bind(this, 'selected3')}
|
||||
prompt="Pick one, just one">
|
||||
<Item label="hello" value="key0" />
|
||||
<Item label="world" value="key1" />
|
||||
</Picker>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title="Picker with no listener">
|
||||
<Picker style={styles.picker}>
|
||||
<Item label="hello" value="key0" />
|
||||
<Item label="world" value="key1" />
|
||||
</Picker>
|
||||
<Text>
|
||||
Cannot change the value of this picker because it doesn't update selectedValue.
|
||||
</Text>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title="Colorful pickers">
|
||||
<Picker
|
||||
style={[styles.picker, {color: 'white', backgroundColor: '#333'}]}
|
||||
selectedValue={this.state.color}
|
||||
onValueChange={this.onValueChange.bind(this, 'color')}
|
||||
mode="dropdown">
|
||||
<Item label="red" color="red" value="red" />
|
||||
<Item label="green" color="green" value="green" />
|
||||
<Item label="blue" color="blue" value="blue" />
|
||||
</Picker>
|
||||
<Picker
|
||||
style={styles.picker}
|
||||
selectedValue={this.state.color}
|
||||
onValueChange={this.onValueChange.bind(this, 'color')}
|
||||
mode="dialog">
|
||||
<Item label="red" color="red" value="red" />
|
||||
<Item label="green" color="green" value="green" />
|
||||
<Item label="blue" color="blue" value="blue" />
|
||||
</Picker>
|
||||
</RNTesterBlock>
|
||||
</RNTesterPage>
|
||||
);
|
||||
}
|
||||
|
||||
changeMode = () => {
|
||||
const newMode = this.state.mode === Picker.MODE_DIALOG
|
||||
? Picker.MODE_DROPDOWN
|
||||
: Picker.MODE_DIALOG;
|
||||
this.setState({mode: newMode});
|
||||
};
|
||||
|
||||
onValueChange = (key: string, value: string) => {
|
||||
const newState = {};
|
||||
newState[key] = value;
|
||||
this.setState(newState);
|
||||
};
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
picker: {
|
||||
width: 100,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = PickerExample;
|
||||
144
packages/examples/src/RNTester/PickerIOSExample.js
Normal file
144
packages/examples/src/RNTester/PickerIOSExample.js
Normal file
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule PickerIOSExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
PickerIOS,
|
||||
Text,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
var PickerItemIOS = PickerIOS.Item;
|
||||
|
||||
var CAR_MAKES_AND_MODELS = {
|
||||
amc: {
|
||||
name: 'AMC',
|
||||
models: ['AMX', 'Concord', 'Eagle', 'Gremlin', 'Matador', 'Pacer'],
|
||||
},
|
||||
alfa: {
|
||||
name: 'Alfa-Romeo',
|
||||
models: ['159', '4C', 'Alfasud', 'Brera', 'GTV6', 'Giulia', 'MiTo', 'Spider'],
|
||||
},
|
||||
aston: {
|
||||
name: 'Aston Martin',
|
||||
models: ['DB5', 'DB9', 'DBS', 'Rapide', 'Vanquish', 'Vantage'],
|
||||
},
|
||||
audi: {
|
||||
name: 'Audi',
|
||||
models: ['90', '4000', '5000', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'Q5', 'Q7'],
|
||||
},
|
||||
austin: {
|
||||
name: 'Austin',
|
||||
models: ['America', 'Maestro', 'Maxi', 'Mini', 'Montego', 'Princess'],
|
||||
},
|
||||
borgward: {
|
||||
name: 'Borgward',
|
||||
models: ['Hansa', 'Isabella', 'P100'],
|
||||
},
|
||||
buick: {
|
||||
name: 'Buick',
|
||||
models: ['Electra', 'LaCrosse', 'LeSabre', 'Park Avenue', 'Regal',
|
||||
'Roadmaster', 'Skylark'],
|
||||
},
|
||||
cadillac: {
|
||||
name: 'Cadillac',
|
||||
models: ['Catera', 'Cimarron', 'Eldorado', 'Fleetwood', 'Sedan de Ville'],
|
||||
},
|
||||
chevrolet: {
|
||||
name: 'Chevrolet',
|
||||
models: ['Astro', 'Aveo', 'Bel Air', 'Captiva', 'Cavalier', 'Chevelle',
|
||||
'Corvair', 'Corvette', 'Cruze', 'Nova', 'SS', 'Vega', 'Volt'],
|
||||
},
|
||||
};
|
||||
|
||||
class PickerExample extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
carMake: 'cadillac',
|
||||
modelIndex: 3,
|
||||
};
|
||||
|
||||
render() {
|
||||
var make = CAR_MAKES_AND_MODELS[this.state.carMake];
|
||||
var selectionString = make.name + ' ' + make.models[this.state.modelIndex];
|
||||
return (
|
||||
<View>
|
||||
<Text>Please choose a make for your car:</Text>
|
||||
<PickerIOS
|
||||
selectedValue={this.state.carMake}
|
||||
onValueChange={(carMake) => this.setState({carMake, modelIndex: 0})}>
|
||||
{Object.keys(CAR_MAKES_AND_MODELS).map((carMake) => (
|
||||
<PickerItemIOS
|
||||
key={carMake}
|
||||
value={carMake}
|
||||
label={CAR_MAKES_AND_MODELS[carMake].name}
|
||||
/>
|
||||
))}
|
||||
</PickerIOS>
|
||||
<Text>Please choose a model of {make.name}:</Text>
|
||||
<PickerIOS
|
||||
selectedValue={this.state.modelIndex}
|
||||
key={this.state.carMake}
|
||||
onValueChange={(modelIndex) => this.setState({modelIndex})}>
|
||||
{CAR_MAKES_AND_MODELS[this.state.carMake].models.map((modelName, modelIndex) => (
|
||||
<PickerItemIOS
|
||||
key={this.state.carMake + '_' + modelIndex}
|
||||
value={modelIndex}
|
||||
label={modelName}
|
||||
/>
|
||||
))}
|
||||
</PickerIOS>
|
||||
<Text>You selected: {selectionString}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PickerStyleExample extends React.Component<{}, $FlowFixMeState> {
|
||||
state = {
|
||||
carMake: 'cadillac',
|
||||
modelIndex: 0,
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<PickerIOS
|
||||
itemStyle={{fontSize: 25, color: 'red', textAlign: 'left', fontWeight: 'bold'}}
|
||||
selectedValue={this.state.carMake}
|
||||
onValueChange={(carMake) => this.setState({carMake, modelIndex: 0})}>
|
||||
{Object.keys(CAR_MAKES_AND_MODELS).map((carMake) => (
|
||||
<PickerItemIOS
|
||||
key={carMake}
|
||||
value={carMake}
|
||||
label={CAR_MAKES_AND_MODELS[carMake].name}
|
||||
/>
|
||||
))}
|
||||
</PickerIOS>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
exports.displayName = (undefined: ?string);
|
||||
exports.title = '<PickerIOS>';
|
||||
exports.description = 'Render lists of selectable options with UIPickerView.';
|
||||
exports.examples = [
|
||||
{
|
||||
title: '<PickerIOS>',
|
||||
render: function(): React.Element<any> {
|
||||
return <PickerExample />;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '<PickerIOS> with custom styling',
|
||||
render: function(): React.Element<any> {
|
||||
return <PickerStyleExample />;
|
||||
},
|
||||
}];
|
||||
254
packages/examples/src/RNTester/PointerEventsExample.js
Normal file
254
packages/examples/src/RNTester/PointerEventsExample.js
Normal file
@@ -0,0 +1,254 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule PointerEventsExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
class ExampleBox extends React.Component<$FlowFixMeProps, $FlowFixMeState> {
|
||||
state = {
|
||||
log: [],
|
||||
};
|
||||
|
||||
handleLog = (msg) => {
|
||||
this.state.log = this.state.log.concat([msg]);
|
||||
};
|
||||
|
||||
flushReactChanges = () => {
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
/**
|
||||
* Capture phase of bubbling to append separator before any of the bubbling
|
||||
* happens.
|
||||
*/
|
||||
handleTouchCapture = () => {
|
||||
this.state.log = this.state.log.concat(['---']);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<View
|
||||
onTouchEndCapture={this.handleTouchCapture}
|
||||
onTouchStart={this.flushReactChanges}>
|
||||
{/* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This
|
||||
* comment suppresses an error when upgrading Flow's support for
|
||||
* React. To see the error delete this comment and run Flow. */}
|
||||
<this.props.Component onLog={this.handleLog} />
|
||||
</View>
|
||||
<View
|
||||
style={styles.logBox}>
|
||||
<DemoText style={styles.logText}>
|
||||
{this.state.log.join('\n')}
|
||||
</DemoText>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NoneExample extends React.Component<$FlowFixMeProps> {
|
||||
render() {
|
||||
return (
|
||||
<View
|
||||
onTouchStart={() => this.props.onLog('A unspecified touched')}
|
||||
style={styles.box}>
|
||||
<DemoText style={styles.text}>
|
||||
A: unspecified
|
||||
</DemoText>
|
||||
<View
|
||||
pointerEvents="none"
|
||||
onTouchStart={() => this.props.onLog('B none touched')}
|
||||
style={[styles.box, styles.boxPassedThrough]}>
|
||||
<DemoText style={[styles.text, styles.textPassedThrough]}>
|
||||
B: none
|
||||
</DemoText>
|
||||
<View
|
||||
onTouchStart={() => this.props.onLog('C unspecified touched')}
|
||||
style={[styles.box, styles.boxPassedThrough]}>
|
||||
<DemoText style={[styles.text, styles.textPassedThrough]}>
|
||||
C: unspecified
|
||||
</DemoText>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Special demo text that makes itself untouchable so that it doesn't destroy
|
||||
* the experiment and confuse the output.
|
||||
*/
|
||||
class DemoText extends React.Component<$FlowFixMeProps> {
|
||||
render() {
|
||||
return (
|
||||
<View pointerEvents="none">
|
||||
<Text
|
||||
style={this.props.style}>
|
||||
{this.props.children}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BoxNoneExample extends React.Component<$FlowFixMeProps> {
|
||||
render() {
|
||||
return (
|
||||
<View
|
||||
onTouchStart={() => this.props.onLog('A unspecified touched')}
|
||||
style={styles.box}>
|
||||
<DemoText style={styles.text}>
|
||||
A: unspecified
|
||||
</DemoText>
|
||||
<View
|
||||
pointerEvents="box-none"
|
||||
onTouchStart={() => this.props.onLog('B box-none touched')}
|
||||
style={[styles.box, styles.boxPassedThrough]}>
|
||||
<DemoText style={[styles.text, styles.textPassedThrough]}>
|
||||
B: box-none
|
||||
</DemoText>
|
||||
<View
|
||||
onTouchStart={() => this.props.onLog('C unspecified touched')}
|
||||
style={styles.box}>
|
||||
<DemoText style={styles.text}>
|
||||
C: unspecified
|
||||
</DemoText>
|
||||
</View>
|
||||
<View
|
||||
pointerEvents="auto"
|
||||
onTouchStart={() => this.props.onLog('C explicitly unspecified touched')}
|
||||
style={[styles.box]}>
|
||||
<DemoText style={[styles.text]}>
|
||||
C: explicitly unspecified
|
||||
</DemoText>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BoxOnlyExample extends React.Component<$FlowFixMeProps> {
|
||||
render() {
|
||||
return (
|
||||
<View
|
||||
onTouchStart={() => this.props.onLog('A unspecified touched')}
|
||||
style={styles.box}>
|
||||
<DemoText style={styles.text}>
|
||||
A: unspecified
|
||||
</DemoText>
|
||||
<View
|
||||
pointerEvents="box-only"
|
||||
onTouchStart={() => this.props.onLog('B box-only touched')}
|
||||
style={styles.box}>
|
||||
<DemoText style={styles.text}>
|
||||
B: box-only
|
||||
</DemoText>
|
||||
<View
|
||||
onTouchStart={() => this.props.onLog('C unspecified touched')}
|
||||
style={[styles.box, styles.boxPassedThrough]}>
|
||||
<DemoText style={[styles.text, styles.textPassedThrough]}>
|
||||
C: unspecified
|
||||
</DemoText>
|
||||
</View>
|
||||
<View
|
||||
pointerEvents="auto"
|
||||
onTouchStart={() => this.props.onLog('C explicitly unspecified touched')}
|
||||
style={[styles.box, styles.boxPassedThrough]}>
|
||||
<DemoText style={[styles.text, styles.textPassedThrough]}>
|
||||
C: explicitly unspecified
|
||||
</DemoText>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
type ExampleClass = {
|
||||
Component: React.ComponentType<any>,
|
||||
title: string,
|
||||
description: string,
|
||||
};
|
||||
|
||||
var exampleClasses: Array<ExampleClass> = [
|
||||
{
|
||||
Component: NoneExample,
|
||||
title: '`none`',
|
||||
description: '`none` causes touch events on the container and its child components to pass through to the parent container.',
|
||||
},
|
||||
{
|
||||
Component: BoxNoneExample,
|
||||
title: '`box-none`',
|
||||
description: '`box-none` causes touch events on the container to pass through and will only detect touch events on its child components.',
|
||||
},
|
||||
{
|
||||
Component: BoxOnlyExample,
|
||||
title: '`box-only`',
|
||||
description: '`box-only` causes touch events on the container\'s child components to pass through and will only detect touch events on the container itself.',
|
||||
}
|
||||
];
|
||||
|
||||
var infoToExample = (info) => {
|
||||
return {
|
||||
title: info.title,
|
||||
description: info.description,
|
||||
render: function() {
|
||||
return <ExampleBox key={info.title} Component={info.Component} />;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
text: {
|
||||
fontSize: 10,
|
||||
color: '#5577cc',
|
||||
},
|
||||
textPassedThrough: {
|
||||
color: '#88aadd',
|
||||
},
|
||||
box: {
|
||||
backgroundColor: '#aaccff',
|
||||
borderWidth: 1,
|
||||
borderColor: '#7799cc',
|
||||
padding: 10,
|
||||
margin: 5,
|
||||
},
|
||||
boxPassedThrough: {
|
||||
borderColor: '#99bbee',
|
||||
},
|
||||
logText: {
|
||||
fontSize: 9,
|
||||
},
|
||||
logBox: {
|
||||
padding: 20,
|
||||
margin: 10,
|
||||
borderWidth: 0.5,
|
||||
borderColor: '#f0f0f0',
|
||||
backgroundColor: '#f9f9f9',
|
||||
},
|
||||
bottomSpacer: {
|
||||
marginBottom: 100,
|
||||
},
|
||||
});
|
||||
|
||||
exports.framework = 'React';
|
||||
exports.title = 'Pointer Events';
|
||||
exports.description = 'Demonstrates the use of the pointerEvents prop of a ' +
|
||||
'View to control how touches should be handled.';
|
||||
exports.examples = exampleClasses.map(infoToExample);
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule ProgressBarAndroidExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var ProgressBar = require('ProgressBarAndroid');
|
||||
var React = require('React');
|
||||
var createReactClass = require('create-react-class');
|
||||
var RNTesterBlock = require('RNTesterBlock');
|
||||
var RNTesterPage = require('RNTesterPage');
|
||||
|
||||
var TimerMixin = require('react-timer-mixin');
|
||||
|
||||
var MovingBar = createReactClass({
|
||||
displayName: 'MovingBar',
|
||||
mixins: [TimerMixin],
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
progress: 0
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.setInterval(
|
||||
() => {
|
||||
var progress = (this.state.progress + 0.02) % 1;
|
||||
this.setState({progress: progress});
|
||||
}, 50
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <ProgressBar progress={this.state.progress} {...this.props} />;
|
||||
},
|
||||
});
|
||||
|
||||
class ProgressBarAndroidExample extends React.Component<{}> {
|
||||
static title = '<ProgressBarAndroid>';
|
||||
static description = 'Horizontal bar to show the progress of some operation.';
|
||||
|
||||
render() {
|
||||
return (
|
||||
<RNTesterPage title="ProgressBar Examples">
|
||||
<RNTesterBlock title="Horizontal Indeterminate ProgressBar">
|
||||
<ProgressBar styleAttr="Horizontal" />
|
||||
</RNTesterBlock>
|
||||
|
||||
<RNTesterBlock title="Horizontal ProgressBar">
|
||||
<MovingBar styleAttr="Horizontal" indeterminate={false} />
|
||||
</RNTesterBlock>
|
||||
|
||||
<RNTesterBlock title="Horizontal Black Indeterminate ProgressBar">
|
||||
<ProgressBar styleAttr="Horizontal" color="black" />
|
||||
</RNTesterBlock>
|
||||
|
||||
<RNTesterBlock title="Horizontal Blue ProgressBar">
|
||||
<MovingBar styleAttr="Horizontal" indeterminate={false} color="blue" />
|
||||
</RNTesterBlock>
|
||||
</RNTesterPage>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ProgressBarAndroidExample;
|
||||
84
packages/examples/src/RNTester/ProgressViewIOSExample.js
Normal file
84
packages/examples/src/RNTester/ProgressViewIOSExample.js
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule ProgressViewIOSExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var createReactClass = require('create-react-class');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
ProgressViewIOS,
|
||||
StyleSheet,
|
||||
View,
|
||||
} = ReactNative;
|
||||
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
|
||||
* found when Flow v0.54 was deployed. To see the error delete this comment and
|
||||
* run Flow. */
|
||||
var TimerMixin = require('react-timer-mixin');
|
||||
|
||||
var ProgressViewExample = createReactClass({
|
||||
displayName: 'ProgressViewExample',
|
||||
mixins: [TimerMixin],
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
progress: 0,
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
this.updateProgress();
|
||||
},
|
||||
|
||||
updateProgress() {
|
||||
var progress = this.state.progress + 0.01;
|
||||
this.setState({ progress });
|
||||
this.requestAnimationFrame(() => this.updateProgress());
|
||||
},
|
||||
|
||||
getProgress(offset) {
|
||||
var progress = this.state.progress + offset;
|
||||
return Math.sin(progress % Math.PI) % 1;
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ProgressViewIOS style={styles.progressView} progress={this.getProgress(0)}/>
|
||||
<ProgressViewIOS style={styles.progressView} progressTintColor="purple" progress={this.getProgress(0.2)}/>
|
||||
<ProgressViewIOS style={styles.progressView} progressTintColor="red" progress={this.getProgress(0.4)}/>
|
||||
<ProgressViewIOS style={styles.progressView} progressTintColor="orange" progress={this.getProgress(0.6)}/>
|
||||
<ProgressViewIOS style={styles.progressView} progressTintColor="yellow" progress={this.getProgress(0.8)}/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
exports.displayName = (undefined: ?string);
|
||||
exports.framework = 'React';
|
||||
exports.title = 'ProgressViewIOS';
|
||||
exports.description = 'ProgressViewIOS';
|
||||
exports.examples = [{
|
||||
title: 'ProgressViewIOS',
|
||||
render() {
|
||||
return (
|
||||
<ProgressViewExample/>
|
||||
);
|
||||
}
|
||||
}];
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
marginTop: -20,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
progressView: {
|
||||
marginTop: 20,
|
||||
}
|
||||
});
|
||||
215
packages/examples/src/RNTester/PushNotificationIOSExample.js
Normal file
215
packages/examples/src/RNTester/PushNotificationIOSExample.js
Normal file
@@ -0,0 +1,215 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule PushNotificationIOSExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
AlertIOS,
|
||||
PushNotificationIOS,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
class Button extends React.Component<$FlowFixMeProps> {
|
||||
render() {
|
||||
return (
|
||||
<TouchableHighlight
|
||||
underlayColor={'white'}
|
||||
style={styles.button}
|
||||
onPress={this.props.onPress}>
|
||||
<Text style={styles.buttonLabel}>
|
||||
{this.props.label}
|
||||
</Text>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NotificationExample extends React.Component<{}> {
|
||||
UNSAFE_componentWillMount() {
|
||||
PushNotificationIOS.addEventListener('register', this._onRegistered);
|
||||
PushNotificationIOS.addEventListener('registrationError', this._onRegistrationError);
|
||||
PushNotificationIOS.addEventListener('notification', this._onRemoteNotification);
|
||||
PushNotificationIOS.addEventListener('localNotification', this._onLocalNotification);
|
||||
|
||||
PushNotificationIOS.requestPermissions();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
PushNotificationIOS.removeEventListener('register', this._onRegistered);
|
||||
PushNotificationIOS.removeEventListener('registrationError', this._onRegistrationError);
|
||||
PushNotificationIOS.removeEventListener('notification', this._onRemoteNotification);
|
||||
PushNotificationIOS.removeEventListener('localNotification', this._onLocalNotification);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Button
|
||||
onPress={this._sendNotification}
|
||||
label="Send fake notification"
|
||||
/>
|
||||
|
||||
<Button
|
||||
onPress={this._sendLocalNotification}
|
||||
label="Send fake local notification"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_sendNotification() {
|
||||
require('RCTDeviceEventEmitter').emit('remoteNotificationReceived', {
|
||||
remote: true,
|
||||
aps: {
|
||||
alert: 'Sample notification',
|
||||
badge: '+1',
|
||||
sound: 'default',
|
||||
category: 'REACT_NATIVE',
|
||||
'content-available': 1,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
_sendLocalNotification() {
|
||||
require('RCTDeviceEventEmitter').emit('localNotificationReceived', {
|
||||
aps: {
|
||||
alert: 'Sample local notification',
|
||||
badge: '+1',
|
||||
sound: 'default',
|
||||
category: 'REACT_NATIVE'
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
_onRegistered(deviceToken) {
|
||||
AlertIOS.alert(
|
||||
'Registered For Remote Push',
|
||||
`Device Token: ${deviceToken}`,
|
||||
[{
|
||||
text: 'Dismiss',
|
||||
onPress: null,
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
_onRegistrationError(error) {
|
||||
AlertIOS.alert(
|
||||
'Failed To Register For Remote Push',
|
||||
`Error (${error.code}): ${error.message}`,
|
||||
[{
|
||||
text: 'Dismiss',
|
||||
onPress: null,
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
_onRemoteNotification(notification) {
|
||||
const result = `Message: ${notification.getMessage()};\n
|
||||
badge: ${notification.getBadgeCount()};\n
|
||||
sound: ${notification.getSound()};\n
|
||||
category: ${notification.getCategory()};\n
|
||||
content-available: ${notification.getContentAvailable()}.`;
|
||||
|
||||
AlertIOS.alert(
|
||||
'Push Notification Received',
|
||||
result,
|
||||
[{
|
||||
text: 'Dismiss',
|
||||
onPress: null,
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
_onLocalNotification(notification){
|
||||
AlertIOS.alert(
|
||||
'Local Notification Received',
|
||||
'Alert message: ' + notification.getMessage(),
|
||||
[{
|
||||
text: 'Dismiss',
|
||||
onPress: null,
|
||||
}]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NotificationPermissionExample extends React.Component<$FlowFixMeProps, any> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {permissions: null};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Button
|
||||
onPress={this._showPermissions.bind(this)}
|
||||
label="Show enabled permissions"
|
||||
/>
|
||||
<Text>
|
||||
{JSON.stringify(this.state.permissions)}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_showPermissions() {
|
||||
PushNotificationIOS.checkPermissions((permissions) => {
|
||||
this.setState({permissions});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
button: {
|
||||
padding: 10,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
buttonLabel: {
|
||||
color: 'blue',
|
||||
},
|
||||
});
|
||||
|
||||
exports.title = 'PushNotificationIOS';
|
||||
exports.description = 'Apple PushNotification and badge value';
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'Badge Number',
|
||||
render(): React.Element<any> {
|
||||
return (
|
||||
<View>
|
||||
<Button
|
||||
onPress={() => PushNotificationIOS.setApplicationIconBadgeNumber(42)}
|
||||
label="Set app's icon badge to 42"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => PushNotificationIOS.setApplicationIconBadgeNumber(0)}
|
||||
label="Clear app's icon badge"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Push Notifications',
|
||||
render(): React.Element<any> {
|
||||
return <NotificationExample />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Notifications Permissions',
|
||||
render(): React.Element<any> {
|
||||
return <NotificationPermissionExample />;
|
||||
}
|
||||
}];
|
||||
93
packages/examples/src/RNTester/RCTRootViewIOSExample.js
Normal file
93
packages/examples/src/RNTester/RCTRootViewIOSExample.js
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule RCTRootViewIOSExample
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const {
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
const requireNativeComponent = require('requireNativeComponent');
|
||||
|
||||
class AppPropertiesUpdateExample extends React.Component<{}> {
|
||||
render() {
|
||||
// Do not require this unless we are actually rendering.
|
||||
const UpdatePropertiesExampleView = requireNativeComponent('UpdatePropertiesExampleView');
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.text}>
|
||||
Press the button to update the field below by passing new properties to the RN app.
|
||||
</Text>
|
||||
<UpdatePropertiesExampleView style={styles.nativeView}>
|
||||
<Text style={styles.text}>
|
||||
Error: This demo is accessible only from RNTester app
|
||||
</Text>
|
||||
</UpdatePropertiesExampleView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RootViewSizeFlexibilityExample extends React.Component<{}> {
|
||||
render() {
|
||||
// Do not require this unless we are actually rendering.
|
||||
const FlexibleSizeExampleView = requireNativeComponent('FlexibleSizeExampleView');
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.text}>
|
||||
Press the button to resize it. On resize, RCTRootViewDelegate is notified. You can use it to handle content size updates.
|
||||
</Text>
|
||||
<FlexibleSizeExampleView style={styles.nativeView}>
|
||||
<Text style={styles.text}>
|
||||
Error: This demo is accessible only from RNTester app
|
||||
</Text>
|
||||
</FlexibleSizeExampleView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#F5FCFF',
|
||||
},
|
||||
text: {
|
||||
marginBottom: 20
|
||||
},
|
||||
nativeView: {
|
||||
height: 140,
|
||||
width: 280
|
||||
}
|
||||
});
|
||||
|
||||
exports.title = 'RCTRootView';
|
||||
exports.description = 'Examples that show useful methods when embedding React Native in a native application';
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'Updating app properties in runtime',
|
||||
render(): React.Element<any> {
|
||||
return (
|
||||
<AppPropertiesUpdateExample/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'RCTRootView\'s size flexibility',
|
||||
render(): React.Element<any> {
|
||||
return (
|
||||
<RootViewSizeFlexibilityExample/>
|
||||
);
|
||||
},
|
||||
}];
|
||||
57
packages/examples/src/RNTester/RNTesterActions.js
Normal file
57
packages/examples/src/RNTester/RNTesterActions.js
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule RNTesterActions
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
export type RNTesterBackAction = {
|
||||
type: 'RNTesterBackAction',
|
||||
};
|
||||
|
||||
export type RNTesterListAction = {
|
||||
type: 'RNTesterListAction',
|
||||
};
|
||||
|
||||
export type RNTesterExampleAction = {
|
||||
type: 'RNTesterExampleAction',
|
||||
openExample: string,
|
||||
};
|
||||
|
||||
export type RNTesterAction = (
|
||||
RNTesterBackAction |
|
||||
RNTesterListAction |
|
||||
RNTesterExampleAction
|
||||
);
|
||||
|
||||
|
||||
function Back(): RNTesterBackAction {
|
||||
return {
|
||||
type: 'RNTesterBackAction',
|
||||
};
|
||||
}
|
||||
|
||||
function ExampleList(): RNTesterListAction {
|
||||
return {
|
||||
type: 'RNTesterListAction',
|
||||
};
|
||||
}
|
||||
|
||||
function ExampleAction(openExample: string): RNTesterExampleAction {
|
||||
return {
|
||||
type: 'RNTesterExampleAction',
|
||||
openExample,
|
||||
};
|
||||
}
|
||||
|
||||
const RNTesterActions = {
|
||||
Back,
|
||||
ExampleList,
|
||||
ExampleAction,
|
||||
};
|
||||
|
||||
module.exports = RNTesterActions;
|
||||
220
packages/examples/src/RNTester/RNTesterApp.android.js
Normal file
220
packages/examples/src/RNTester/RNTesterApp.android.js
Normal file
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule RNTesterApp
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const AppRegistry = require('AppRegistry');
|
||||
const AsyncStorage = require('AsyncStorage');
|
||||
const BackHandler = require('BackHandler');
|
||||
const Dimensions = require('Dimensions');
|
||||
const DrawerLayoutAndroid = require('DrawerLayoutAndroid');
|
||||
const Linking = require('Linking');
|
||||
const React = require('react');
|
||||
const StatusBar = require('StatusBar');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const ToolbarAndroid = require('ToolbarAndroid');
|
||||
const RNTesterActions = require('./RNTesterActions');
|
||||
const RNTesterExampleContainer = require('./RNTesterExampleContainer');
|
||||
const RNTesterExampleList = require('./RNTesterExampleList');
|
||||
const RNTesterList = require('./RNTesterList');
|
||||
const RNTesterNavigationReducer = require('./RNTesterNavigationReducer');
|
||||
const UIManager = require('UIManager');
|
||||
const URIActionMap = require('./URIActionMap');
|
||||
const View = require('View');
|
||||
|
||||
const nativeImageSource = require('nativeImageSource');
|
||||
|
||||
import type { RNTesterNavigationState } from './RNTesterNavigationReducer';
|
||||
|
||||
UIManager.setLayoutAnimationEnabledExperimental(true);
|
||||
|
||||
const DRAWER_WIDTH_LEFT = 56;
|
||||
|
||||
type Props = {
|
||||
exampleFromAppetizeParams: string,
|
||||
};
|
||||
|
||||
const APP_STATE_KEY = 'RNTesterAppState.v2';
|
||||
|
||||
const HEADER_LOGO_ICON = nativeImageSource({
|
||||
android: 'launcher_icon',
|
||||
width: 132,
|
||||
height: 144
|
||||
});
|
||||
|
||||
const HEADER_NAV_ICON = nativeImageSource({
|
||||
android: 'ic_menu_black_24dp',
|
||||
width: 48,
|
||||
height: 48
|
||||
});
|
||||
|
||||
class RNTesterApp extends React.Component<Props, RNTesterNavigationState> {
|
||||
UNSAFE_componentWillMount() {
|
||||
BackHandler.addEventListener('hardwareBackPress', this._handleBackButtonPress);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
Linking.getInitialURL().then((url) => {
|
||||
AsyncStorage.getItem(APP_STATE_KEY, (err, storedString) => {
|
||||
const exampleAction = URIActionMap(this.props.exampleFromAppetizeParams);
|
||||
const urlAction = URIActionMap(url);
|
||||
const launchAction = exampleAction || urlAction;
|
||||
if (err || !storedString) {
|
||||
const initialAction = launchAction || {type: 'InitialAction'};
|
||||
this.setState(RNTesterNavigationReducer(null, initialAction));
|
||||
return;
|
||||
}
|
||||
const storedState = JSON.parse(storedString);
|
||||
if (launchAction) {
|
||||
this.setState(RNTesterNavigationReducer(storedState, launchAction));
|
||||
return;
|
||||
}
|
||||
this.setState(storedState);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.state) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<DrawerLayoutAndroid
|
||||
drawerPosition={DrawerLayoutAndroid.positions.Left}
|
||||
drawerWidth={Dimensions.get('window').width - DRAWER_WIDTH_LEFT}
|
||||
keyboardDismissMode="on-drag"
|
||||
onDrawerOpen={() => {
|
||||
this._overrideBackPressForDrawerLayout = true;
|
||||
}}
|
||||
onDrawerClose={() => {
|
||||
this._overrideBackPressForDrawerLayout = false;
|
||||
}}
|
||||
ref={(drawer) => { this.drawer = drawer; }}
|
||||
renderNavigationView={this._renderDrawerContent}
|
||||
statusBarBackgroundColor="#589c90">
|
||||
{this._renderApp()}
|
||||
</DrawerLayoutAndroid>
|
||||
);
|
||||
}
|
||||
|
||||
_renderDrawerContent = () => {
|
||||
return (
|
||||
<View style={styles.drawerContentWrapper}>
|
||||
<RNTesterExampleList
|
||||
list={RNTesterList}
|
||||
displayTitleRow={true}
|
||||
disableSearch={true}
|
||||
onNavigate={this._handleAction}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
_renderApp() {
|
||||
const {
|
||||
openExample,
|
||||
} = this.state;
|
||||
|
||||
if (openExample) {
|
||||
const ExampleModule = RNTesterList.Modules[openExample];
|
||||
if (ExampleModule.external) {
|
||||
return (
|
||||
<ExampleModule
|
||||
onExampleExit={() => {
|
||||
this._handleAction(RNTesterActions.Back());
|
||||
}}
|
||||
ref={(example) => { this._exampleRef = example; }}
|
||||
/>
|
||||
);
|
||||
} else if (ExampleModule) {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ToolbarAndroid
|
||||
logo={HEADER_LOGO_ICON}
|
||||
navIcon={HEADER_NAV_ICON}
|
||||
onIconClicked={() => this.drawer.openDrawer()}
|
||||
style={styles.toolbar}
|
||||
title={ExampleModule.title}
|
||||
/>
|
||||
<RNTesterExampleContainer
|
||||
module={ExampleModule}
|
||||
ref={(example) => { this._exampleRef = example; }}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ToolbarAndroid
|
||||
logo={HEADER_LOGO_ICON}
|
||||
navIcon={HEADER_NAV_ICON}
|
||||
onIconClicked={() => this.drawer.openDrawer()}
|
||||
style={styles.toolbar}
|
||||
title="RNTester"
|
||||
/>
|
||||
<RNTesterExampleList
|
||||
onNavigate={this._handleAction}
|
||||
list={RNTesterList}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_handleAction = (action: Object): boolean => {
|
||||
this.drawer && this.drawer.closeDrawer();
|
||||
const newState = RNTesterNavigationReducer(this.state, action);
|
||||
if (this.state !== newState) {
|
||||
this.setState(
|
||||
newState,
|
||||
() => AsyncStorage.setItem(APP_STATE_KEY, JSON.stringify(this.state))
|
||||
);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
_handleBackButtonPress = () => {
|
||||
if (this._overrideBackPressForDrawerLayout) {
|
||||
// This hack is necessary because drawer layout provides an imperative API
|
||||
// with open and close methods. This code would be cleaner if the drawer
|
||||
// layout provided an `isOpen` prop and allowed us to pass a `onDrawerClose` handler.
|
||||
this.drawer && this.drawer.closeDrawer();
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
this._exampleRef &&
|
||||
this._exampleRef.handleBackAction &&
|
||||
this._exampleRef.handleBackAction()
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return this._handleAction(RNTesterActions.Back());
|
||||
};
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
toolbar: {
|
||||
backgroundColor: '#E9EAED',
|
||||
height: 56,
|
||||
},
|
||||
drawerContentWrapper: {
|
||||
flex: 1,
|
||||
paddingTop: StatusBar.currentHeight,
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
});
|
||||
|
||||
AppRegistry.registerComponent('RNTesterApp', () => RNTesterApp);
|
||||
|
||||
module.exports = RNTesterApp;
|
||||
192
packages/examples/src/RNTester/RNTesterApp.ios.js
Normal file
192
packages/examples/src/RNTester/RNTesterApp.ios.js
Normal file
@@ -0,0 +1,192 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule RNTesterApp
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const AsyncStorage = require('AsyncStorage');
|
||||
const BackHandler = require('BackHandler');
|
||||
const Linking = require('Linking');
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const RNTesterActions = require('./RNTesterActions');
|
||||
const RNTesterExampleContainer = require('./RNTesterExampleContainer');
|
||||
const RNTesterExampleList = require('./RNTesterExampleList');
|
||||
const RNTesterList = require('./RNTesterList.ios');
|
||||
const RNTesterNavigationReducer = require('./RNTesterNavigationReducer');
|
||||
const URIActionMap = require('./URIActionMap');
|
||||
|
||||
const {
|
||||
Button,
|
||||
AppRegistry,
|
||||
SnapshotViewIOS,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
SafeAreaView
|
||||
} = ReactNative;
|
||||
|
||||
import type { RNTesterExample } from './RNTesterList.ios';
|
||||
import type { RNTesterAction } from './RNTesterActions';
|
||||
import type { RNTesterNavigationState } from './RNTesterNavigationReducer';
|
||||
|
||||
type Props = {
|
||||
exampleFromAppetizeParams: string,
|
||||
};
|
||||
|
||||
const APP_STATE_KEY = 'RNTesterAppState.v2';
|
||||
|
||||
const Header = ({ onBack, title }: { onBack?: () => mixed, title: string }) => (
|
||||
<SafeAreaView style={styles.headerContainer}>
|
||||
<View style={styles.header}>
|
||||
<View style={styles.headerCenter}>
|
||||
<Text style={styles.title}>{title}</Text>
|
||||
</View>
|
||||
{onBack && <View style={styles.headerLeft}>
|
||||
<Button title="Back" onPress={onBack} />
|
||||
</View>}
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
|
||||
class RNTesterApp extends React.Component<Props, RNTesterNavigationState> {
|
||||
UNSAFE_componentWillMount() {
|
||||
BackHandler.addEventListener('hardwareBackPress', this._handleBack);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
Linking.getInitialURL().then((url) => {
|
||||
AsyncStorage.getItem(APP_STATE_KEY, (err, storedString) => {
|
||||
const exampleAction = URIActionMap(this.props.exampleFromAppetizeParams);
|
||||
const urlAction = URIActionMap(url);
|
||||
const launchAction = exampleAction || urlAction;
|
||||
if (err || !storedString) {
|
||||
const initialAction = launchAction || {type: 'InitialAction'};
|
||||
this.setState(RNTesterNavigationReducer(undefined, initialAction));
|
||||
return;
|
||||
}
|
||||
const storedState = JSON.parse(storedString);
|
||||
if (launchAction) {
|
||||
this.setState(RNTesterNavigationReducer(storedState, launchAction));
|
||||
return;
|
||||
}
|
||||
this.setState(storedState);
|
||||
});
|
||||
});
|
||||
|
||||
Linking.addEventListener('url', (url) => {
|
||||
this._handleAction(URIActionMap(url));
|
||||
});
|
||||
}
|
||||
|
||||
_handleBack = () => {
|
||||
this._handleAction(RNTesterActions.Back());
|
||||
}
|
||||
|
||||
_handleAction = (action: ?RNTesterAction) => {
|
||||
if (!action) {
|
||||
return;
|
||||
}
|
||||
const newState = RNTesterNavigationReducer(this.state, action);
|
||||
if (this.state !== newState) {
|
||||
this.setState(
|
||||
newState,
|
||||
() => AsyncStorage.setItem(APP_STATE_KEY, JSON.stringify(this.state))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.state) {
|
||||
return null;
|
||||
}
|
||||
if (this.state.openExample) {
|
||||
const Component = RNTesterList.Modules[this.state.openExample];
|
||||
if (Component.external) {
|
||||
return (
|
||||
<Component
|
||||
onExampleExit={this._handleBack}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<View style={styles.exampleContainer}>
|
||||
<Header onBack={this._handleBack} title={Component.title} />
|
||||
<RNTesterExampleContainer module={Component} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
return (
|
||||
<View style={styles.exampleContainer}>
|
||||
<Header title="RNTester" />
|
||||
{/* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This
|
||||
* comment suppresses an error when upgrading Flow's support for
|
||||
* React. To see the error delete this comment and run Flow. */}
|
||||
<RNTesterExampleList
|
||||
onNavigate={this._handleAction}
|
||||
list={RNTesterList}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
headerContainer: {
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomColor: '#96969A',
|
||||
backgroundColor: '#F5F5F6',
|
||||
},
|
||||
header: {
|
||||
height: 40,
|
||||
flexDirection: 'row'
|
||||
},
|
||||
headerLeft: {
|
||||
},
|
||||
headerCenter: {
|
||||
flex: 1,
|
||||
position: 'absolute',
|
||||
top: 7,
|
||||
left: 0,
|
||||
right: 0,
|
||||
},
|
||||
title: {
|
||||
fontSize: 19,
|
||||
fontWeight: '600',
|
||||
textAlign: 'center',
|
||||
},
|
||||
exampleContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
AppRegistry.registerComponent('SetPropertiesExampleApp', () => require('./SetPropertiesExampleApp'));
|
||||
AppRegistry.registerComponent('RootViewSizeFlexibilityExampleApp', () => require('./RootViewSizeFlexibilityExampleApp'));
|
||||
AppRegistry.registerComponent('RNTesterApp', () => RNTesterApp);
|
||||
|
||||
// Register suitable examples for snapshot tests
|
||||
RNTesterList.ComponentExamples.concat(RNTesterList.APIExamples).forEach((Example: RNTesterExample) => {
|
||||
const ExampleModule = Example.module;
|
||||
if (ExampleModule.displayName) {
|
||||
class Snapshotter extends React.Component<{}> {
|
||||
render() {
|
||||
return (
|
||||
<SnapshotViewIOS>
|
||||
<RNTesterExampleContainer module={ExampleModule} />
|
||||
</SnapshotViewIOS>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AppRegistry.registerComponent(ExampleModule.displayName, () => Snapshotter);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = RNTesterApp;
|
||||
201
packages/examples/src/RNTester/RNTesterApp.web.js
Normal file
201
packages/examples/src/RNTester/RNTesterApp.web.js
Normal file
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule RNTesterApp
|
||||
* @flow
|
||||
*/
|
||||
import React from 'react';
|
||||
import RNTesterActions from './RNTesterActions';
|
||||
import RNTesterExampleContainer from './RNTesterExampleContainer';
|
||||
import RNTesterExampleList from './RNTesterExampleList';
|
||||
import RNTesterList from './RNTesterList';
|
||||
import RNTesterNavigationReducer from './RNTesterNavigationReducer';
|
||||
import URIActionMap from './URIActionMap';
|
||||
|
||||
import {
|
||||
AppRegistry,
|
||||
AsyncStorage,
|
||||
BackHandler,
|
||||
Button,
|
||||
Linking,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
SafeAreaView
|
||||
} from 'react-native';
|
||||
|
||||
import type { RNTesterExample } from './RNTesterList';
|
||||
import type { RNTesterAction } from './RNTesterActions';
|
||||
import type { RNTesterNavigationState } from './RNTesterNavigationReducer';
|
||||
|
||||
type Props = {
|
||||
exampleFromAppetizeParams: string,
|
||||
};
|
||||
|
||||
const APP_STATE_KEY = 'RNTesterAppState.v2';
|
||||
|
||||
const Header = ({ onBack, title }: { onBack?: () => mixed, title: string }) => (
|
||||
<SafeAreaView style={styles.headerContainer}>
|
||||
<View style={styles.header}>
|
||||
<View style={styles.headerCenter}>
|
||||
<Text accessibilityRole="heading" aria-level="3" style={styles.title}>{title}</Text>
|
||||
</View>
|
||||
{onBack ? (
|
||||
<React.Fragment>
|
||||
<View style={styles.headerLeft}>
|
||||
<Button title="Back" onPress={onBack} />
|
||||
</View>
|
||||
<View style={styles.headerRight}>
|
||||
</View>
|
||||
</React.Fragment>
|
||||
) : null}
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
|
||||
class RNTesterApp extends React.Component<Props, RNTesterNavigationState> {
|
||||
UNSAFE_componentWillMount() {
|
||||
BackHandler.addEventListener('hardwareBackPress', this._handleBack);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
Linking.getInitialURL().then((url) => {
|
||||
AsyncStorage.getItem(APP_STATE_KEY, (err, storedString) => {
|
||||
const exampleAction = URIActionMap(this.props.exampleFromAppetizeParams);
|
||||
const urlAction = URIActionMap(url);
|
||||
const launchAction = exampleAction || urlAction;
|
||||
if (err || !storedString) {
|
||||
const initialAction = launchAction || {type: 'InitialAction'};
|
||||
this.setState(RNTesterNavigationReducer(undefined, initialAction));
|
||||
return;
|
||||
}
|
||||
const storedState = JSON.parse(storedString);
|
||||
if (launchAction) {
|
||||
this.setState(RNTesterNavigationReducer(storedState, launchAction));
|
||||
return;
|
||||
}
|
||||
this.setState(storedState);
|
||||
});
|
||||
});
|
||||
|
||||
Linking.addEventListener('url', (url) => {
|
||||
this._handleAction(URIActionMap(url));
|
||||
});
|
||||
}
|
||||
|
||||
_handleBack = () => {
|
||||
this._handleAction(RNTesterActions.Back());
|
||||
}
|
||||
|
||||
_handleAction = (action: ?RNTesterAction) => {
|
||||
if (!action) {
|
||||
return;
|
||||
}
|
||||
const newState = RNTesterNavigationReducer(this.state, action);
|
||||
if (this.state !== newState) {
|
||||
this.setState(
|
||||
newState,
|
||||
() => AsyncStorage.setItem(APP_STATE_KEY, JSON.stringify(this.state))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.state) {
|
||||
return null;
|
||||
}
|
||||
if (this.state.openExample) {
|
||||
const Component = RNTesterList.Modules[this.state.openExample];
|
||||
if (Component.external) {
|
||||
return (
|
||||
<Component
|
||||
onExampleExit={this._handleBack}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<View style={styles.exampleContainer}>
|
||||
<Header onBack={this._handleBack} title={Component.title} />
|
||||
<RNTesterExampleContainer module={Component} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
return (
|
||||
<View style={styles.exampleContainer}>
|
||||
<Header title="React Native" />
|
||||
{/* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This
|
||||
* comment suppresses an error when upgrading Flow's support for
|
||||
* React. To see the error delete this comment and run Flow. */}
|
||||
<RNTesterExampleList
|
||||
onNavigate={this._handleAction}
|
||||
list={RNTesterList}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
headerContainer: {
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomColor: '#96969A',
|
||||
backgroundColor: '#F5F5F6',
|
||||
},
|
||||
header: {
|
||||
padding: 10,
|
||||
paddingVertical: 5,
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
minHeight: 50
|
||||
},
|
||||
headerCenter: {
|
||||
flex: 1,
|
||||
order: 2
|
||||
},
|
||||
headerLeft: {
|
||||
order: 1,
|
||||
width: 80
|
||||
},
|
||||
headerRight: {
|
||||
order: 3,
|
||||
width: 80
|
||||
},
|
||||
title: {
|
||||
fontSize: 19,
|
||||
fontWeight: '600',
|
||||
textAlign: 'center',
|
||||
},
|
||||
exampleContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
AppRegistry.registerComponent('SetPropertiesExampleApp', () => require('./SetPropertiesExampleApp'));
|
||||
AppRegistry.registerComponent('RootViewSizeFlexibilityExampleApp', () => require('./RootViewSizeFlexibilityExampleApp'));
|
||||
AppRegistry.registerComponent('RNTesterApp', () => RNTesterApp);
|
||||
|
||||
// Register suitable examples for snapshot tests
|
||||
RNTesterList.ComponentExamples.concat(RNTesterList.APIExamples).forEach((Example: RNTesterExample) => {
|
||||
const ExampleModule = Example.module;
|
||||
if (ExampleModule.displayName) {
|
||||
class Snapshotter extends React.Component<{}> {
|
||||
render() {
|
||||
return (
|
||||
<SnapshotViewIOS>
|
||||
<RNTesterExampleContainer module={ExampleModule} />
|
||||
</SnapshotViewIOS>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AppRegistry.registerComponent(ExampleModule.displayName, () => Snapshotter);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = RNTesterApp;
|
||||
|
||||
100
packages/examples/src/RNTester/RNTesterBlock.js
Normal file
100
packages/examples/src/RNTester/RNTesterBlock.js
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule RNTesterBlock
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var PropTypes = require('prop-types');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
class RNTesterBlock extends React.Component<{
|
||||
title?: string,
|
||||
description?: string,
|
||||
}, $FlowFixMeState> {
|
||||
static propTypes = {
|
||||
title: PropTypes.string,
|
||||
description: PropTypes.string,
|
||||
};
|
||||
|
||||
state = {description: (null: ?string)};
|
||||
|
||||
render() {
|
||||
var description;
|
||||
if (this.props.description) {
|
||||
description =
|
||||
<Text style={styles.descriptionText}>
|
||||
{this.props.description}
|
||||
</Text>;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.titleContainer}>
|
||||
<Text style={styles.titleText}>
|
||||
{this.props.title}
|
||||
</Text>
|
||||
{description}
|
||||
</View>
|
||||
<View style={styles.children}>
|
||||
{
|
||||
// $FlowFixMe found when converting React.createClass to ES6
|
||||
this.props.children}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
borderRadius: 3,
|
||||
borderWidth: 0.5,
|
||||
borderColor: '#d6d7da',
|
||||
backgroundColor: '#ffffff',
|
||||
margin: 10,
|
||||
marginVertical: 5,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
titleContainer: {
|
||||
borderBottomWidth: 0.5,
|
||||
borderTopLeftRadius: 3,
|
||||
borderTopRightRadius: 2.5,
|
||||
borderBottomColor: '#d6d7da',
|
||||
backgroundColor: '#f6f7f8',
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 5,
|
||||
},
|
||||
titleText: {
|
||||
fontSize: 14,
|
||||
fontWeight: '500',
|
||||
},
|
||||
descriptionText: {
|
||||
fontSize: 14,
|
||||
},
|
||||
disclosure: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
padding: 10,
|
||||
},
|
||||
disclosureIcon: {
|
||||
width: 12,
|
||||
height: 8,
|
||||
},
|
||||
children: {
|
||||
margin: 10,
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = RNTesterBlock;
|
||||
55
packages/examples/src/RNTester/RNTesterButton.js
Normal file
55
packages/examples/src/RNTester/RNTesterButton.js
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule RNTesterButton
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var PropTypes = require('prop-types');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
} = ReactNative;
|
||||
|
||||
class RNTesterButton extends React.Component<{onPress?: Function}> {
|
||||
static propTypes = {
|
||||
onPress: PropTypes.func,
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TouchableHighlight
|
||||
onPress={this.props.onPress}
|
||||
style={styles.button}
|
||||
underlayColor="grey">
|
||||
<Text>
|
||||
{
|
||||
// $FlowFixMe found when converting React.createClass to ES6
|
||||
this.props.children}
|
||||
</Text>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
button: {
|
||||
borderColor: '#696969',
|
||||
borderRadius: 8,
|
||||
borderWidth: 1,
|
||||
padding: 10,
|
||||
margin: 5,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#d3d3d3',
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = RNTesterButton;
|
||||
51
packages/examples/src/RNTester/RNTesterExampleContainer.js
Normal file
51
packages/examples/src/RNTester/RNTesterExampleContainer.js
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule RNTesterExampleContainer
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const {
|
||||
Platform,
|
||||
} = require('react-native');
|
||||
const RNTesterBlock = require('./RNTesterBlock');
|
||||
const RNTesterPage = require('./RNTesterPage');
|
||||
|
||||
class RNTesterExampleContainer extends React.Component {
|
||||
renderExample(example, i) {
|
||||
// Filter platform-specific examples
|
||||
var {title, description, platform} = example;
|
||||
if (platform) {
|
||||
if (Platform.OS !== platform) {
|
||||
return null;
|
||||
}
|
||||
title += ' (' + platform + ' only)';
|
||||
}
|
||||
return (
|
||||
<RNTesterBlock
|
||||
key={i}
|
||||
title={title}
|
||||
description={description}>
|
||||
{example.render()}
|
||||
</RNTesterBlock>
|
||||
);
|
||||
}
|
||||
|
||||
render(): React.Element<any> {
|
||||
if (!this.props.module.examples) {
|
||||
return <this.props.module />;
|
||||
}
|
||||
|
||||
return (
|
||||
<RNTesterPage title={this.props.title}>
|
||||
{this.props.module.examples.map(this.renderExample)}
|
||||
</RNTesterPage>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RNTesterExampleContainer;
|
||||
237
packages/examples/src/RNTester/RNTesterExampleList.js
Normal file
237
packages/examples/src/RNTester/RNTesterExampleList.js
Normal file
@@ -0,0 +1,237 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule RNTesterExampleList
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const Platform = require('Platform');
|
||||
const React = require('react');
|
||||
const SectionList = require('SectionList');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const Text = require('Text');
|
||||
const TextInput = require('TextInput');
|
||||
const TouchableHighlight = require('TouchableHighlight');
|
||||
const RNTesterActions = require('./RNTesterActions');
|
||||
const RNTesterStatePersister = require('./RNTesterStatePersister');
|
||||
const View = require('View');
|
||||
|
||||
import type {RNTesterExample} from './RNTesterList.ios';
|
||||
import type {PassProps} from './RNTesterStatePersister';
|
||||
import type {DangerouslyImpreciseStyleProp} from 'StyleSheet';
|
||||
|
||||
type Props = {
|
||||
onNavigate: Function,
|
||||
list: {
|
||||
ComponentExamples: Array<RNTesterExample>,
|
||||
APIExamples: Array<RNTesterExample>,
|
||||
},
|
||||
persister: PassProps<*>,
|
||||
searchTextInputStyle: DangerouslyImpreciseStyleProp,
|
||||
style?: ?DangerouslyImpreciseStyleProp,
|
||||
};
|
||||
|
||||
class RowComponent extends React.PureComponent<{
|
||||
item: Object,
|
||||
onNavigate: Function,
|
||||
onPress?: Function,
|
||||
onShowUnderlay?: Function,
|
||||
onHideUnderlay?: Function,
|
||||
}> {
|
||||
_onPress = () => {
|
||||
if (this.props.onPress) {
|
||||
this.props.onPress();
|
||||
return;
|
||||
}
|
||||
this.props.onNavigate(RNTesterActions.ExampleAction(this.props.item.key));
|
||||
};
|
||||
render() {
|
||||
const {item} = this.props;
|
||||
return (
|
||||
<TouchableHighlight {...this.props} onPress={this._onPress}>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.rowTitleText}>
|
||||
{item.module.title}
|
||||
</Text>
|
||||
<Text style={styles.rowDetailText}>
|
||||
{item.module.description}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const renderSectionHeader = ({section}) =>
|
||||
<Text style={styles.sectionHeader}>
|
||||
{section.title}
|
||||
</Text>;
|
||||
|
||||
class RNTesterExampleList extends React.Component<Props, $FlowFixMeState> {
|
||||
render() {
|
||||
const filterText = this.props.persister.state.filter;
|
||||
const filterRegex = new RegExp(String(filterText), 'i');
|
||||
const filter = (example) =>
|
||||
this.props.disableSearch ||
|
||||
filterRegex.test(example.module.title) &&
|
||||
(!Platform.isTVOS || example.supportsTVOS);
|
||||
|
||||
const sections = [
|
||||
{
|
||||
data: this.props.list.ComponentExamples.filter(filter),
|
||||
title: 'COMPONENTS',
|
||||
key: 'c',
|
||||
},
|
||||
{
|
||||
data: this.props.list.APIExamples.filter(filter),
|
||||
title: 'APIS',
|
||||
key: 'a',
|
||||
},
|
||||
];
|
||||
return (
|
||||
<View style={[styles.listContainer, this.props.style]}>
|
||||
{this._renderTitleRow()}
|
||||
{this._renderTextInput()}
|
||||
<SectionList
|
||||
ItemSeparatorComponent={ItemSeparator}
|
||||
contentContainerStyle={{backgroundColor: 'white'}}
|
||||
style={styles.list}
|
||||
sections={sections}
|
||||
renderItem={this._renderItem}
|
||||
enableEmptySections={true}
|
||||
itemShouldUpdate={this._itemShouldUpdate}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
automaticallyAdjustContentInsets={false}
|
||||
keyboardDismissMode="on-drag"
|
||||
legacyImplementation={false}
|
||||
renderSectionHeader={renderSectionHeader}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_itemShouldUpdate(curr, prev) {
|
||||
return curr.item !== prev.item;
|
||||
}
|
||||
|
||||
_renderItem = ({item, separators}) => (
|
||||
<RowComponent
|
||||
item={item}
|
||||
onNavigate={this.props.onNavigate}
|
||||
onShowUnderlay={separators.highlight}
|
||||
onHideUnderlay={separators.unhighlight}
|
||||
/>
|
||||
);
|
||||
|
||||
_renderTitleRow(): ?React.Element<any> {
|
||||
if (!this.props.displayTitleRow) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<RowComponent
|
||||
item={{module: {
|
||||
title: 'RNTester',
|
||||
description: 'React Native Examples',
|
||||
}}}
|
||||
onNavigate={this.props.onNavigate}
|
||||
onPress={() => {
|
||||
this.props.onNavigate(RNTesterActions.ExampleList());
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_renderTextInput(): ?React.Element<any> {
|
||||
if (this.props.disableSearch) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<View style={styles.searchRow}>
|
||||
<TextInput
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
clearButtonMode="always"
|
||||
onChangeText={text => {
|
||||
this.props.persister.setState(() => ({filter: text}));
|
||||
}}
|
||||
placeholder="Search..."
|
||||
underlineColorAndroid="transparent"
|
||||
style={[styles.searchTextInput, this.props.searchTextInputStyle]}
|
||||
testID="explorer_search"
|
||||
value={this.props.persister.state.filter}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_handleRowPress(exampleKey: string): void {
|
||||
this.props.onNavigate(RNTesterActions.ExampleAction(exampleKey));
|
||||
}
|
||||
}
|
||||
|
||||
const ItemSeparator = ({highlighted}) => (
|
||||
<View style={highlighted ? styles.separatorHighlighted : styles.separator} />
|
||||
);
|
||||
|
||||
RNTesterExampleList = RNTesterStatePersister.createContainer(RNTesterExampleList, {
|
||||
cacheKeySuffix: () => 'mainList',
|
||||
getInitialState: () => ({filter: ''}),
|
||||
});
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
listContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
list: {
|
||||
backgroundColor: '#eeeeee',
|
||||
},
|
||||
sectionHeader: {
|
||||
backgroundColor: '#eeeeee',
|
||||
padding: 5,
|
||||
fontWeight: '500',
|
||||
fontSize: 11,
|
||||
},
|
||||
row: {
|
||||
backgroundColor: 'white',
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 15,
|
||||
paddingVertical: 8,
|
||||
},
|
||||
separator: {
|
||||
height: StyleSheet.hairlineWidth,
|
||||
backgroundColor: '#bbbbbb',
|
||||
marginLeft: 15,
|
||||
},
|
||||
separatorHighlighted: {
|
||||
height: StyleSheet.hairlineWidth,
|
||||
backgroundColor: 'rgb(217, 217, 217)',
|
||||
},
|
||||
rowTitleText: {
|
||||
fontSize: 17,
|
||||
fontWeight: '500',
|
||||
},
|
||||
rowDetailText: {
|
||||
fontSize: 15,
|
||||
color: '#888888',
|
||||
lineHeight: 20,
|
||||
},
|
||||
searchRow: {
|
||||
backgroundColor: '#eeeeee',
|
||||
padding: 10,
|
||||
},
|
||||
searchTextInput: {
|
||||
backgroundColor: 'white',
|
||||
borderColor: '#cccccc',
|
||||
borderRadius: 3,
|
||||
borderWidth: 1,
|
||||
paddingLeft: 8,
|
||||
paddingVertical: 0,
|
||||
height: 35,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = RNTesterExampleList;
|
||||
263
packages/examples/src/RNTester/RNTesterList.android.js
Normal file
263
packages/examples/src/RNTester/RNTesterList.android.js
Normal file
@@ -0,0 +1,263 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule RNTesterList
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
export type RNTesterExample = {
|
||||
key: string,
|
||||
module: Object,
|
||||
};
|
||||
|
||||
const ComponentExamples: Array<RNTesterExample> = [
|
||||
{
|
||||
key: 'ActivityIndicatorExample',
|
||||
module: require('./ActivityIndicatorExample'),
|
||||
},
|
||||
{
|
||||
key: 'ButtonExample',
|
||||
module: require('./ButtonExample'),
|
||||
},
|
||||
{
|
||||
key: 'CheckBoxExample',
|
||||
module: require('./CheckBoxExample'),
|
||||
},
|
||||
{
|
||||
key: 'FlatListExample',
|
||||
module: require('./FlatListExample'),
|
||||
},
|
||||
{
|
||||
key: 'ImageExample',
|
||||
module: require('./ImageExample'),
|
||||
},
|
||||
{
|
||||
key: 'ListViewExample',
|
||||
module: require('./ListViewExample'),
|
||||
},
|
||||
{
|
||||
key: 'ListViewGridLayoutExample',
|
||||
module: require('./ListViewGridLayoutExample'),
|
||||
},
|
||||
{
|
||||
key: 'ListViewPagingExample',
|
||||
module: require('./ListViewPagingExample'),
|
||||
},
|
||||
{
|
||||
key: 'ModalExample',
|
||||
module: require('./ModalExample'),
|
||||
},
|
||||
{
|
||||
key: 'MultiColumnExample',
|
||||
module: require('./MultiColumnExample'),
|
||||
},
|
||||
{
|
||||
key: 'PickerExample',
|
||||
module: require('./PickerExample'),
|
||||
},
|
||||
{
|
||||
key: 'ProgressBarAndroidExample',
|
||||
module: require('./ProgressBarAndroidExample'),
|
||||
},
|
||||
{
|
||||
key: 'RefreshControlExample',
|
||||
module: require('./RefreshControlExample'),
|
||||
},
|
||||
{
|
||||
key: 'ScrollViewSimpleExample',
|
||||
module: require('./ScrollViewSimpleExample'),
|
||||
},
|
||||
{
|
||||
key: 'SectionListExample',
|
||||
module: require('./SectionListExample'),
|
||||
},
|
||||
{
|
||||
key: 'SliderExample',
|
||||
module: require('./SliderExample'),
|
||||
},
|
||||
{
|
||||
key: 'StatusBarExample',
|
||||
module: require('./StatusBarExample'),
|
||||
},
|
||||
{
|
||||
key: 'SwipeableFlatListExample',
|
||||
module: require('./SwipeableFlatListExample')
|
||||
},
|
||||
{
|
||||
key: 'SwipeableListViewExample',
|
||||
module: require('./SwipeableListViewExample')
|
||||
},
|
||||
{
|
||||
key: 'SwitchExample',
|
||||
module: require('./SwitchExample'),
|
||||
},
|
||||
{
|
||||
key: 'TextExample',
|
||||
module: require('./TextExample'),
|
||||
},
|
||||
{
|
||||
key: 'TextInputExample',
|
||||
module: require('./TextInputExample'),
|
||||
},
|
||||
{
|
||||
key: 'ToolbarAndroidExample',
|
||||
module: require('./ToolbarAndroidExample'),
|
||||
},
|
||||
{
|
||||
key: 'TouchableExample',
|
||||
module: require('./TouchableExample'),
|
||||
},
|
||||
{
|
||||
key: 'ViewExample',
|
||||
module: require('./ViewExample'),
|
||||
},
|
||||
{
|
||||
key: 'ViewPagerAndroidExample',
|
||||
module: require('./ViewPagerAndroidExample'),
|
||||
},
|
||||
{
|
||||
key: 'WebViewExample',
|
||||
module: require('./WebViewExample'),
|
||||
},
|
||||
];
|
||||
|
||||
const APIExamples: Array<RNTesterExample> = [
|
||||
{
|
||||
key: 'AccessibilityAndroidExample',
|
||||
module: require('./AccessibilityAndroidExample'),
|
||||
},
|
||||
{
|
||||
key: 'AlertExample',
|
||||
module: require('./AlertExample').AlertExample,
|
||||
},
|
||||
{
|
||||
key: 'AnimatedExample',
|
||||
module: require('./AnimatedExample'),
|
||||
},
|
||||
{
|
||||
key: 'AppStateExample',
|
||||
module: require('./AppStateExample'),
|
||||
},
|
||||
{
|
||||
key: 'BorderExample',
|
||||
module: require('./BorderExample'),
|
||||
},
|
||||
{
|
||||
key: 'CameraRollExample',
|
||||
module: require('./CameraRollExample'),
|
||||
},
|
||||
{
|
||||
key: 'ClipboardExample',
|
||||
module: require('./ClipboardExample'),
|
||||
},
|
||||
{
|
||||
key: 'DatePickerAndroidExample',
|
||||
module: require('./DatePickerAndroidExample'),
|
||||
},
|
||||
{
|
||||
key: 'Dimensions',
|
||||
module: require('./DimensionsExample'),
|
||||
},
|
||||
{
|
||||
key: 'GeolocationExample',
|
||||
module: require('./GeolocationExample'),
|
||||
},
|
||||
{
|
||||
key: 'ImageEditingExample',
|
||||
module: require('./ImageEditingExample'),
|
||||
},
|
||||
{
|
||||
key: 'LayoutEventsExample',
|
||||
module: require('./LayoutEventsExample'),
|
||||
},
|
||||
{
|
||||
key: 'LinkingExample',
|
||||
module: require('./LinkingExample'),
|
||||
},
|
||||
{
|
||||
key: 'LayoutAnimationExample',
|
||||
module: require('./LayoutAnimationExample'),
|
||||
},
|
||||
{
|
||||
key: 'LayoutExample',
|
||||
module: require('./LayoutExample'),
|
||||
},
|
||||
{
|
||||
key: 'NativeAnimationsExample',
|
||||
module: require('./NativeAnimationsExample'),
|
||||
},
|
||||
{
|
||||
key: 'NetInfoExample',
|
||||
module: require('./NetInfoExample'),
|
||||
},
|
||||
{
|
||||
key: 'OrientationChangeExample',
|
||||
module: require('./OrientationChangeExample'),
|
||||
},
|
||||
{
|
||||
key: 'PanResponderExample',
|
||||
module: require('./PanResponderExample'),
|
||||
},
|
||||
{
|
||||
key: 'PermissionsExampleAndroid',
|
||||
module: require('./PermissionsExampleAndroid'),
|
||||
},
|
||||
{
|
||||
key: 'PointerEventsExample',
|
||||
module: require('./PointerEventsExample'),
|
||||
},
|
||||
{
|
||||
key: 'RTLExample',
|
||||
module: require('./RTLExample'),
|
||||
},
|
||||
{
|
||||
key: 'ShareExample',
|
||||
module: require('./ShareExample'),
|
||||
},
|
||||
{
|
||||
key: 'TimePickerAndroidExample',
|
||||
module: require('./TimePickerAndroidExample'),
|
||||
},
|
||||
{
|
||||
key: 'TimerExample',
|
||||
module: require('./TimerExample'),
|
||||
},
|
||||
{
|
||||
key: 'ToastAndroidExample',
|
||||
module: require('./ToastAndroidExample'),
|
||||
},
|
||||
{
|
||||
key: 'TransformExample',
|
||||
module: require('./TransformExample'),
|
||||
},
|
||||
{
|
||||
key: 'VibrationExample',
|
||||
module: require('./VibrationExample'),
|
||||
},
|
||||
{
|
||||
key: 'WebSocketExample',
|
||||
module: require('./WebSocketExample'),
|
||||
},
|
||||
{
|
||||
key: 'XHRExample',
|
||||
module: require('./XHRExample'),
|
||||
},
|
||||
];
|
||||
|
||||
const Modules = {};
|
||||
|
||||
APIExamples.concat(ComponentExamples).forEach(Example => {
|
||||
Modules[Example.key] = Example.module;
|
||||
});
|
||||
|
||||
const RNTesterList = {
|
||||
APIExamples,
|
||||
ComponentExamples,
|
||||
Modules,
|
||||
};
|
||||
|
||||
module.exports = RNTesterList;
|
||||
401
packages/examples/src/RNTester/RNTesterList.ios.js
Normal file
401
packages/examples/src/RNTester/RNTesterList.ios.js
Normal file
@@ -0,0 +1,401 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule RNTesterList
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
export type RNTesterExample = {
|
||||
key: string,
|
||||
module: Object,
|
||||
supportsTVOS: boolean
|
||||
};
|
||||
|
||||
const ComponentExamples: Array<RNTesterExample> = [
|
||||
{
|
||||
key: 'ActivityIndicatorExample',
|
||||
module: require('./ActivityIndicatorExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'ARTExample',
|
||||
module: require('./ARTExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'ButtonExample',
|
||||
module: require('./ButtonExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'DatePickerIOSExample',
|
||||
module: require('./DatePickerIOSExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'FlatListExample',
|
||||
module: require('./FlatListExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'ImageExample',
|
||||
module: require('./ImageExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'InputAccessoryViewExample',
|
||||
module: require('./InputAccessoryViewExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'KeyboardAvoidingViewExample',
|
||||
module: require('./KeyboardAvoidingViewExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'LayoutEventsExample',
|
||||
module: require('./LayoutEventsExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'ListViewExample',
|
||||
module: require('./ListViewExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'ListViewGridLayoutExample',
|
||||
module: require('./ListViewGridLayoutExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'ListViewPagingExample',
|
||||
module: require('./ListViewPagingExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'MaskedViewExample',
|
||||
module: require('./MaskedViewExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'ModalExample',
|
||||
module: require('./ModalExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'MultiColumnExample',
|
||||
module: require('./MultiColumnExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'NavigatorIOSColorsExample',
|
||||
module: require('./NavigatorIOSColorsExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'NavigatorIOSBarStyleExample',
|
||||
module: require('./NavigatorIOSBarStyleExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'NavigatorIOSExample',
|
||||
module: require('./NavigatorIOSExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'PickerExample',
|
||||
module: require('./PickerExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'PickerIOSExample',
|
||||
module: require('./PickerIOSExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'ProgressViewIOSExample',
|
||||
module: require('./ProgressViewIOSExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'RefreshControlExample',
|
||||
module: require('./RefreshControlExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'SafeAreaViewExample',
|
||||
module: require('./SafeAreaViewExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'ScrollViewExample',
|
||||
module: require('./ScrollViewExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'SectionListExample',
|
||||
module: require('./SectionListExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'SegmentedControlIOSExample',
|
||||
module: require('./SegmentedControlIOSExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'SliderExample',
|
||||
module: require('./SliderExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'StatusBarExample',
|
||||
module: require('./StatusBarExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'SwipeableFlatListExample',
|
||||
module: require('./SwipeableFlatListExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'SwipeableListViewExample',
|
||||
module: require('./SwipeableListViewExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'SwitchExample',
|
||||
module: require('./SwitchExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'TabBarIOSExample',
|
||||
module: require('./TabBarIOSExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'TabBarIOSBarStyleExample',
|
||||
module: require('./TabBarIOSBarStyleExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'TextExample',
|
||||
module: require('./TextExample.ios'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'TextInputExample',
|
||||
module: require('./TextInputExample.ios'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'TouchableExample',
|
||||
module: require('./TouchableExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'TransparentHitTestExample',
|
||||
module: require('./TransparentHitTestExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'ViewExample',
|
||||
module: require('./ViewExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'WebViewExample',
|
||||
module: require('./WebViewExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
];
|
||||
|
||||
const APIExamples: Array<RNTesterExample> = [
|
||||
{
|
||||
key: 'AccessibilityIOSExample',
|
||||
module: require('./AccessibilityIOSExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'ActionSheetIOSExample',
|
||||
module: require('./ActionSheetIOSExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'AlertExample',
|
||||
module: require('./AlertExample').AlertExample,
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'AlertIOSExample',
|
||||
module: require('./AlertIOSExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'AnimatedExample',
|
||||
module: require('./AnimatedExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'AnExApp',
|
||||
module: require('./AnimatedGratuitousApp/AnExApp'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'AppStateExample',
|
||||
module: require('./AppStateExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'AsyncStorageExample',
|
||||
module: require('./AsyncStorageExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'BorderExample',
|
||||
module: require('./BorderExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'BoxShadowExample',
|
||||
module: require('./BoxShadowExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'CameraRollExample',
|
||||
module: require('./CameraRollExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'ClipboardExample',
|
||||
module: require('./ClipboardExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'Dimensions',
|
||||
module: require('./DimensionsExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'GeolocationExample',
|
||||
module: require('./GeolocationExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'ImageEditingExample',
|
||||
module: require('./ImageEditingExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'LayoutAnimationExample',
|
||||
module: require('./LayoutAnimationExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'LayoutExample',
|
||||
module: require('./LayoutExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'LinkingExample',
|
||||
module: require('./LinkingExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'NativeAnimationsExample',
|
||||
module: require('./NativeAnimationsExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'NetInfoExample',
|
||||
module: require('./NetInfoExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'OrientationChangeExample',
|
||||
module: require('./OrientationChangeExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'PanResponderExample',
|
||||
module: require('./PanResponderExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'PointerEventsExample',
|
||||
module: require('./PointerEventsExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'PushNotificationIOSExample',
|
||||
module: require('./PushNotificationIOSExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'RCTRootViewIOSExample',
|
||||
module: require('./RCTRootViewIOSExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'RTLExample',
|
||||
module: require('./RTLExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'ShareExample',
|
||||
module: require('./ShareExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'SnapshotExample',
|
||||
module: require('./SnapshotExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'TimerExample',
|
||||
module: require('./TimerExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'TransformExample',
|
||||
module: require('./TransformExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'TVEventHandlerExample',
|
||||
module: require('./TVEventHandlerExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'VibrationExample',
|
||||
module: require('./VibrationExample'),
|
||||
supportsTVOS: false,
|
||||
},
|
||||
{
|
||||
key: 'WebSocketExample',
|
||||
module: require('./WebSocketExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'XHRExample',
|
||||
module: require('./XHRExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
];
|
||||
|
||||
const Modules = {};
|
||||
|
||||
APIExamples.concat(ComponentExamples).forEach(Example => {
|
||||
Modules[Example.key] = Example.module;
|
||||
});
|
||||
|
||||
const RNTesterList = {
|
||||
APIExamples,
|
||||
ComponentExamples,
|
||||
Modules,
|
||||
};
|
||||
|
||||
module.exports = RNTesterList;
|
||||
262
packages/examples/src/RNTester/RNTesterList.web.js
Normal file
262
packages/examples/src/RNTester/RNTesterList.web.js
Normal file
@@ -0,0 +1,262 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule RNTesterList
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
export type RNTesterExample = {
|
||||
key: string,
|
||||
module: Object,
|
||||
supportsTVOS: boolean
|
||||
};
|
||||
|
||||
const ComponentExamples: Array<RNTesterExample> = [
|
||||
{
|
||||
key: 'ActivityIndicatorExample',
|
||||
module: require('./ActivityIndicatorExample')
|
||||
},
|
||||
{
|
||||
key: 'ARTExample',
|
||||
module: require('./ARTExample')
|
||||
},
|
||||
{
|
||||
key: 'ButtonExample',
|
||||
module: require('./ButtonExample')
|
||||
},
|
||||
{
|
||||
key: 'FlatListExample',
|
||||
module: require('./FlatListExample')
|
||||
},
|
||||
{
|
||||
key: 'MultiColumnExample',
|
||||
module: require('./MultiColumnExample')
|
||||
},
|
||||
{
|
||||
key: 'ImageExample',
|
||||
module: require('./ImageExample')
|
||||
},
|
||||
//{
|
||||
// key: 'InputAccessoryViewExample',
|
||||
// module: require('./InputAccessoryViewExample'),
|
||||
//},
|
||||
//{
|
||||
// key: 'KeyboardAvoidingViewExample',
|
||||
// module: require('./KeyboardAvoidingViewExample'),
|
||||
//},
|
||||
{
|
||||
key: 'ListViewExample',
|
||||
module: require('./ListViewExample')
|
||||
},
|
||||
{
|
||||
key: 'ListViewGridLayoutExample',
|
||||
module: require('./ListViewGridLayoutExample'),
|
||||
},
|
||||
{
|
||||
key: 'ListViewPagingExample',
|
||||
module: require('./ListViewPagingExample'),
|
||||
},
|
||||
//{
|
||||
// key: 'MaskedViewExample',
|
||||
// module: require('./MaskedViewExample'),
|
||||
//},
|
||||
//{
|
||||
// key: 'ModalExample',
|
||||
// module: require('./ModalExample'),
|
||||
//},
|
||||
|
||||
{
|
||||
key: 'PickerExample',
|
||||
module: require('./PickerExample')
|
||||
},
|
||||
//{
|
||||
// key: 'RefreshControlExample',
|
||||
// module: require('./RefreshControlExample'),
|
||||
//},
|
||||
{
|
||||
key: 'SafeAreaViewExample',
|
||||
module: require('./SafeAreaViewExample')
|
||||
},
|
||||
{
|
||||
key: 'ScrollViewExample',
|
||||
module: require('./ScrollViewExample')
|
||||
},
|
||||
{
|
||||
key: 'SectionListExample',
|
||||
module: require('./SectionListExample')
|
||||
},
|
||||
//{
|
||||
// key: 'SliderExample',
|
||||
// module: require('./SliderExample'),
|
||||
//},
|
||||
{
|
||||
key: 'StatusBarExample',
|
||||
module: require('./StatusBarExample')
|
||||
},
|
||||
{
|
||||
key: 'SwipeableFlatListExample',
|
||||
module: require('./SwipeableFlatListExample'),
|
||||
},
|
||||
{
|
||||
key: 'SwipeableListViewExample',
|
||||
module: require('./SwipeableListViewExample'),
|
||||
},
|
||||
{
|
||||
key: 'SwitchExample',
|
||||
module: require('./SwitchExample')
|
||||
},
|
||||
{
|
||||
key: 'TextExample',
|
||||
module: require('./TextExample')
|
||||
},
|
||||
{
|
||||
key: 'TextInputExample',
|
||||
module: require('./TextInputExample')
|
||||
},
|
||||
{
|
||||
key: 'TouchableExample',
|
||||
module: require('./TouchableExample')
|
||||
},
|
||||
//{
|
||||
// key: 'TransparentHitTestExample',
|
||||
// module: require('./TransparentHitTestExample')
|
||||
//},
|
||||
{
|
||||
key: 'ViewExample',
|
||||
module: require('./ViewExample')
|
||||
},
|
||||
//{
|
||||
// key: 'WebViewExample',
|
||||
// module: require('./WebViewExample'),
|
||||
//},
|
||||
{
|
||||
key: 'LayoutEventsExample',
|
||||
module: require('./LayoutEventsExample')
|
||||
},
|
||||
];
|
||||
|
||||
const APIExamples: Array<RNTesterExample> = [
|
||||
//{
|
||||
// key: 'AlertExample',
|
||||
// module: require('./AlertExample').AlertExample,
|
||||
//},
|
||||
{
|
||||
key: 'AnimatedExample',
|
||||
module: require('./AnimatedExample')
|
||||
},
|
||||
//{
|
||||
// key: 'AnExApp',
|
||||
// module: require('./AnimatedGratuitousApp/AnExApp'),
|
||||
//},
|
||||
{
|
||||
key: 'AppStateExample',
|
||||
module: require('./AppStateExample')
|
||||
},
|
||||
{
|
||||
key: 'AsyncStorageExample',
|
||||
module: require('./AsyncStorageExample')
|
||||
},
|
||||
{
|
||||
key: 'BorderExample',
|
||||
module: require('./BorderExample')
|
||||
},
|
||||
{
|
||||
key: 'BoxShadowExample',
|
||||
module: require('./BoxShadowExample')
|
||||
},
|
||||
//{
|
||||
// key: 'CameraRollExample',
|
||||
// module: require('./CameraRollExample'),
|
||||
//},
|
||||
{
|
||||
key: 'ClipboardExample',
|
||||
module: require('./ClipboardExample')
|
||||
},
|
||||
{
|
||||
key: 'Dimensions',
|
||||
module: require('./DimensionsExample')
|
||||
},
|
||||
{
|
||||
key: 'GeolocationExample',
|
||||
module: require('./GeolocationExample')
|
||||
},
|
||||
//{
|
||||
// key: 'ImageEditingExample',
|
||||
// module: require('./ImageEditingExample'),
|
||||
//},
|
||||
{
|
||||
key: 'LayoutAnimationExample',
|
||||
module: require('./LayoutAnimationExample')
|
||||
},
|
||||
{
|
||||
key: 'LayoutExample',
|
||||
module: require('./LayoutExample')
|
||||
},
|
||||
{
|
||||
key: 'LinkingExample',
|
||||
module: require('./LinkingExample')
|
||||
},
|
||||
{
|
||||
key: 'NativeAnimationsExample',
|
||||
module: require('./NativeAnimationsExample')
|
||||
},
|
||||
{
|
||||
key: 'NetInfoExample',
|
||||
module: require('./NetInfoExample')
|
||||
},
|
||||
//{
|
||||
// key: 'OrientationChangeExample',
|
||||
// module: require('./OrientationChangeExample'),
|
||||
//},
|
||||
{
|
||||
key: 'PanResponderExample',
|
||||
module: require('./PanResponderExample')
|
||||
},
|
||||
{
|
||||
key: 'PointerEventsExample',
|
||||
module: require('./PointerEventsExample')
|
||||
},
|
||||
{
|
||||
key: 'RTLExample',
|
||||
module: require('./RTLExample')
|
||||
},
|
||||
{
|
||||
key: 'ShareExample',
|
||||
module: require('./ShareExample')
|
||||
},
|
||||
{
|
||||
key: 'TimerExample',
|
||||
module: require('./TimerExample')
|
||||
},
|
||||
{
|
||||
key: 'TransformExample',
|
||||
module: require('./TransformExample')
|
||||
},
|
||||
{
|
||||
key: 'VibrationExample',
|
||||
module: require('./VibrationExample')
|
||||
},
|
||||
//{
|
||||
// key: 'XHRExample',
|
||||
// module: require('./XHRExample'),
|
||||
// supportsTVOS: true,
|
||||
//},
|
||||
];
|
||||
|
||||
const Modules = {};
|
||||
|
||||
APIExamples.concat(ComponentExamples).forEach(Example => {
|
||||
Modules[Example.key] = Example.module;
|
||||
});
|
||||
|
||||
const RNTesterList = {
|
||||
APIExamples,
|
||||
ComponentExamples,
|
||||
Modules
|
||||
};
|
||||
|
||||
module.exports = RNTesterList;
|
||||
55
packages/examples/src/RNTester/RNTesterNavigationReducer.js
Normal file
55
packages/examples/src/RNTester/RNTesterNavigationReducer.js
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule RNTesterNavigationReducer
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
// $FlowFixMe : This is a platform-forked component, and flow seems to only run on iOS?
|
||||
const RNTesterList = require('./RNTesterList');
|
||||
|
||||
export type RNTesterNavigationState = {
|
||||
openExample: ?string,
|
||||
};
|
||||
|
||||
function RNTesterNavigationReducer(
|
||||
state: ?RNTesterNavigationState,
|
||||
action: any
|
||||
): RNTesterNavigationState {
|
||||
|
||||
if (
|
||||
// Default value is to see example list
|
||||
!state ||
|
||||
|
||||
// Handle the explicit list action
|
||||
action.type === 'RNTesterListAction' ||
|
||||
|
||||
// Handle requests to go back to the list when an example is open
|
||||
(state.openExample && action.type === 'RNTesterBackAction')
|
||||
) {
|
||||
return {
|
||||
// A null openExample will cause the views to display the RNTester example list
|
||||
openExample: null,
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === 'RNTesterExampleAction') {
|
||||
|
||||
// Make sure we see the module before returning the new state
|
||||
const ExampleModule = RNTesterList.Modules[action.openExample];
|
||||
|
||||
if (ExampleModule) {
|
||||
return {
|
||||
openExample: action.openExample,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
module.exports = RNTesterNavigationReducer;
|
||||
78
packages/examples/src/RNTester/RNTesterPage.js
Normal file
78
packages/examples/src/RNTester/RNTesterPage.js
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule RNTesterPage
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var PropTypes = require('prop-types');
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
var RNTesterTitle = require('./RNTesterTitle');
|
||||
|
||||
class RNTesterPage extends React.Component<{
|
||||
noScroll?: boolean,
|
||||
noSpacer?: boolean,
|
||||
}> {
|
||||
static propTypes = {
|
||||
noScroll: PropTypes.bool,
|
||||
noSpacer: PropTypes.bool,
|
||||
};
|
||||
|
||||
render() {
|
||||
var ContentWrapper;
|
||||
var wrapperProps = {};
|
||||
if (this.props.noScroll) {
|
||||
ContentWrapper = ((View: any): React.ComponentType<any>);
|
||||
} else {
|
||||
ContentWrapper = (ScrollView: React.ComponentType<any>);
|
||||
// $FlowFixMe found when converting React.createClass to ES6
|
||||
wrapperProps.automaticallyAdjustContentInsets = !this.props.title;
|
||||
wrapperProps.keyboardShouldPersistTaps = 'handled';
|
||||
wrapperProps.keyboardDismissMode = 'interactive';
|
||||
}
|
||||
var title = this.props.title ?
|
||||
<RNTesterTitle title={this.props.title} /> :
|
||||
null;
|
||||
var spacer = this.props.noSpacer ? null : <View style={styles.spacer} />;
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{title}
|
||||
<ContentWrapper
|
||||
style={styles.wrapper}
|
||||
{...wrapperProps}>
|
||||
{
|
||||
// $FlowFixMe found when converting React.createClass to ES6
|
||||
this.props.children}
|
||||
{spacer}
|
||||
</ContentWrapper>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: '#e9eaed',
|
||||
flex: 1,
|
||||
},
|
||||
spacer: {
|
||||
height: 270,
|
||||
},
|
||||
wrapper: {
|
||||
flex: 1,
|
||||
paddingTop: 10,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = RNTesterPage;
|
||||
52
packages/examples/src/RNTester/RNTesterSettingSwitchRow.js
Normal file
52
packages/examples/src/RNTester/RNTesterSettingSwitchRow.js
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule RNTesterSettingSwitchRow
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const React = require('React');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const Switch = require('Switch');
|
||||
const Text = require('Text');
|
||||
const RNTesterStatePersister = require('./RNTesterStatePersister');
|
||||
const View = require('View');
|
||||
|
||||
class RNTesterSettingSwitchRow extends React.Component<$FlowFixMeProps, $FlowFixMeState> {
|
||||
UNSAFE_componentWillReceiveProps(newProps) {
|
||||
const {onEnable, onDisable, persister} = this.props;
|
||||
if (newProps.persister.state !== persister.state) {
|
||||
newProps.persister.state ? onEnable() : onDisable();
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const {label, persister} = this.props;
|
||||
return (
|
||||
<View style={styles.row}>
|
||||
<Text>{label}</Text>
|
||||
<Switch
|
||||
value={persister.state}
|
||||
onValueChange={(value) => {
|
||||
persister.setState(() => value);
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
const styles = StyleSheet.create({
|
||||
row: {
|
||||
padding: 10,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
});
|
||||
RNTesterSettingSwitchRow = RNTesterStatePersister.createContainer(RNTesterSettingSwitchRow, {
|
||||
cacheKeySuffix: ({label}) => 'Switch:' + label,
|
||||
getInitialState: ({initialValue}) => initialValue,
|
||||
});
|
||||
module.exports = RNTesterSettingSwitchRow;
|
||||
77
packages/examples/src/RNTester/RNTesterStatePersister.js
Normal file
77
packages/examples/src/RNTester/RNTesterStatePersister.js
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @providesModule RNTesterStatePersister
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const AsyncStorage = require('AsyncStorage');
|
||||
const React = require('React');
|
||||
|
||||
export type PassProps<State> = {
|
||||
state: State,
|
||||
setState: (stateLamda: (state: State) => State) => void,
|
||||
};
|
||||
|
||||
/**
|
||||
* A simple container for persisting some state and passing it into the wrapped component as
|
||||
* `props.persister.state`. Update it with `props.persister.setState`. The component is initially
|
||||
* rendered using `getInitialState` in the spec and is then re-rendered with the persisted data
|
||||
* once it's fetched.
|
||||
*
|
||||
* This is currently tied to RNTester because it's generally not good to use AsyncStorage like
|
||||
* this in real apps with user data, but we could maybe pull it out for other internal settings-type
|
||||
* usage.
|
||||
*/
|
||||
function createContainer<Props: Object, State>(
|
||||
Component: React.ComponentType<Props & {persister: PassProps<State>}>,
|
||||
spec: {
|
||||
cacheKeySuffix: (props: Props) => string,
|
||||
getInitialState: (props: Props) => State,
|
||||
version?: string,
|
||||
},
|
||||
): React.ComponentType<Props> {
|
||||
return class ComponentWithPersistedState extends React.Component<Props, $FlowFixMeState> {
|
||||
/* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This comment
|
||||
* suppresses an error when upgrading Flow's support for React. To see the
|
||||
* error delete this comment and run Flow. */
|
||||
static displayName = `RNTesterStatePersister(${Component.displayName || Component.name})`;
|
||||
state = {value: spec.getInitialState(this.props)};
|
||||
_cacheKey = `RNTester:${spec.version || 'v1'}:${spec.cacheKeySuffix(this.props)}`;
|
||||
componentDidMount() {
|
||||
AsyncStorage.getItem(this._cacheKey, (err, value) => {
|
||||
if (!err && value) {
|
||||
this.setState({value: JSON.parse(value)});
|
||||
}
|
||||
});
|
||||
}
|
||||
_passSetState = (stateLamda: (state: State) => State): void => {
|
||||
this.setState((state) => {
|
||||
const value = stateLamda(state.value);
|
||||
AsyncStorage.setItem(this._cacheKey, JSON.stringify(value));
|
||||
return {value};
|
||||
});
|
||||
};
|
||||
render(): React.Node {
|
||||
return (
|
||||
<Component
|
||||
{...this.props}
|
||||
persister={{
|
||||
state: this.state.value,
|
||||
setState: this._passSetState,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const RNTesterStatePersister = {
|
||||
createContainer,
|
||||
};
|
||||
|
||||
module.exports = RNTesterStatePersister;
|
||||
49
packages/examples/src/RNTester/RNTesterTitle.js
Normal file
49
packages/examples/src/RNTester/RNTesterTitle.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule RNTesterTitle
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
class RNTesterTitle extends React.Component<$FlowFixMeProps> {
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.text}>
|
||||
{this.props.title}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
borderRadius: 4,
|
||||
borderWidth: 0.5,
|
||||
borderColor: '#d6d7da',
|
||||
margin: 10,
|
||||
marginBottom: 0,
|
||||
height: 45,
|
||||
padding: 10,
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
text: {
|
||||
fontSize: 19,
|
||||
fontWeight: '500',
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = RNTesterTitle;
|
||||
682
packages/examples/src/RNTester/RTLExample.js
Normal file
682
packages/examples/src/RNTester/RTLExample.js
Normal file
@@ -0,0 +1,682 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
* @providesModule RTLExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const {
|
||||
Alert,
|
||||
Animated,
|
||||
I18nManager,
|
||||
Image,
|
||||
PanResponder,
|
||||
PixelRatio,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableWithoutFeedback,
|
||||
Switch,
|
||||
View,
|
||||
Button,
|
||||
} = ReactNative;
|
||||
const Platform = require('Platform');
|
||||
|
||||
const RNTesterPage = require('./RNTesterPage');
|
||||
const RNTesterBlock = require('./RNTesterBlock');
|
||||
|
||||
type State = {
|
||||
toggleStatus: any,
|
||||
pan: Object,
|
||||
linear: Object,
|
||||
isRTL: boolean,
|
||||
windowWidth: number,
|
||||
};
|
||||
|
||||
const SCALE = PixelRatio.get();
|
||||
const IMAGE_DIMENSION = 100 * SCALE;
|
||||
const IMAGE_SIZE = [IMAGE_DIMENSION, IMAGE_DIMENSION];
|
||||
|
||||
const IS_RTL = I18nManager.isRTL;
|
||||
|
||||
function ListItem(props) {
|
||||
return (
|
||||
<View style={styles.row}>
|
||||
<View style={styles.column1}>
|
||||
<Image source={props.imageSource} style={styles.icon} />
|
||||
</View>
|
||||
<View style={styles.column2}>
|
||||
<View style={styles.textBox}>
|
||||
<Text>Text Text Text</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.column3}>
|
||||
<View style={styles.smallButton}>
|
||||
<Text style={styles.fontSizeSmall}>Button</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function TextAlignmentExample(props) {
|
||||
return (
|
||||
<RNTesterBlock title={props.title} description={props.description}>
|
||||
<View>
|
||||
<Text style={props.style}>
|
||||
Left-to-Right language without text alignment.
|
||||
</Text>
|
||||
<Text style={props.style}>
|
||||
{'\u0645\u0646 \u0627\u0644\u064A\u0645\u064A\u0646 ' +
|
||||
'\u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631 ' +
|
||||
'\u0627\u0644\u0644\u063A\u0629 \u062F\u0648\u0646 ' +
|
||||
'\u0645\u062D\u0627\u0630\u0627\u0629 \u0627\u0644\u0646\u0635'}
|
||||
</Text>
|
||||
<Text style={props.style}>
|
||||
{'\u05DE\u05D9\u05DE\u05D9\u05DF \u05DC\u05E9\u05DE\u05D0\u05DC ' +
|
||||
'\u05D4\u05E9\u05E4\u05D4 \u05D1\u05DC\u05D9 ' +
|
||||
'\u05D9\u05D9\u05E9\u05D5\u05E8 \u05D8\u05E7\u05E1\u05D8'}
|
||||
</Text>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
);
|
||||
}
|
||||
|
||||
function AnimationBlock(props) {
|
||||
return (
|
||||
<View style={styles.block}>
|
||||
<TouchableWithoutFeedback onPress={props.onPress}>
|
||||
<Animated.Image
|
||||
style={[styles.img, props.imgStyle]}
|
||||
source={require('./Thumbnails/poke.png')}
|
||||
/>
|
||||
</TouchableWithoutFeedback>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
type RTLSwitcherComponentState = {|
|
||||
isRTL: boolean,
|
||||
|};
|
||||
|
||||
function withRTLState(Component) {
|
||||
return class extends React.Component<*, RTLSwitcherComponentState> {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.state = {
|
||||
isRTL: IS_RTL,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const setRTL = isRTL => this.setState({isRTL: isRTL});
|
||||
return <Component isRTL={this.state.isRTL} setRTL={setRTL} />;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const RTLToggler = ({isRTL, setRTL}) => {
|
||||
if (Platform.OS === 'android' || Platform.OS === 'web') {
|
||||
return <Text style={styles.rtlToggler}>{isRTL ? 'RTL' : 'LTR'}</Text>;
|
||||
}
|
||||
|
||||
const toggleRTL = () => setRTL(!isRTL);
|
||||
return (
|
||||
<Button
|
||||
onPress={toggleRTL}
|
||||
title={isRTL ? 'RTL' : 'LTR'}
|
||||
color="gray"
|
||||
accessibilityLabel="Change layout direction"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const PaddingExample = withRTLState(({isRTL, setRTL}) => {
|
||||
const color = 'teal';
|
||||
|
||||
return (
|
||||
<RNTesterBlock title={'Padding Start/End'}>
|
||||
<Text style={styles.bold}>Styles</Text>
|
||||
<Text>paddingStart: 50,</Text>
|
||||
<Text>paddingEnd: 10</Text>
|
||||
<Text />
|
||||
<Text style={styles.bold}>Demo: </Text>
|
||||
<Text>The {color} is padding.</Text>
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: color,
|
||||
paddingStart: 50,
|
||||
paddingEnd: 10,
|
||||
borderWidth: 1,
|
||||
borderColor: color,
|
||||
direction: isRTL ? 'rtl' : 'ltr',
|
||||
}}>
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: 'white',
|
||||
paddingTop: 5,
|
||||
paddingBottom: 5,
|
||||
borderLeftWidth: 1,
|
||||
borderRightWidth: 1,
|
||||
borderColor: 'gray',
|
||||
}}>
|
||||
<RTLToggler setRTL={setRTL} isRTL={isRTL} />
|
||||
</View>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
);
|
||||
});
|
||||
|
||||
const MarginExample = withRTLState(({isRTL, setRTL}) => {
|
||||
return (
|
||||
<RNTesterBlock title={'Margin Start/End'}>
|
||||
<Text style={styles.bold}>Styles</Text>
|
||||
<Text>marginStart: 50,</Text>
|
||||
<Text>marginEnd: 10</Text>
|
||||
<Text />
|
||||
<Text style={styles.bold}>Demo: </Text>
|
||||
<Text>The green is margin.</Text>
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: 'green',
|
||||
borderWidth: 1,
|
||||
borderColor: 'green',
|
||||
direction: isRTL ? 'rtl' : 'ltr',
|
||||
}}>
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: 'white',
|
||||
paddingTop: 5,
|
||||
paddingBottom: 5,
|
||||
marginStart: 50,
|
||||
marginEnd: 10,
|
||||
borderLeftWidth: 1,
|
||||
borderRightWidth: 1,
|
||||
borderColor: 'gray',
|
||||
}}>
|
||||
<RTLToggler setRTL={setRTL} isRTL={isRTL} />
|
||||
</View>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
);
|
||||
});
|
||||
|
||||
const PositionExample = withRTLState(({isRTL, setRTL}) => {
|
||||
return (
|
||||
<RNTesterBlock title={'Position Start/End'}>
|
||||
<Text style={styles.bold}>Styles</Text>
|
||||
<Text>start: 50</Text>
|
||||
<Text />
|
||||
<Text style={styles.bold}>Demo: </Text>
|
||||
<Text>The orange is position.</Text>
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: 'orange',
|
||||
borderWidth: 1,
|
||||
borderColor: 'orange',
|
||||
direction: isRTL ? 'rtl' : 'ltr',
|
||||
}}>
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: 'white',
|
||||
start: 50,
|
||||
borderColor: 'gray',
|
||||
}}>
|
||||
<RTLToggler setRTL={setRTL} isRTL={isRTL} />
|
||||
</View>
|
||||
</View>
|
||||
<Text />
|
||||
<Text style={styles.bold}>Styles</Text>
|
||||
<Text>end: 50</Text>
|
||||
<Text />
|
||||
<Text style={styles.bold}>Demo: </Text>
|
||||
<Text>The orange is position.</Text>
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: 'orange',
|
||||
borderWidth: 1,
|
||||
borderColor: 'orange',
|
||||
direction: isRTL ? 'rtl' : 'ltr',
|
||||
}}>
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: 'white',
|
||||
end: 50,
|
||||
borderColor: 'gray',
|
||||
}}>
|
||||
<RTLToggler setRTL={setRTL} isRTL={isRTL} />
|
||||
</View>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
);
|
||||
});
|
||||
|
||||
const BorderWidthExample = withRTLState(({isRTL, setRTL}) => {
|
||||
return (
|
||||
<RNTesterBlock title={'Border Width Start/End'}>
|
||||
<Text style={styles.bold}>Styles</Text>
|
||||
<Text>borderStartWidth: 10,</Text>
|
||||
<Text>borderEndWidth: 50</Text>
|
||||
<Text />
|
||||
<Text style={styles.bold}>Demo: </Text>
|
||||
<View style={{direction: isRTL ? 'rtl' : 'ltr'}}>
|
||||
<View
|
||||
style={{
|
||||
borderStartWidth: 10,
|
||||
borderEndWidth: 50,
|
||||
}}>
|
||||
<View>
|
||||
<RTLToggler setRTL={setRTL} isRTL={isRTL} />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
);
|
||||
});
|
||||
|
||||
const BorderColorExample = withRTLState(({isRTL, setRTL}) => {
|
||||
return (
|
||||
<RNTesterBlock title={'Border Color Start/End'}>
|
||||
<Text style={styles.bold}>Styles</Text>
|
||||
<Text>borderStartColor: 'red',</Text>
|
||||
<Text>borderEndColor: 'green',</Text>
|
||||
<Text />
|
||||
<Text style={styles.bold}>Demo: </Text>
|
||||
<View style={{direction: isRTL ? 'rtl' : 'ltr'}}>
|
||||
<View
|
||||
style={{
|
||||
borderStartColor: 'red',
|
||||
borderEndColor: 'green',
|
||||
borderLeftWidth: 20,
|
||||
borderRightWidth: 20,
|
||||
padding: 10,
|
||||
}}>
|
||||
<View>
|
||||
<RTLToggler setRTL={setRTL} isRTL={isRTL} />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
);
|
||||
});
|
||||
|
||||
const BorderRadiiExample = withRTLState(({isRTL, setRTL}) => {
|
||||
return (
|
||||
<RNTesterBlock title={'Border Radii Start/End'}>
|
||||
<Text style={styles.bold}>Styles</Text>
|
||||
<Text>borderTopStartRadius: 10,</Text>
|
||||
<Text>borderTopEndRadius: 20,</Text>
|
||||
<Text>borderBottomStartRadius: 30,</Text>
|
||||
<Text>borderBottomEndRadius: 40</Text>
|
||||
<Text />
|
||||
<Text style={styles.bold}>Demo: </Text>
|
||||
<View style={{direction: isRTL ? 'rtl' : 'ltr'}}>
|
||||
<View
|
||||
style={{
|
||||
borderWidth: 10,
|
||||
borderTopStartRadius: 10,
|
||||
borderTopEndRadius: 20,
|
||||
borderBottomStartRadius: 30,
|
||||
borderBottomEndRadius: 40,
|
||||
padding: 10,
|
||||
}}>
|
||||
<View>
|
||||
<RTLToggler setRTL={setRTL} isRTL={isRTL} />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
);
|
||||
});
|
||||
|
||||
const BorderExample = withRTLState(({isRTL, setRTL}) => {
|
||||
return (
|
||||
<RNTesterBlock title={'Border '}>
|
||||
<Text style={styles.bold}>Styles</Text>
|
||||
<Text>borderStartColor: 'red',</Text>
|
||||
<Text>borderEndColor: 'green',</Text>
|
||||
<Text>borderStartWidth: 10,</Text>
|
||||
<Text>borderEndWidth: 50,</Text>
|
||||
<Text>borderTopStartRadius: 10,</Text>
|
||||
<Text>borderTopEndRadius: 20,</Text>
|
||||
<Text>borderBottomStartRadius: 30,</Text>
|
||||
<Text>borderBottomEndRadius: 40</Text>
|
||||
<Text />
|
||||
<Text style={styles.bold}>Demo: </Text>
|
||||
<View style={{direction: isRTL ? 'rtl' : 'ltr'}}>
|
||||
<View
|
||||
style={{
|
||||
borderStartColor: 'red',
|
||||
borderEndColor: 'green',
|
||||
borderStartWidth: 10,
|
||||
borderEndWidth: 50,
|
||||
borderTopStartRadius: 10,
|
||||
borderTopEndRadius: 20,
|
||||
borderBottomStartRadius: 30,
|
||||
borderBottomEndRadius: 40,
|
||||
padding: 10,
|
||||
}}>
|
||||
<View>
|
||||
<RTLToggler setRTL={setRTL} isRTL={isRTL} />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
);
|
||||
});
|
||||
|
||||
class RTLExample extends React.Component<any, State> {
|
||||
static title = 'RTLExample';
|
||||
static description = 'Examples to show how to apply components to RTL layout.';
|
||||
|
||||
_panResponder: Object;
|
||||
|
||||
constructor(props: Object) {
|
||||
super(props);
|
||||
const pan = new Animated.ValueXY();
|
||||
|
||||
this._panResponder = PanResponder.create({
|
||||
onStartShouldSetPanResponder: () => true,
|
||||
onPanResponderGrant: this._onPanResponderGrant,
|
||||
onPanResponderMove: Animated.event([null, {dx: pan.x, dy: pan.y}]),
|
||||
onPanResponderRelease: this._onPanResponderEnd,
|
||||
onPanResponderTerminate: this._onPanResponderEnd,
|
||||
});
|
||||
|
||||
this.state = {
|
||||
toggleStatus: {},
|
||||
pan,
|
||||
linear: new Animated.Value(0),
|
||||
isRTL: IS_RTL,
|
||||
windowWidth: 0,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ScrollView
|
||||
style={[
|
||||
styles.container,
|
||||
// `direction` property is supported only on iOS now.
|
||||
Platform.OS === 'ios'
|
||||
? {direction: this.state.isRTL ? 'rtl' : 'ltr'}
|
||||
: null,
|
||||
]}
|
||||
onLayout={this._onLayout}>
|
||||
<RNTesterPage title={'Right-to-Left (RTL) UI Layout'}>
|
||||
<RNTesterBlock title={'Current Layout Direction'}>
|
||||
<View style={styles.directionBox}>
|
||||
<Text style={styles.directionText}>
|
||||
{this.state.isRTL ? 'Right-to-Left' : 'Left-to-Right'}
|
||||
</Text>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title={'Quickly Test RTL Layout'}>
|
||||
<View style={styles.flexDirectionRow}>
|
||||
<Text style={styles.switchRowTextView}>forceRTL</Text>
|
||||
<View style={styles.switchRowSwitchView}>
|
||||
<Switch
|
||||
onValueChange={this._onDirectionChange}
|
||||
style={styles.rightAlignStyle}
|
||||
value={this.state.isRTL}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title={'A Simple List Item Layout'}>
|
||||
<View style={styles.list}>
|
||||
<ListItem imageSource={require('./Thumbnails/like.png')} />
|
||||
<ListItem imageSource={require('./Thumbnails/poke.png')} />
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
<TextAlignmentExample
|
||||
title={'Default Text Alignment'}
|
||||
description={
|
||||
'In iOS, it depends on active language. ' +
|
||||
'In Android, it depends on the text content.'
|
||||
}
|
||||
style={styles.fontSizeSmall}
|
||||
/>
|
||||
<TextAlignmentExample
|
||||
title={"Using textAlign: 'left'"}
|
||||
description={
|
||||
'In iOS/Android, text alignment flips regardless of ' +
|
||||
'languages or text content.'
|
||||
}
|
||||
style={[styles.fontSizeSmall, styles.textAlignLeft]}
|
||||
/>
|
||||
<TextAlignmentExample
|
||||
title={"Using textAlign: 'right'"}
|
||||
description={
|
||||
'In iOS/Android, text alignment flips regardless of ' +
|
||||
'languages or text content.'
|
||||
}
|
||||
style={[styles.fontSizeSmall, styles.textAlignRight]}
|
||||
/>
|
||||
<RNTesterBlock title={'Working With Icons'}>
|
||||
<View style={styles.flexDirectionRow}>
|
||||
<View>
|
||||
<Image
|
||||
source={require('./Thumbnails/like.png')}
|
||||
style={styles.image}
|
||||
/>
|
||||
<Text style={styles.fontSizeExtraSmall}>
|
||||
Without directional meaning
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.rightAlignStyle}>
|
||||
<Image
|
||||
source={require('./Thumbnails/poke.png')}
|
||||
style={[styles.image, styles.withRTLStyle]}
|
||||
/>
|
||||
<Text style={styles.fontSizeExtraSmall}>
|
||||
With directional meaning
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock
|
||||
title={'Controlling Animation'}
|
||||
description={'Animation direction according to layout'}>
|
||||
<View Style={styles.view}>
|
||||
<AnimationBlock
|
||||
onPress={this._linearTap}
|
||||
imgStyle={{
|
||||
transform: [
|
||||
{translateX: this.state.linear},
|
||||
{scaleX: IS_RTL ? -1 : 1},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</RNTesterBlock>
|
||||
<PaddingExample />
|
||||
<MarginExample />
|
||||
<PositionExample />
|
||||
<BorderWidthExample />
|
||||
<BorderColorExample />
|
||||
<BorderRadiiExample />
|
||||
<BorderExample />
|
||||
</RNTesterPage>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
_onLayout = (e: Object) => {
|
||||
this.setState({
|
||||
windowWidth: e.nativeEvent.layout.width,
|
||||
});
|
||||
};
|
||||
|
||||
_onDirectionChange = () => {
|
||||
I18nManager.forceRTL(!this.state.isRTL);
|
||||
this.setState({isRTL: !this.state.isRTL});
|
||||
Alert.alert(
|
||||
'Reload this page',
|
||||
'Please reload this page to change the UI direction! ' +
|
||||
'All examples in this app will be affected. ' +
|
||||
'Check them out to see what they look like in RTL layout.',
|
||||
);
|
||||
};
|
||||
|
||||
_linearTap = (refName: string, e: Object) => {
|
||||
this.setState({
|
||||
toggleStatus: {
|
||||
...this.state.toggleStatus,
|
||||
[refName]: !this.state.toggleStatus[refName],
|
||||
},
|
||||
});
|
||||
const offset = IMAGE_SIZE[0] / SCALE / 2 + 10;
|
||||
const toMaxDistance =
|
||||
(IS_RTL ? -1 : 1) * (this.state.windowWidth / 2 - offset);
|
||||
Animated.timing(this.state.linear, {
|
||||
toValue: this.state.toggleStatus[refName] ? toMaxDistance : 0,
|
||||
duration: 2000,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
};
|
||||
|
||||
_onPanResponderGrant = (e: Object, gestureState: Object) => {
|
||||
this.state.pan.stopAnimation(value => {
|
||||
this.state.pan.setOffset(value);
|
||||
});
|
||||
};
|
||||
|
||||
_onPanResponderEnd = (e: Object, gestureState: Object) => {
|
||||
this.state.pan.flattenOffset();
|
||||
Animated.sequence([
|
||||
Animated.decay(this.state.pan, {
|
||||
velocity: {x: gestureState.vx, y: gestureState.vy},
|
||||
deceleration: 0.995,
|
||||
}),
|
||||
Animated.spring(this.state.pan, {toValue: {x: 0, y: 0}}),
|
||||
]).start();
|
||||
};
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: '#e9eaed',
|
||||
paddingTop: 15,
|
||||
},
|
||||
directionBox: {
|
||||
flex: 1,
|
||||
backgroundColor: '#f8f8f8',
|
||||
borderWidth: 0.5,
|
||||
borderColor: 'black',
|
||||
},
|
||||
directionText: {
|
||||
padding: 10,
|
||||
fontSize: 16,
|
||||
textAlign: 'center',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
switchRowTextView: {
|
||||
flex: 1,
|
||||
marginBottom: 5,
|
||||
marginTop: 5,
|
||||
textAlign: 'center',
|
||||
},
|
||||
switchRowSwitchView: {
|
||||
flex: 3,
|
||||
},
|
||||
rightAlignStyle: {
|
||||
right: 10,
|
||||
position: 'absolute',
|
||||
},
|
||||
list: {
|
||||
height: 120,
|
||||
marginBottom: 5,
|
||||
borderTopWidth: 0.5,
|
||||
borderLeftWidth: 0.5,
|
||||
borderRightWidth: 0.5,
|
||||
borderColor: '#e5e5e5',
|
||||
},
|
||||
row: {
|
||||
height: 60,
|
||||
flexDirection: 'row',
|
||||
borderBottomWidth: 0.5,
|
||||
borderColor: '#e5e5e5',
|
||||
},
|
||||
column1: {
|
||||
width: 60,
|
||||
},
|
||||
column2: {
|
||||
flex: 2.5,
|
||||
padding: 6,
|
||||
},
|
||||
column3: {
|
||||
flex: 1.5,
|
||||
},
|
||||
icon: {
|
||||
width: 48,
|
||||
height: 48,
|
||||
margin: 6,
|
||||
borderWidth: 0.5,
|
||||
borderColor: '#e5e5e5',
|
||||
},
|
||||
withRTLStyle: {
|
||||
transform: [{scaleX: IS_RTL ? -1 : 1}],
|
||||
},
|
||||
image: {
|
||||
left: 30,
|
||||
width: 48,
|
||||
height: 48,
|
||||
},
|
||||
img: {
|
||||
width: IMAGE_SIZE[0] / SCALE,
|
||||
height: IMAGE_SIZE[1] / SCALE,
|
||||
},
|
||||
view: {
|
||||
flex: 1,
|
||||
},
|
||||
block: {
|
||||
padding: 10,
|
||||
alignItems: 'center',
|
||||
},
|
||||
smallButton: {
|
||||
top: 18,
|
||||
borderRadius: 5,
|
||||
height: 24,
|
||||
width: 64,
|
||||
backgroundColor: '#e5e5e5',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
fontSizeSmall: {
|
||||
fontSize: 10,
|
||||
},
|
||||
fontSizeExtraSmall: {
|
||||
fontSize: 8,
|
||||
},
|
||||
textAlignLeft: {
|
||||
textAlign: 'left',
|
||||
},
|
||||
textAlignRight: {
|
||||
textAlign: 'right',
|
||||
},
|
||||
textBox: {
|
||||
width: 28,
|
||||
},
|
||||
flexDirectionRow: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
bold: {
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
rtlToggler: {
|
||||
color: 'gray',
|
||||
padding: 8,
|
||||
textAlign: 'center',
|
||||
fontWeight: '500',
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = RTLExample;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user