Compare commits

..

141 Commits
0.5.2 ... 0.8.9

Author SHA1 Message Date
Nicolas Gallagher
9c8407162e 0.8.9 2018-07-19 12:32:40 -07:00
Jeremy
16b9ec2917 [fix] nested ScrollView scroll behaviour propagation
Allow ScrollView to be scrolled from gestures originating in a
descendent ScrollView.

Fix #1042
Close #1043
2018-07-19 12:17:42 -07:00
Nicolas Gallagher
d4af1eb981 0.8.8 2018-07-06 15:06:46 -07:00
Nicolas Gallagher
405a3b79e8 [fix] TextInput calls onSelectionChange upon user input
Fix #1018
2018-07-06 15:00:50 -07:00
Nicolas Gallagher
afd5293172 Update React packages 2018-07-06 13:46:21 -07:00
Nicolas Gallagher
e4831b7bd8 Update benchmarks dependencies 2018-07-06 13:45:56 -07:00
Nicolas Gallagher
baffc9a9e6 0.8.7 2018-07-05 11:50:17 -07:00
Nicolas Gallagher
4151b47005 [fix] backgroundClip prefixing when value is 'text'
Temporary work-around for a bug in inline-style-prefixer.

Fix #1014
2018-07-05 11:46:49 -07:00
Nicolas Gallagher
96ec805f59 [fix] TextInput multiline value with onSubmitEditing
Prevent a newline from being added when submitting a multiline text
input using the "Enter" key.

Fix #1013
2018-07-04 12:32:48 -07:00
Nicolas Gallagher
d2df2c296e 0.8.6 2018-06-28 09:47:56 -07:00
Nicolas Gallagher
af80b046fa Update Flow config documentation
Close #1007
2018-06-28 09:33:07 -07:00
Nicolas Gallagher
1d01af57d0 [fix] add mousedown and mouseup to supported View props
Fix #1008
2018-06-28 09:08:28 -07:00
Johannes
e7613ca4d1 [fix] onLayout TypeError if node already unmounted
Close #993
2018-06-28 09:02:20 -07:00
Nicolas Gallagher
0e81c6ef27 0.8.5 2018-06-26 07:55:35 -07:00
Nicolas Gallagher
7fa8940325 [fix] VirtualizedList calls to findNodeHandle
Fix #1004
2018-06-26 07:52:07 -07:00
Nicolas Gallagher
3e681bed3e GitHub issue template adjustment
Move help text into HTML comments and make it clear that issues will be
closed if the issue template is ignored.
2018-06-15 10:47:04 -07:00
ral51
ecd4b40c71 Fix source code links on website
Close #994
2018-06-14 13:45:38 -07:00
Nicolas Gallagher
e7cb364b63 0.8.4 2018-06-14 13:40:57 -07:00
Nicolas Gallagher
90bd23f783 [fix] onLayout event object
Make sure that `this` within the `target` getter references the React
component rather than the layout event object.

Fix #996
2018-06-14 13:21:25 -07:00
Nicolas Gallagher
47a281373a 0.8.3 2018-06-06 16:24:02 -07:00
Nicolas Gallagher
6a0302169c [fix] react-native-web package files
Fix #987
2018-06-06 16:21:33 -07:00
Nicolas Gallagher
b195f2b1f5 0.8.2 2018-06-05 13:22:24 -07:00
Nicolas Gallagher
7b81d2a7ec Fix AsyncStorageExample
Comment out React Native's use of non-standard `done()` method, which
isn't available on `async` functions even though it is polyfilled for
the global Promise.
2018-06-05 13:13:36 -07:00
Nicolas Gallagher
a1d8ea776e [fix] PickerIOS is alias for Picker
PickerIOS API is a subset of the cross-platform Picker. This patch
provides some compatibility for use of PickerIOS / PickerIOS.Item in
legacy code.
2018-06-05 13:12:59 -07:00
Nicolas Gallagher
15b960f097 0.8.1 2018-06-05 09:31:42 -07:00
Nicolas Gallagher
001be82178 [fix] remove AppRegistry logging in production
Fix #986
2018-06-05 09:08:36 -07:00
Nicolas Gallagher
5eeef9e3d2 [fix] only call preventDefault for clicks on links
The previous incarnation of this fix would cancel clicks that bubble up
to elements like ScrollViews, with undesired impact on child element
events. Instead, limit the hack to elements with accessibilityRole=link.

Fix #985
2018-06-05 09:02:35 -07:00
Nicolas Gallagher
6a310999d0 0.8.0 2018-06-04 11:55:16 -07:00
Nicolas Gallagher
a9ad313a79 Add examples app to release tasks 2018-06-04 11:54:11 -07:00
Nicolas Gallagher
d0ca7585ab Update docs
Close #960
2018-06-04 11:40:08 -07:00
Nicolas Gallagher
3f8624e25f [change] StyleSheet.hairlineWidth is whole pixel value
Even on high DPI screens, browsers can round sub-pixel values down to
`0`. This causes hairlineWidth borders/heights/etc not to be rendered.
Revert `hairlineWidth` value back to `1`.
2018-06-04 11:40:08 -07:00
Nicolas Gallagher
8f0c39c2fe [change] introduce Jest preset
A simple Jest preset that configures module mapping and produces
human-readable styles (i.e., not converted to numeric form).

Fix #928
Fix #963
2018-06-04 11:40:07 -07:00
Nicolas Gallagher
4f9216853b Add more paths to jest ignore 2018-06-04 11:40:07 -07:00
Nicolas Gallagher
14f7dfd515 [fix] Clipboard preserves line breaks
Fix #979
2018-06-04 11:40:07 -07:00
Nicolas Gallagher
c336995952 [change] prevent default click behavior when using responder system
Certain HTML elements have a default behaviour (e.g., link navigation)
that can only be prevented in the `click` event handler. The responder
event system doesn't make use of `click` and no callbacks have access to
the `click` event. To prevent unwanted default behaviour, and emulate
the behavior in React Native, the `click` callback will automatically
call `preventDefault()` when the responder system is being used.

The result is that components like `Touchable*` that are overloaded as
web links need to explicitly trigger the link navigation, e.g.,

```
<TouchableOpacity
  accessibilityTraits="link"
  href={href}
  onPress={(e) => {
    Linking.openUrl(href);
  }}
/>
```

Fix #970
2018-06-04 11:40:07 -07:00
Nicolas Gallagher
2afa5a3cf7 [change] remove componentWillReceiveProps from Image 2018-06-04 11:40:07 -07:00
Nicolas Gallagher
9fb818cfd4 [fix] Image resizeMode for 'center' and 'repeat'
React Native will scale down an image to fit its container before
centering or repeating it.
2018-06-04 11:40:07 -07:00
Nicolas Gallagher
3153cd8213 [add] Image support for blurRadius, tintColor, and shadows
Use CSS filters to implement React Native image styles.

Ref #362
Ref #548
2018-06-04 11:40:07 -07:00
Nicolas Gallagher
026a92fd53 [fix] babel-plugin option to rewrite to commonjs paths
Allow the babel plugin to be configured to rewrite paths to either ES
modules (default) or CommonJS.

Ref #961
2018-06-04 11:40:07 -07:00
Nicolas Gallagher
e0f010da47 [change] whitelist props passed to React DOM
Avoiding unknown property warnings from React DOM. Many components
within and build upon React Native do not filter their own properties
and spread them into View. It is expected that React Native ignore these
properties.

Fix #898
2018-06-04 11:40:06 -07:00
Nicolas Gallagher
b299eb6c59 [add] SwipeableFlatList and SwipeableListView 2018-06-04 11:39:55 -07:00
Nicolas Gallagher
da38e87b50 [fix] minor inconsistency in textShadow style resolution
React Native doesn't apply textShadow styles when the 'height' or
'width' offset is 0.
2018-06-04 11:39:12 -07:00
Nicolas Gallagher
a40521f485 [fix] shadow style props resolution 2018-06-04 10:24:35 -07:00
Nicolas Gallagher
f62ed22a14 [add] Text support for textDecoration{Color,Style} 2018-06-04 10:24:35 -07:00
Nicolas Gallagher
bfaca0557b [fix] add UIManager to NativeModules 2018-06-04 10:24:35 -07:00
Nicolas Gallagher
c3eedabac4 Add React Native examples app
RNTester examples taken from React Native 0.55.4.

Minor changes to disable / enable some examples for the web.
2018-06-04 10:24:31 -07:00
Nicolas Gallagher
cf43ffd700 [change] Export stubs for iOS/Android modules
Fix #958
Fix #967
2018-06-04 10:21:45 -07:00
Nicolas Gallagher
a8e5d43db5 Move 'website' to 'packages/website'
Keep all workspaces in the 'packages' directory.
2018-06-03 11:32:55 -07:00
Nicolas Gallagher
b4e3427fea [fix] UIManager measurements don't block
A large number of layout measurements (and their corresponding tasks)
can block the main thread. Make the work async and try to keep the UI
responsive to user input while the layout work is taking place.
2018-06-03 11:32:54 -07:00
Nicolas Gallagher
a82cfbe504 [fix] Image layout in Firefox
Layout is more reliable in Firefox if the root element's flexBasis is
'auto'. This patch also moves the background image to an internal tile.
2018-06-03 11:32:54 -07:00
Nicolas Gallagher
0eae7bed2e [change] Image should not be draggable by default 2018-06-03 11:32:54 -07:00
Nicolas Gallagher
b8f54f61f0 [change] Image uses decode() not requestIdleCallback()
Chrome heavily throttles `requestIdleCallback` while scrolling, which
causes delays in image loading. Instead, use the relatively new
`decode()` API to decode the image off the main thread and prevent jank,
without delaying loading itself.

Fix #764
Ref #759
2018-06-03 11:32:54 -07:00
Nicolas Gallagher
d5c6b98340 [fix] Image loading callbacks when mounting cached image 2018-06-03 11:32:54 -07:00
Nicolas Gallagher
16b2fb9bd7 [fix] Image loading for source={{ uri: '' }}
Avoid an error being thrown from attempting to call `match` on an object
value.

Fix #962
2018-06-03 11:32:54 -07:00
Nicolas Gallagher
48e62fcd64 Add PJPEG example to docs 2018-06-03 11:32:54 -07:00
Nicolas Gallagher
0816c40790 [fix] add 'target' to onLayout nativeEvent 2018-06-03 11:32:54 -07:00
Nicolas Gallagher
2756ab49c3 [fix] TextInput add 'numbers-and-punctuation' keyboardType 2018-06-03 11:32:54 -07:00
Nicolas Gallagher
10407f3aa2 [add] Share API
Fix #958
2018-06-03 11:32:54 -07:00
Nicolas Gallagher
0ee3310290 [change] Linking API
Linking updates the application document's URL rather than opening a new
window. This change also makes deep-linking work.
2018-06-03 11:32:53 -07:00
Nicolas Gallagher
19b356aaea [add] Alert API 2018-06-03 11:32:53 -07:00
Nicolas Gallagher
ea744fe780 [add] NativeEventEmitter export
Export NativeEventEmitter and provide React Native's implementation.
2018-06-03 11:32:53 -07:00
Nicolas Gallagher
f254c8eae6 [change] update Animated implementation
Mirror contents of React Native 0.55.4
2018-06-03 11:32:53 -07:00
Nicolas Gallagher
5fcb36fc21 [change] ListView update 2018-06-03 11:32:53 -07:00
Nicolas Gallagher
377f23f725 [add] SectionList
Close #831
2018-06-03 11:32:51 -07:00
Nicolas Gallagher
fc0b81416a [add] FlatList 2018-06-03 10:16:51 -07:00
Nicolas Gallagher
cb545b0dac [add] LayoutAnimation export
Fix #803
2018-06-03 10:16:51 -07:00
Nicolas Gallagher
f684a3656e [fix] InteractionManager improvements 2018-06-03 10:16:51 -07:00
Nicolas Gallagher
45975d3a1e Update and reorganize React Native vendor code 2018-06-03 10:16:50 -07:00
Nicolas Gallagher
2d83ffbd6b Add .prettierignore file 2018-06-03 10:16:50 -07:00
Nicolas Gallagher
64307c066a Update eslint globals 2018-06-03 10:16:50 -07:00
Nicolas Gallagher
bb66639519 [fix] ScrollView stickyHeaderIndices basic support
Close #434
2018-06-03 10:16:47 -07:00
Nicolas Gallagher
c00360491b [fix] ScrollView methods and horizontal layout
Horizontal ScrollView was not scrollable when combined with
RefreshControl.

Add a stub for the flashScrollIndicators method.
2018-05-29 11:04:27 -07:00
Nicolas Gallagher
4bc16fa3eb [fix] AppState should not throw for 'memoryWarning' event 2018-05-29 11:04:27 -07:00
Nicolas Gallagher
2237777341 [fix] Vibration has default pattern
Rather than throwing an error when a pattern is not provided, set a
default value as per React Native.
2018-05-29 11:04:27 -07:00
Nicolas Gallagher
6a2252891a [fix] ImageBackground with <Text> children
Prevent Text from displaying behind the Image.
2018-05-29 11:04:27 -07:00
Nicolas Gallagher
206a236df2 [add] AccessibilityInfo and DeviceInfo stubs 2018-05-29 11:04:27 -07:00
Nicolas Gallagher
ff5a928a50 [fix] processColor compatibility with React Native 2018-05-29 11:04:27 -07:00
Nicolas Gallagher
61bf7e76b0 [fix] CommonJS exports can be imported without 'default'
Make sure all the CommonJS modules can be required as normal, rather
than needing to be suffixed with `.default` due to being compiled from
ES modules.
2018-05-29 11:04:24 -07:00
Nicolas Gallagher
e3170623f1 [fix] use ES modules everywhere
Webpack doesn't like mixing `import` with `module.exports`, and some of
the dynamic requires cannot be safely used in both an ES and CommonJS
module environment without looking for `.default` each time.
2018-05-25 15:45:38 -07:00
Nicolas Gallagher
392de22992 0.7.3 2018-05-25 15:39:03 -07:00
Sunny R Gupta
c84163d80e Fix typo in examples text 2018-05-25 15:06:48 -07:00
Philipp Spiess
50442c4e7c [fix] ResponderEventPlugin with React 16.4
React 16.4 includes the necessary ResponderEventPlugin dependencies and
makes some changes to the event internals that causes a breaking change.
This patch fixes rendering with react-dom@16.4 while preserving
backward-compatibility with earlier versions of react-dom.

Close #908
2018-05-25 15:02:28 -07:00
Nicolas Gallagher
eb0e0b8771 0.7.2 2018-05-19 08:46:55 -07:00
Nicolas Gallagher
15d5e57e92 [fix] convert VirtualizedList vendor code to ES modules
Webpack has trouble bundling a mix of CommonJS and ES modules in the
same package.

Fix #957
2018-05-19 08:45:21 -07:00
Nicolas Gallagher
def873e9e3 0.7.1 2018-05-18 18:16:51 -07:00
Nicolas Gallagher
9e9b40f155 [fix] ResponderEventPlugin injection for tree-shaking bundler
Previously this depended on a side-effecting `import` statement which
would be removed under tree-shaking.
2018-05-18 18:13:30 -07:00
Nicolas Gallagher
4a45595b7a 0.7.0 2018-05-18 17:29:47 -07:00
Nicolas Gallagher
45095fd300 Documentation updates 2018-05-18 17:27:39 -07:00
Nicolas Gallagher
004c7ce478 [change] export ES modules by default
ES modules are the default package export. Commonjs modules are exported
from 'dist/cjs'. Modern bundlers like webpack can consume ES modules.
The addition of the `sideEffects:false` to the `package.json` helps
webpack tree-shaking modules.
2018-05-18 17:27:36 -07:00
Nicolas Gallagher
96c3f09fac [fix] Firefox UI for numeric TextInput
Fix #900
2018-05-18 13:40:15 -07:00
Nicolas Gallagher
edc99e79eb [change] ResponderEventPlugin filters browser emulated mouse events
Browsers dispatch mouse events after touch events:
https://developer.mozilla.org/en-US/docs/Web/API/Touch_events/Supporting_both_TouchEvent_and_MouseEvent

There have been several attempts to avoid this behaviour affecting the
ResponderEvent system. The previous approach of cancelling the event in
the `onResponderRelease` event handler can end up cancelling other
events that are expected, e.g., `focus`.

Instead, this patch changes the `ResponderEventPlugin.extractEvents`
function to filter the mouse events that occur a short time after a
touch event. (It's assumed that people will not be clicking a mouse
within a few hundred ms of performing a touch.) This allows the
ResponderEvent system to function as expected and leaves other callbacks
to fire as they would be expected to in React DOM, i.e., both
`onTouchStart` and `onMouseDown` will be called following a touch start.

Fix #835
Fix #888
Fix #932
Close #938
Ref #802
2018-05-18 13:39:25 -07:00
Nicolas Gallagher
e8f2c98786 [add] mark focusable DOM nodes with 'data-focusable'
Focus-based UIs can use out-of-tree focus algorithms to manage focus
using remote control devices. This patch marks DOM nodes that React
Native considers "focusable".

Close #827
2018-05-18 11:56:56 -07:00
Nicolas Gallagher
dcdf1468f9 [fix] onLayout timing without ResizeObserver
onLayout is called after the component is mounted to the DOM. This makes
both the fallback and ResizeObserver code path behave the same as React
Native.

Fix #911
Fix #941
Close #939
2018-05-18 11:44:30 -07:00
Nicolas Gallagher
ee5e80064f [add] support for CSP policy requiring 'nonce' on <style>
CSP policy may prevent writing to `<style>` unless a `nonce` attribute
is set. This change makes that possible by moving the modality-related
styles into the main style sheet, and allowing additional props to be
provided to the `<style>` element when rendering on the server. For
example:

```
const { element, getStyleElement } = AppRegistry.getApplication('App');
const html = renderToString(element);
const css = renderToStaticMarkup(getStyleElement({ nonce }));
```
2018-05-18 11:44:24 -07:00
Philipp Spiess
3e4d8d6b2f Upgrade React and all transitive dependencies
Close #942
2018-05-16 15:01:19 -07:00
Nicolas Gallagher
d3a8270d55 Add a development sandbox to storybook 2018-05-09 10:38:34 -07:00
Nicolas Gallagher
18933724d6 Remove @providesModule from all modules
Ref: d5e9e55fa3
2018-05-08 10:28:25 -07:00
Nicolas Gallagher
e7f84a9228 Add GitHub issue templates
Close #931
2018-05-08 10:11:27 -07:00
Scott Kyle
8b1e6f816f [change] remove NetInfo.isConnected.getConnectionInfo()
React Native doesn't have `NetInfo.isConnected.getConnectionInfo()`.
This was incorrectly added to the API while updating the main `NetInfo`
API.

Close #937
2018-05-08 10:01:41 -07:00
Scott Kyle
02b6f3ff3c [fix] findNodeHandle import in VirtualizedList
Close #936
2018-05-08 09:53:50 -07:00
odalay
6f52007cc8 Fix CheckboxScreen link on website
Close #926
2018-05-08 09:53:16 -07:00
Nicolas Gallagher
1e59e53e66 Update jest 2018-05-08 09:49:02 -07:00
Nicolas Gallagher
f6a65210ca Update formatter and linter 2018-05-08 09:24:08 -07:00
Nicolas Gallagher
2e9071eb0e Update benchmarks dependencies 2018-05-08 09:15:51 -07:00
Nicolas Gallagher
8f25fcc05b Update storybook dependencies 2018-05-07 15:00:22 -07:00
Nicolas Gallagher
1e8577fc9e [fix] setting flex styles as inline styles
Reverts #648 as browsers are inconsistent in how they handle
'flex-basis', so this hack isn't effective. And React has no support for
using '!important' in inline styles.

Fix #798
2018-04-30 11:51:18 -07:00
Nicolas Gallagher
c51f567d19 Simplify tests for several exported modules 2018-04-24 15:54:40 -07:00
Nicolas Gallagher
bce5957991 0.6.1 2018-04-21 12:16:05 -07:00
Nicolas Gallagher
34d8409d43 Update yarn.lock 2018-04-21 12:15:18 -07:00
Sébastien Lorber
8442f13e96 Add Gatsby integration to README
Close #868
2018-04-21 12:12:50 -07:00
Nicolas Gallagher
c6e2b584af [fix] AppRegistry.getApplication shouldn't throw if missing options
Fix #899
2018-04-21 12:05:42 -07:00
Philipp Spiess
38affa9bae Fix Game2048 on desktop browsers
The React Native example only listens for touch events but on web we can
listen to mouse events to improve support.

Close #909
2018-04-21 11:56:14 -07:00
Nicolas Gallagher
f9ebdb6327 Documentation layout fixes 2018-04-21 11:50:39 -07:00
Nicolas Gallagher
83a8758f68 0.6.0 2018-04-15 15:56:10 -07:00
MoOx
e022d166dd Update Phenomic integration link to official example 2018-04-15 15:26:24 -07:00
Nicolas Gallagher
1a225bc449 [fix] Text onPress event propagation
The press event should not propagate.

Fix #897
2018-04-15 15:23:32 -07:00
Nicolas Gallagher
cf2612663b [fix] createElement uses provided function component
If the component provided to 'createElement' is not a string alias for a
DOM component, it will no longer attempt to replace that component with
a DOM component inferred from the 'accessibility{Component,Role,Traits}'
prop.

Fix #895
2018-04-15 15:03:34 -07:00
Nicolas Gallagher
1aec803086 [fix] Picker.Item support for 'color' prop
Not well supported by browsers.

Fix #810
2018-04-08 11:43:20 -07:00
Nicolas Gallagher
2050730b77 [fix] consistency of nativeEvent.location{X,Y} between touch and mouse
Calculate `location{X,Y}` in the same way for both touch and mouse
events. Also defer the call to `getBoundingClientRect` to avoid
unnecessary DOM queries when the data is not used.
2018-04-08 11:05:12 -07:00
Nicolas Gallagher
a67bf0f490 [change] synchronous layout measurement 2018-04-08 10:38:31 -07:00
Nicolas Gallagher
4529a4ac0a Update benchmarks dependencies 2018-04-07 17:20:01 -07:00
Giuseppe Gurgone
5a04d07a35 [change] call 'onLayout' when elements are resized
Uses ResizeObserver to monitor layout changes. Falls back to
window.onresize (with initial firing).

Fix #60
Close #848
2018-04-07 17:03:17 -07:00
Nicolas Gallagher
9427eea293 [add] VirtualizedList support
Make changes needed to run VirtualizedList using React Native for Web.

Close #659
2018-04-07 10:25:15 -07:00
Nicolas Gallagher
b9803e1e07 Add snapshot of VirtualizedList from React Native
Exact source code of the module and its dependencies, for the given SHA.
2018-04-07 10:25:15 -07:00
slorber
7a3a9a5c3f [add] AppRegistry support for React render callback
Close #858

Co-authored-by: Nicolas Gallagher <nicolasgallagher@gmail.com>
2018-04-07 10:15:17 -07:00
Nicolas Gallagher
4c59343fd3 [fix] SafeAreaView inset padding for Safari
See https://webkit.org/blog/7929/designing-websites-for-iphone-x/

Fix #859
2018-04-02 14:32:15 -07:00
Nicolas Gallagher
ce89b7e3ec [change] move 'react-art' to peerDependencies 2018-04-02 14:31:26 -07:00
Nicolas Gallagher
23fa663a6e [fix] Text font styling
Don't use 'font' shorthand internally to reset font styles, as the
framework's internal styles may be injected after application styles,
causing 'font' to override 'font-*' properties.

Fix #881
2018-04-02 11:37:59 -07:00
Nicolas Gallagher
b96dd668d3 [add] AppRegistry provider methods
Adds support for the following methods in React Native:
- setComponentProviderInstrumentationHook
- setWrapperComponentProvider
2018-04-01 10:29:27 -07:00
Nicolas Gallagher
a9cacb2ef5 [change] AppRegistry container is not absolutely positioned
Aligned with latest layout of AppContainer in React Native.

Fix #871
2018-04-01 09:47:02 -07:00
Nicolas Gallagher
c122814591 0.5.4 2018-04-01 09:34:51 -07:00
Bruno Lemos
efe18f1b7e [fix] Linking.openURL on Firefox
Fix #883
Close #884
2018-04-01 09:25:16 -07:00
Nicolas Gallagher
e1b576e427 [add] StyleSheet support for 'System' in font stack
Allow 'System' to be used in a font-family stack, e.g., "Noto, System".

Update the 'System' font stack to include "system-ui", which is the
latest keyword for system fonts.
2018-03-31 14:57:29 -07:00
Nicolas Gallagher
5d77d6e30f [fix] TextInput focus management
Defer to the browser's native handling of 'blur' and 'focus'; directly
update the internal state of TextInputState. Fixes an issue with
preserving focus when the tab is backgrounded.

Also ensure the TextInputState is correctly set when a component mounts.
When 'autoFocus' is true, 'onFocus' can be called before the internal
ref is set.

Fix #880
2018-03-31 14:01:19 -07:00
Jesús Darío
8fb9a88ee6 Add webpack-cli to getting started guide 2018-03-29 15:49:43 -07:00
Kyle Goggin
a7cda988ef [fix] ignore 'tvParallaxProperties' prop
Close #867
2018-03-29 15:47:39 -07:00
Nicolas Gallagher
b239cfb04d [fix] update InteractionManager stub
Fix #875
2018-03-29 15:32:38 -07:00
Nicolas Gallagher
8e94d858b2 [fix] Picker prop types
Fix #879
2018-03-29 15:11:16 -07:00
Nicolas Gallagher
aa22b06359 0.5.3 2018-03-15 13:32:24 -07:00
Paul Armstrong
2aa565c7c3 [fix] Picker default fontFamily
Close #860
2018-03-15 13:08:23 -07:00
Sakamoto, Kazunori
7b9b57960d Update getting-started.md
Fix __DEV__ value in example web/webpack.config.js

Close #862
2018-03-15 13:06:58 -07:00
575 changed files with 40986 additions and 9765 deletions

View File

@@ -2,3 +2,4 @@ coverage
dist
node_modules
packages/**/vendor/*
packages/examples

View File

@@ -25,7 +25,8 @@
"document": false,
"navigator": false,
"window": false,
// Flow global types
// Flow global types,
"$Enum": false,
"HTMLInputElement": false,
"ReactClass": false,
"ReactComponent": false,

View File

@@ -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]

View File

@@ -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

View File

@@ -1,20 +0,0 @@
**Do you want to request a *feature* or report a *bug*?**
**What is the current behavior?**
**If the current behavior is a bug, please provide the steps to reproduce and
if a minimal demo of the problem via Glitch or similar (template:
https://glitch.com/edit/#!/react-native-web-playground).**
1.
2.
**What is the expected behavior?**
**Environment (include versions). Did this work in previous versions?**
* OS:
* Device:
* Browser:
* React Native for Web (version):
* React (version):

49
.github/ISSUE_TEMPLATE/bug.md vendored Normal file
View File

@@ -0,0 +1,49 @@
---
name: "\U0001F41B Bug report"
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**
<!--
REQUIRED: A clear and concise description of the bug or problem.
-->
**How to reproduce**
<!--
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.
**Expected behavior**
<!--
REQUIRED: A clear and concise description of what you expected to happen.
-->
**Environment (include versions). Did this work in previous versions?**
* React Native for Web (version): TBC
* React (version): TBC
* Browser: TBC
<!--
OPTIONAL:
**Additional context**
Add any other context about the problem here.
-->

17
.github/ISSUE_TEMPLATE/feature.md vendored Normal file
View File

@@ -0,0 +1,17 @@
---
name: "\U0001F680 Feature request"
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 [...] -->
**Describe a solution you'd like**
<!-- 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. -->
**Additional context**
<!-- Add any other context or screenshots about the feature request here. -->

View File

@@ -1 +0,0 @@
**Before submitting a pull request,** please make sure you have followed the steps the CONTRIBUTING guide.

5
.prettierignore Normal file
View File

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

169
README.md
View File

@@ -2,69 +2,70 @@
[![npm version][package-badge]][package-url] [![Build Status][ci-badge]][ci-url] [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://reactjs.org/docs/how-to-contribute.html#your-first-pull-request)
"React Native for Web" brings the platform-agnostic Components and APIs of
[React Native][react-native-url] to the Web.
**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
[ready-to-go project on Glitch](https://glitch.com/edit/#!/react-native).
You dont need to install anything to try it out.
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 dont need to install
anything to try it out.
For installation and configuration details please read the [getting
started](https://github.com/necolas/react-native-web/blob/master/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
system](https://facebook.github.io/react-native/docs/gesture-responder-system.html)
and [animations](https://facebook.github.io/react-native/docs/animations.html).
### Installation
Install using `yarn` or `npm`:
```
yarn add react react-dom react-native-web
yarn add --dev babel-plugin-react-native-web
```
### 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';
@@ -99,11 +100,100 @@ Native. This allows the app to be rendered to web and native platforms.
Examples of using React Native for Web with other web tools:
* [Gatsby](https://github.com/gatsbyjs/gatsby/tree/master/examples/using-react-native-web)
* [Next.js](https://github.com/zeit/next.js/tree/master/examples/with-react-native-web)
* [Storybook](https://github.com/necolas/react-native-web/tree/0.5.1/website/storybook/.storybook)
* [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)
* [Phenomic](https://github.com/phenomic/phenomic/tree/v1.0.0-alpha.20/docs)
* [Styleguidist](https://github.com/styleguidist/react-styleguidist/tree/v6.2.6/examples/react-native)
* [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
@@ -142,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

View File

@@ -1,31 +1,35 @@
{
"private": true,
"version": "0.5.2",
"version": "0.8.9",
"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": "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": "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",
"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": {
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-eslint": "^8.0.3",
"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",
@@ -37,34 +41,37 @@
"del-cli": "^1.1.0",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.0",
"enzyme-to-json": "^3.2.2",
"eslint": "^4.12.1",
"enzyme-to-json": "^3.3.3",
"eslint": "^4.19.1",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-promise": "^3.6.0",
"eslint-plugin-react": "^7.5.1",
"eslint-plugin-promise": "^3.7.0",
"eslint-plugin-react": "^7.7.0",
"flow-bin": "^0.63.1",
"glob": "^7.1.2",
"husky": "^0.14.3",
"jest": "^21.2.1",
"lint-staged": "^6.0.0",
"prettier": "^1.8.2",
"raf": "^3.4.0",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-test-renderer": "^16.2.0"
"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.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": [
"fmt:cmd",
"prettier --write",
"git update-index --again",
"eslint"
],
"packages/react-native-web/src/index.js": [
"node ./scripts/babel/createModuleMap.js"
]
},
"prettier": {

View File

@@ -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

View File

@@ -1,6 +1,6 @@
{
"name": "babel-plugin-react-native-web",
"version": "0.5.2",
"version": "0.8.9",
"description": "Babel plugin for React Native for Web",
"main": "index.js",
"devDependencies": {

View File

@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`export from "react-native" 1`] = `
exports[`Rewrite react-native to react-native-web export from "react-native": export from "react-native" 1`] = `
"
export { View } from 'react-native';
export { ColorPropType, StyleSheet, Text, createElement } from 'react-native';
@@ -15,7 +15,7 @@ export { default as createElement } from 'react-native-web/dist/exports/createEl
"
`;
exports[`export from "react-native-web" 1`] = `
exports[`Rewrite react-native to react-native-web export from "react-native-web": export from "react-native-web" 1`] = `
"
export { View } from 'react-native-web';
export { ColorPropType, StyleSheet, Text, createElement } from 'react-native-web';
@@ -30,7 +30,7 @@ export { default as createElement } from 'react-native-web/dist/exports/createEl
"
`;
exports[`import from "native-native" 1`] = `
exports[`Rewrite react-native to react-native-web import from "native-native": import from "native-native" 1`] = `
"
import ReactNative from 'react-native';
import { View } from 'react-native';
@@ -48,7 +48,25 @@ import * as ReactNativeModules from 'react-native-web/dist/index';
"
`;
exports[`import from "react-native-web" 1`] = `
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';
import { ColorPropType, StyleSheet, View, TouchableOpacity, processColor } from 'react-native-web';
@@ -66,11 +84,11 @@ import * as ReactNativeModules from 'react-native-web/dist/index';
"
`;
exports[`require "react-native" 1`] = `
exports[`Rewrite react-native to react-native-web require "react-native": require "react-native" 1`] = `
"
const ReactNative = require('react-native');
const { View } = require('react-native');
const { StyleSheet, TouchableOpacity } = require('react-native');
const { StyleSheet, TouchableOpacity } = require('react-native');
↓ ↓ ↓ ↓ ↓ ↓
@@ -84,7 +102,25 @@ const TouchableOpacity = require('react-native-web/dist/exports/TouchableOpacity
"
`;
exports[`require "react-native-web" 1`] = `
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');
const { createElement } = require('react-native-web');

View File

@@ -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');

View File

@@ -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')
)

View File

@@ -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,

View File

@@ -1,36 +1,37 @@
{
"private": true,
"name": "benchmarks",
"version": "0.5.2",
"version": "0.8.9",
"scripts": {
"build": "mkdir -p dist && cp -f index.html dist/index.html && webpack --config ./webpack.config.js",
"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": "1.2.5",
"classnames": "^2.2.5",
"d3-scale-chromatic": "^1.2.0",
"emotion": "^9.0.2",
"fela": "^6.1.4",
"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.22.1",
"react": "^16.2.0",
"react-component-benchmark": "^0.0.4",
"react-dom": "^16.2.0",
"react-fela": "^7.0.1",
"react-jss": "^8.3.3",
"react-native-web": "0.5.2",
"reactxp": "^1.0.0",
"styled-components": "^3.2.0",
"styled-jsx": "^2.2.5",
"styletron-engine-atomic": "^1.0.0",
"styletron-react": "^4.0.0"
"radium": "^0.24.0",
"react": "^16.4.1",
"react-dom": "^16.4.1",
"react-fela": "^7.3.1",
"react-jss": "^8.6.1",
"react-native-web": "0.8.9",
"reactxp": "^1.3.0",
"styled-components": "^3.3.3",
"styled-jsx": "^2.2.7",
"styletron-engine-atomic": "^1.0.5",
"styletron-react": "^4.3.1"
},
"devDependencies": {
"babel-plugin-react-native-web": "0.5.2",
"css-loader": "^0.28.10",
"style-loader": "^0.20.2",
"webpack": "^3.10.0",
"webpack-bundle-analyzer": "^2.9.2"
"babel-plugin-react-native-web": "0.8.9",
"css-loader": "^1.0.0",
"style-loader": "^0.21.0",
"url-loader": "^1.0.1",
"webpack": "^4.15.1",
"webpack-bundle-analyzer": "^2.13.1",
"webpack-cli": "^3.0.8"
}
}

View File

@@ -292,7 +292,6 @@ const styles = StyleSheet.create({
},
button: {
borderRadius: 0,
fontSize: 32,
flex: 1
}
});

View File

@@ -1,3 +1,9 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017 Paul Armstrong
* https://github.com/paularmstrong/react-component-benchmark
*/
/* global $Values */
/**
* @flow

View File

@@ -56,6 +56,7 @@ const styles = StyleSheet.create({
},
text: {
color: '#fff',
fontSize: 20,
fontWeight: '500',
padding: 8,
textAlign: 'center',

View File

@@ -1,4 +1,4 @@
import { BenchmarkType } from 'react-component-benchmark';
import { BenchmarkType } from '../app/Benchmark';
import { number, object } from 'prop-types';
import React from 'react';
import { interpolatePurples, interpolateBuPu, interpolateRdPu } from 'd3-scale-chromatic';

View File

@@ -1,4 +1,4 @@
import { BenchmarkType } from 'react-component-benchmark';
import { BenchmarkType } from '../app/Benchmark';
import { number, object } from 'prop-types';
import React, { Component } from 'react';

View File

@@ -25,7 +25,7 @@ const Box = styled(View)`
flex-direction: ${props => (props.layout === 'column' ? 'column' : 'row')};
padding: ${props => (props.outer ? '4px' : '0')};
${props => props.fixed && 'height:6px;'} ${props =>
props.fixed && 'width:6px;'} background-color: ${props => getColor(props.color)};
props.fixed && 'width:6px;'} background-color: ${props => getColor(props.color)};
`;
export default Box;

View File

@@ -1,11 +1,11 @@
const babelPreset = require('../../scripts/babel/preset');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const path = require('path');
const webpack = require('webpack');
const appDirectory = path.resolve(__dirname);
module.exports = {
mode: 'production',
context: __dirname,
entry: './src/index',
output: {
@@ -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]' }
}
]
},
@@ -32,7 +32,7 @@ module.exports = {
options: {
cacheDirectory: false,
presets: babelPreset,
plugins: ['react-native-web', 'styled-jsx/babel']
plugins: ['styled-jsx/babel']
}
}
}
@@ -42,17 +42,11 @@ module.exports = {
new BundleAnalyzerPlugin({
analyzerMode: 'static',
openAnalyzer: false
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
dead_code: true,
drop_console: false,
screw_ie8: true,
warnings: false
}
})
]
],
resolve: {
alias: {
'react-native': 'react-native-web'
}
}
};

View 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
View File

@@ -0,0 +1,2 @@
facebook/react-native@v0.55.4
facebook/react-native@370bcffba748e895ad8afa825bfef40bff859c95

View File

@@ -0,0 +1,23 @@
{
"private": true,
"name": "react-native-examples",
"version": "0.8.9",
"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.9"
},
"devDependencies": {
"babel-plugin-react-native-web": "0.8.9",
"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"
}
}

View 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 />;
}
},
];

View File

@@ -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;

View 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 />; }
},
];

View 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 />;
}
}
];

View 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,
},
});

View 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,
};

View 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,
},
});

View 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',
},
});

View 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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View 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;

View File

@@ -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 />

View 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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

View 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} />; }
},
];

View 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;

View 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 />; }
},
];

View 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]} />;
}
}
];

View 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>;
}
},
];

View 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"
/>
);
},
},
];

View 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 />;
},
},
];

View 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;

View 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;

View 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/>;
}
}
];

View 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;

View 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,
},
});

View 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" />; }
},
];

View 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>,
};

View 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;

View 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',
},
});

View 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;

View 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
*/

View 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',
},
});

View 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;

View 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;

View 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 />;
},
}];

View 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 />;
},
}];

View 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;

View 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;

View 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,
};

View 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;

View 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;

View 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;

View 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;

View 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,
},
});

View 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;

View 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 />;
},
},
];

View File

@@ -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;

View 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;

View 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;

View 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 />; }
},
];

View 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 />; },
},
];

View 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;

View File

@@ -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,
}
});

View 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;

View 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 />;
},
}];

View 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);

View File

@@ -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;

View 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,
}
});

View 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 />;
}
}];

View 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/>
);
},
}];

View 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;

View 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;

View 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;

View 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;

View 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;

View 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;

View 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;

View 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;

View 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;

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