mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-03-30 23:23:35 +08:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88d13f06f8 | ||
|
|
3dd94880e0 | ||
|
|
e1080d72d7 | ||
|
|
55849cdd0d | ||
|
|
0aef117733 | ||
|
|
977d8729f5 | ||
|
|
9222cbf4bd | ||
|
|
103560fc11 | ||
|
|
3a2daf386d | ||
|
|
6640b61b3e | ||
|
|
07d1124d60 | ||
|
|
c7b3a8e60b | ||
|
|
d32eec7239 | ||
|
|
f8f2898095 | ||
|
|
201bfd2e4d | ||
|
|
496839d19a | ||
|
|
6fe3f1f533 | ||
|
|
aa53d931d5 | ||
|
|
88b184d540 | ||
|
|
011affb110 | ||
|
|
87a4f56a89 | ||
|
|
2f94295739 | ||
|
|
fdf4a88251 |
215
.eslintrc
215
.eslintrc
@@ -1,14 +1,213 @@
|
||||
{
|
||||
// babel parser to support ES features
|
||||
// babel parser to support ES6/7 features
|
||||
"parser": "babel-eslint",
|
||||
// based on https://github.com/feross/standard
|
||||
"extends": [ "standard", "standard-react" ],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 7,
|
||||
"ecmaFeatures": {
|
||||
"experimentalObjectRestSpread": true,
|
||||
"jsx": true
|
||||
},
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"jsx-a11y",
|
||||
"promise",
|
||||
"react"
|
||||
],
|
||||
"env": {
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"globals": {
|
||||
"document": false,
|
||||
"navigator": false,
|
||||
"window": false
|
||||
},
|
||||
"rules": {
|
||||
// overrides of the standard style
|
||||
"space-before-function-paren": [ 2, { "anonymous": "always", "named": "never" } ],
|
||||
"wrap-iife": [ 2, "outside" ],
|
||||
// overrides of the standard-react style
|
||||
"accessor-pairs": 2,
|
||||
"array-bracket-spacing": ["error", "always"],
|
||||
"arrow-parens": [2, "always"],
|
||||
"arrow-spacing": [2, { "before": true, "after": true }],
|
||||
"block-spacing": [2, "always"],
|
||||
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
|
||||
"camelcase": 0,
|
||||
"comma-dangle": [2, "never"],
|
||||
"comma-spacing": [2, { "before": false, "after": true }],
|
||||
"comma-style": [2, "last"],
|
||||
"computed-property-spacing": ["error", "never"],
|
||||
"constructor-super": 2,
|
||||
"curly": [2, "all"],
|
||||
"default-case": [2, { commentPattern: '^no default$' }],
|
||||
"dot-location": [2, "property"],
|
||||
"eol-last": 2,
|
||||
"eqeqeq": [2, "allow-null"],
|
||||
"generator-star-spacing": [2, { "before": true, "after": true }],
|
||||
"handle-callback-err": [2, "^(err|error)$" ],
|
||||
"indent": [2, 2, { "SwitchCase": 1 }],
|
||||
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
|
||||
"keyword-spacing": [2, { "before": true, "after": true }],
|
||||
"max-len": [2, 120, 4],
|
||||
"new-cap": [2, { "newIsCap": true, "capIsNew": false }],
|
||||
"new-parens": 2,
|
||||
"no-alert": 1,
|
||||
"no-array-constructor": 2,
|
||||
"no-caller": 2,
|
||||
"no-case-declarations": 2,
|
||||
"no-class-assign": 2,
|
||||
"no-cond-assign": 2,
|
||||
"no-const-assign": 2,
|
||||
"no-control-regex": 2,
|
||||
"no-debugger": 2,
|
||||
"no-delete-var": 2,
|
||||
"no-dupe-args": 2,
|
||||
"no-dupe-class-members": 2,
|
||||
"no-dupe-keys": 2,
|
||||
"no-duplicate-case": 2,
|
||||
"no-duplicate-imports": 2,
|
||||
"no-empty-character-class": 2,
|
||||
"no-empty-pattern": 2,
|
||||
"no-eval": 2,
|
||||
"no-ex-assign": 2,
|
||||
"no-extend-native": 2,
|
||||
"no-extra-bind": 2,
|
||||
"no-extra-boolean-cast": 2,
|
||||
"no-extra-parens": [2, "functions"],
|
||||
"no-extra-semi": 2,
|
||||
"no-fallthrough": 2,
|
||||
"no-floating-decimal": 2,
|
||||
"no-func-assign": 2,
|
||||
"no-implied-eval": 2,
|
||||
"no-inner-declarations": [2, "functions"],
|
||||
"no-invalid-regexp": 2,
|
||||
"no-irregular-whitespace": 2,
|
||||
"no-iterator": 2,
|
||||
"no-label-var": 2,
|
||||
"no-labels": [2, { "allowLoop": false, "allowSwitch": false }],
|
||||
"no-lone-blocks": 2,
|
||||
"no-loop-func": 2,
|
||||
"no-mixed-spaces-and-tabs": 2,
|
||||
"no-multi-spaces": 2,
|
||||
"no-multi-str": 2,
|
||||
"no-multiple-empty-lines": [2, { "max": 1 }],
|
||||
"no-native-reassign": 2,
|
||||
"no-negated-in-lhs": 2,
|
||||
"no-new": 2,
|
||||
"no-new-func": 2,
|
||||
"no-new-object": 2,
|
||||
"no-new-require": 2,
|
||||
"no-new-symbol": 2,
|
||||
"no-new-wrappers": 2,
|
||||
"no-obj-calls": 2,
|
||||
"no-octal": 2,
|
||||
"no-octal-escape": 2,
|
||||
"no-path-concat": 2,
|
||||
"no-proto": 2,
|
||||
"no-redeclare": 2,
|
||||
"no-regex-spaces": 2,
|
||||
"no-return-assign": [2, "except-parens"],
|
||||
"no-script-url": 2,
|
||||
"no-self-assign": 2,
|
||||
"no-self-compare": 2,
|
||||
"no-sequences": 2,
|
||||
"no-shadow-restricted-names": 2,
|
||||
"no-spaced-func": 2,
|
||||
"no-sparse-arrays": 2,
|
||||
"no-this-before-super": 2,
|
||||
"no-throw-literal": 2,
|
||||
"no-trailing-spaces": 2,
|
||||
"no-undef": 2,
|
||||
"no-undef-init": 2,
|
||||
"no-unexpected-multiline": 2,
|
||||
"no-unmodified-loop-condition": 2,
|
||||
"no-unneeded-ternary": [2, { "defaultAssignment": false }],
|
||||
"no-unreachable": 2,
|
||||
"no-unsafe-finally": 2,
|
||||
"no-unused-vars": [2, { "vars": "all", "args": "none" }],
|
||||
"no-useless-call": 2,
|
||||
"no-useless-computed-key": 2,
|
||||
"no-useless-concat": 2,
|
||||
"no-useless-constructor": 2,
|
||||
"no-useless-escape": 2,
|
||||
"no-var": 2,
|
||||
"no-whitespace-before-property": 2,
|
||||
"no-with": 2,
|
||||
"object-curly-spacing": ["error", "always"],
|
||||
"operator-linebreak": [2, "after"],
|
||||
"padded-blocks": [2, "never"],
|
||||
"prefer-const": 2,
|
||||
"prefer-rest-params": 2,
|
||||
"prefer-template": 2,
|
||||
"quotes": [2, "single", "avoid-escape"],
|
||||
"radix": 2,
|
||||
"rest-spread-spacing": ["error"],
|
||||
"semi": [2, "always"],
|
||||
"semi-spacing": [2, { "before": false, "after": true }],
|
||||
"space-before-blocks": [2, "always"],
|
||||
"space-before-function-paren": [2, { "anonymous": "always", "named": "never" }],
|
||||
"space-in-parens": [2, "never"],
|
||||
"space-infix-ops": 2,
|
||||
"space-unary-ops": [2, { "words": true, "nonwords": false }],
|
||||
"spaced-comment": [2, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }],
|
||||
"template-curly-spacing": [2, "never"],
|
||||
"use-isnan": 2,
|
||||
"valid-typeof": 2,
|
||||
"wrap-iife": [2, "outside"],
|
||||
"yield-star-spacing": [2, "both"],
|
||||
"yoda": [2, "never"],
|
||||
|
||||
// promise
|
||||
"promise/param-names": 2,
|
||||
|
||||
// jsx accessibility
|
||||
"jsx-a11y/aria-props": 2,
|
||||
"jsx-a11y/aria-proptypes": 2,
|
||||
"jsx-a11y/aria-role": 2,
|
||||
"jsx-a11y/aria-unsupported-elements": 2,
|
||||
"jsx-a11y/heading-has-content": 2,
|
||||
"jsx-a11y/href-no-hash": 2,
|
||||
"jsx-a11y/html-has-lang": 2,
|
||||
"jsx-a11y/img-has-alt": 2,
|
||||
"jsx-a11y/img-redundant-alt": 2,
|
||||
"jsx-a11y/label-has-for": 2,
|
||||
"jsx-a11y/mouse-events-have-key-events": 2,
|
||||
"jsx-a11y/no-access-key": 2,
|
||||
"jsx-a11y/no-marquee": 2,
|
||||
"jsx-a11y/no-onchange": 0,
|
||||
"jsx-a11y/onclick-has-focus": 2,
|
||||
"jsx-a11y/onclick-has-role": 2,
|
||||
"jsx-a11y/role-has-required-aria-props": 2,
|
||||
"jsx-a11y/role-supports-aria-props": 2,
|
||||
"jsx-a11y/scope": 2,
|
||||
"jsx-a11y/tabindex-no-positive": 2,
|
||||
|
||||
// react
|
||||
"jsx-quotes": [2, "prefer-single"],
|
||||
"react/display-name": 0,
|
||||
"react/jsx-boolean-value": 2,
|
||||
"react/jsx-handler-names": [2, {
|
||||
"eventHandlerPrefix": "_handle"
|
||||
}],
|
||||
"react/jsx-indent": [2, 2],
|
||||
"react/jsx-indent-props": [2, 2],
|
||||
"react/jsx-no-bind": 2,
|
||||
"react/jsx-no-duplicate-props": 2,
|
||||
"react/jsx-no-undef": 2,
|
||||
"react/jsx-pascal-case": 2,
|
||||
"react/jsx-sort-props": 2,
|
||||
"react/jsx-sort-prop-types": 2
|
||||
"react/jsx-uses-react": 2,
|
||||
"react/jsx-uses-vars": 2,
|
||||
"react/no-did-mount-set-state": 0,
|
||||
"react/no-did-update-set-state": 2,
|
||||
"react/no-direct-mutation-state": 2,
|
||||
"react/no-multi-comp": 0,
|
||||
"react/no-string-refs": 2,
|
||||
"react/no-unknown-property": 2,
|
||||
"react/prefer-es6-class": 2,
|
||||
"react/prop-types": 2,
|
||||
"react/react-in-jsx-scope": 0,
|
||||
"react/self-closing-comp": 2,
|
||||
"react/sort-comp": 0,
|
||||
"react/sort-prop-types": 2,
|
||||
"react/wrap-multilines": 0
|
||||
}
|
||||
}
|
||||
|
||||
138
README.md
138
README.md
@@ -18,6 +18,10 @@ Browser support: Chrome, Firefox, Safari >= 7, IE 10, Edge.
|
||||
"React Native for Web" is a project to bring React Native's building blocks and
|
||||
touch handling to the Web. [Read more](#why).
|
||||
|
||||
Browse the UI Explorer to see React Native [examples running on
|
||||
Web](https://necolas.github.io/react-native-web/storybook/). Or try it out
|
||||
online with [React Native for Web: Playground](http://codepen.io/necolas/pen/PZzwBR).
|
||||
|
||||
## Quick start
|
||||
|
||||
To install in your app:
|
||||
@@ -28,15 +32,71 @@ npm install --save react react-native-web
|
||||
|
||||
Read the [Client and Server rendering](docs/guides/rendering.md) guide.
|
||||
|
||||
You can also bootstrap a standard React Native project structure for web by
|
||||
using [react-native-web-starter](https://github.com/grabcode/react-native-web-starter).
|
||||
Alternatively, you can quickly setup a local project
|
||||
using [create-react-app](https://github.com/facebookincubator/create-react-app)
|
||||
(which supports `react-native-web` out-of-the-box once installed) and
|
||||
[react-native-web-starter](https://github.com/grabcode/react-native-web-starter).
|
||||
|
||||
## Examples
|
||||
## Documentation
|
||||
|
||||
* React Native [examples running on Web](https://necolas.github.io/react-native-web/storybook/)
|
||||
* [React Native for Web: Playground](http://codepen.io/necolas/pen/PZzwBR).
|
||||
Guides:
|
||||
|
||||
Sample:
|
||||
* [Accessibility](docs/guides/accessibility.md)
|
||||
* [Client and server rendering](docs/guides/rendering.md)
|
||||
* [Direct manipulation](docs/guides/direct-manipulation.md)
|
||||
* [Internationalization](docs/guides/internationalization.md)
|
||||
* [Known issues](docs/guides/known-issues.md)
|
||||
* [React Native](docs/guides/react-native.md)
|
||||
* [Style](docs/guides/style.md)
|
||||
|
||||
Exported modules:
|
||||
|
||||
* Components
|
||||
* [`ActivityIndicator`](docs/components/ActivityIndicator.md)
|
||||
* [`Image`](docs/components/Image.md)
|
||||
* [`ListView`](docs/components/ListView.md)
|
||||
* [`ScrollView`](docs/components/ScrollView.md)
|
||||
* [`Switch`](docs/components/Switch.md)
|
||||
* [`Text`](docs/components/Text.md)
|
||||
* [`TextInput`](docs/components/TextInput.md)
|
||||
* [`TouchableHighlight`](http://facebook.github.io/react-native/releases/0.22/docs/touchablehighlight.html) (mirrors React Native)
|
||||
* [`TouchableOpacity`](http://facebook.github.io/react-native/releases/0.22/docs/touchableopacity.html) (mirrors React Native)
|
||||
* [`TouchableWithoutFeedback`](docs/components/TouchableWithoutFeedback.md)
|
||||
* [`View`](docs/components/View.md)
|
||||
* APIs
|
||||
* [`Animated`](http://facebook.github.io/react-native/releases/0.20/docs/animated.html) (mirrors React Native)
|
||||
* [`AppRegistry`](docs/apis/AppRegistry.md)
|
||||
* [`AppState`](docs/apis/AppState.md)
|
||||
* [`AsyncStorage`](docs/apis/AsyncStorage.md)
|
||||
* [`Dimensions`](docs/apis/Dimensions.md)
|
||||
* [`I18nManager`](docs/apis/I18nManager.md)
|
||||
* [`NativeMethods`](docs/apis/NativeMethods.md)
|
||||
* [`NetInfo`](docs/apis/NetInfo.md)
|
||||
* [`PanResponder`](http://facebook.github.io/react-native/releases/0.20/docs/panresponder.html#content) (mirrors React Native)
|
||||
* [`PixelRatio`](docs/apis/PixelRatio.md)
|
||||
* [`Platform`](docs/apis/Platform.md)
|
||||
* [`StyleSheet`](docs/apis/StyleSheet.md)
|
||||
* [`Vibration`](docs/apis/Vibration.md)
|
||||
|
||||
<span id="#why"></span>
|
||||
## Why?
|
||||
|
||||
There are many different teams at Twitter building web applications with React.
|
||||
We want to share React components, libraries, and APIs between teams…much like
|
||||
the OSS community tries to do. At our scale, this involves dealing with
|
||||
multiple, inter-related problems including: a common way to handle style,
|
||||
animation, touch, viewport adaptation, accessibility, themes, RTL layout, and
|
||||
server-rendering.
|
||||
|
||||
This is hard to do with React DOM, as the components are essentially the same
|
||||
low-level building blocks that the browser provides. However, React Native
|
||||
avoids, solves, or can solve almost all these problems facing Web teams.
|
||||
Central to this is React Native's JavaScript style API (not strictly
|
||||
"CSS-in-JS") which avoids the key [problems with
|
||||
CSS](https://speakerdeck.com/vjeux/react-css-in-js) by giving up some of the
|
||||
complexity of CSS.
|
||||
|
||||
## Example code
|
||||
|
||||
```js
|
||||
import React from 'react'
|
||||
@@ -75,72 +135,6 @@ AppRegistry.registerComponent('MyApp', () => App)
|
||||
AppRegistry.runApplication('MyApp', { rootTag: document.getElementById('react-root') })
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Guides:
|
||||
|
||||
* [Accessibility](docs/guides/accessibility.md)
|
||||
* [Client and server rendering](docs/guides/rendering.md)
|
||||
* [Direct manipulation](docs/guides/direct-manipulation.md)
|
||||
* [Known issues](docs/guides/known-issues.md)
|
||||
* [React Native](docs/guides/react-native.md)
|
||||
* [Style](docs/guides/style.md)
|
||||
|
||||
Exported modules:
|
||||
|
||||
* Components
|
||||
* [`ActivityIndicator`](docs/components/ActivityIndicator.md)
|
||||
* [`Image`](docs/components/Image.md)
|
||||
* [`ListView`](docs/components/ListView.md)
|
||||
* [`ScrollView`](docs/components/ScrollView.md)
|
||||
* [`Text`](docs/components/Text.md)
|
||||
* [`TextInput`](docs/components/TextInput.md)
|
||||
* [`TouchableHighlight`](http://facebook.github.io/react-native/releases/0.22/docs/touchablehighlight.html) (mirrors React Native)
|
||||
* [`TouchableOpacity`](http://facebook.github.io/react-native/releases/0.22/docs/touchableopacity.html) (mirrors React Native)
|
||||
* [`TouchableWithoutFeedback`](docs/components/TouchableWithoutFeedback.md)
|
||||
* [`View`](docs/components/View.md)
|
||||
* APIs
|
||||
* [`Animated`](http://facebook.github.io/react-native/releases/0.20/docs/animated.html) (mirrors React Native)
|
||||
* [`AppRegistry`](docs/apis/AppRegistry.md)
|
||||
* [`AppState`](docs/apis/AppState.md)
|
||||
* [`AsyncStorage`](docs/apis/AsyncStorage.md)
|
||||
* [`Dimensions`](docs/apis/Dimensions.md)
|
||||
* [`I18nManager`](docs/apis/I18nManager.md)
|
||||
* [`NativeMethods`](docs/apis/NativeMethods.md)
|
||||
* [`NetInfo`](docs/apis/NetInfo.md)
|
||||
* [`PanResponder`](http://facebook.github.io/react-native/releases/0.20/docs/panresponder.html#content) (mirrors React Native)
|
||||
* [`PixelRatio`](docs/apis/PixelRatio.md)
|
||||
* [`Platform`](docs/apis/Platform.md)
|
||||
* [`StyleSheet`](docs/apis/StyleSheet.md)
|
||||
* [`Vibration`](docs/apis/Vibration.md)
|
||||
|
||||
<span id="#why"></span>
|
||||
## Why?
|
||||
|
||||
React Native is a comprehensive JavaScript framework for building application
|
||||
user interfaces. It provides high-level, platform-agnostic components and APIs
|
||||
– e.g., `Text`, `View`, `Touchable*`, `Animated`, `StyleSheet` - that simplify
|
||||
working with layout, gestures, animations, and styles. The entire React Native
|
||||
ecosystem can depend on these shared building blocks.
|
||||
|
||||
In contrast, the React DOM ecosystem is limited by the lack of a higher-level
|
||||
framework. At Twitter, we want to seamlessly author and share React component
|
||||
libraries between different Web applications (with increasing interest from
|
||||
product teams for multi-platform solutions). This goal draws together multiple,
|
||||
inter-related problems including: styling, animation, gestures, themes,
|
||||
viewport adaptation, accessibility, diverse build processes, and RTL layouts.
|
||||
|
||||
Almost all these problems are avoided, solved, or can be solved in React
|
||||
Native. Central to this is React Native's JavaScript style API (not strictly
|
||||
"CSS-in-JS") which avoids the key [problems with
|
||||
CSS](https://speakerdeck.com/vjeux/react-css-in-js). By giving up some of the
|
||||
complexity of CSS it also provides a reliable surface for style composition,
|
||||
animation, gestures, server-side rendering, RTL layout; and removes the
|
||||
requirement for CSS-specific build tools.
|
||||
|
||||
Bringing the React Native APIs and components to the Web has the added benefit
|
||||
of allowing teams to explore code-sharing between Native and Web platforms.
|
||||
|
||||
## Related projects
|
||||
|
||||
* [react-native-web-starter](https://github.com/grabcode/react-native-web-starter)
|
||||
|
||||
@@ -6,17 +6,17 @@
|
||||
|
||||
**animating**: bool = true
|
||||
|
||||
Whether to show the indicator (true, the default) or hide it (false).
|
||||
Whether to show the indicator or hide it.
|
||||
|
||||
**color**: string = #999999
|
||||
**color**: string = '#1976D2'
|
||||
|
||||
The foreground color of the spinner (default is gray).
|
||||
The foreground color of the spinner.
|
||||
|
||||
**hidesWhenStopped**: bool = true
|
||||
|
||||
Whether the indicator should hide when not animating (true by default).
|
||||
Whether the indicator should hide when not animating.
|
||||
|
||||
**size**: oneOf('small, 'large')
|
||||
**size**: oneOf('small, 'large') | number = 'small'
|
||||
|
||||
Size of the indicator. Small has a height of `20`, large has a height of `36`.
|
||||
|
||||
|
||||
23
docs/components/ProgressBar.md
Normal file
23
docs/components/ProgressBar.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# ProgressBar
|
||||
|
||||
Display an activity progress bar.
|
||||
|
||||
## Props
|
||||
|
||||
[...View props](./View.md)
|
||||
|
||||
**color**: string = '#1976D2'
|
||||
|
||||
Color of the progress bar.
|
||||
|
||||
**indeterminate**: bool = true
|
||||
|
||||
Whether the progress bar will show indeterminate progress.
|
||||
|
||||
**progress**: number
|
||||
|
||||
The progress value (between 0 and 1).
|
||||
|
||||
(web) **trackColor**: string = 'transparent'
|
||||
|
||||
Color of the track bar.
|
||||
76
docs/components/Switch.md
Normal file
76
docs/components/Switch.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Switch
|
||||
|
||||
This is a controlled component that requires an `onValueChange` callback that
|
||||
updates the value prop in order for the component to reflect user actions. If
|
||||
the `value` prop is not updated, the component will continue to render the
|
||||
supplied `value` prop instead of the expected result of any user actions.
|
||||
|
||||
## Props
|
||||
|
||||
[...View props](./View.md)
|
||||
|
||||
**disabled**: bool = false
|
||||
|
||||
If `true` the user won't be able to interact with the switch.
|
||||
|
||||
**onValueChange**: func
|
||||
|
||||
Invoked with the new value when the value changes.
|
||||
|
||||
**value**: bool = false
|
||||
|
||||
The value of the switch. If `true` the switch will be turned on.
|
||||
|
||||
(web) **activeThumbColor**: color = #009688
|
||||
|
||||
The color of the thumb grip when the switch is turned on.
|
||||
|
||||
(web) **activeTrackColor**: color = #A3D3CF
|
||||
|
||||
The color of the track when the switch is turned on.
|
||||
|
||||
(web) **thumbColor**: color = #FAFAFA
|
||||
|
||||
The color of the thumb grip when the switch is turned off.
|
||||
|
||||
(web) **trackColor**: color = #939393
|
||||
|
||||
The color of the track when the switch is turned off.
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
import React, { Component } from 'react'
|
||||
import { Switch, View } from 'react-native'
|
||||
|
||||
class ColorSwitchExample extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
colorTrueSwitchIsOn: true,
|
||||
colorFalseSwitchIsOn: false
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Switch
|
||||
activeThumbColor='#428BCA'
|
||||
activeTrackColor='#A0C4E3'
|
||||
onValueChange={(value) => this.setState({ colorFalseSwitchIsOn: value })}
|
||||
value={this.state.colorFalseSwitchIsOn}
|
||||
/>
|
||||
<Switch
|
||||
activeThumbColor='#5CB85C'
|
||||
activeTrackColor='#ADDAAD'
|
||||
onValueChange={(value) => this.setState({ colorTrueSwitchIsOn: value })}
|
||||
thumbColor='#EBA9A7'
|
||||
trackColor='#D9534F'
|
||||
value={this.state.colorTrueSwitchIsOn}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
34
docs/guides/internationalization.md
Normal file
34
docs/guides/internationalization.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Internationalization
|
||||
|
||||
To support right-to-left languages, application layout can be automatically
|
||||
flipped from LTR to RTL. The `I18nManager` API can be used to help with more
|
||||
fine-grained control and testing of RTL layouts.
|
||||
|
||||
React Native for Web provides an experimental feature to support "true left"
|
||||
and "true right" styles. For example, `left` will be flipped to `right` in RTL
|
||||
mode, but `left$noI18n` will not. More information is available in the `Text`
|
||||
and `View` documentation.
|
||||
|
||||
## Working with icons and images
|
||||
|
||||
Icons and images that must match the LTR or RTL layout of the app need to be manually flipped.
|
||||
|
||||
Either use a transform style:
|
||||
|
||||
```js
|
||||
<Image
|
||||
source={...}
|
||||
style={{ transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }] }}
|
||||
/>
|
||||
```
|
||||
|
||||
Or replace the source asset:
|
||||
|
||||
```js
|
||||
import imageSourceLTR from './back.png';
|
||||
import imageSourceRTL from './forward.png';
|
||||
|
||||
<Image
|
||||
source={I18nManager.isRTL ? imageSourceRTL : imageSourceLTR}
|
||||
/>
|
||||
```
|
||||
79
examples/apis/I18nManager/RTLExample.js
Normal file
79
examples/apis/I18nManager/RTLExample.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import { I18nManager, StyleSheet, TouchableHighlight, Text, View } from 'react-native'
|
||||
import React, { Component } from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
|
||||
class RTLExample extends Component {
|
||||
componentWillUnmount() {
|
||||
I18nManager.setPreferredLanguageRTL(false)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text accessibilityRole='heading' style={styles.welcome}>
|
||||
LTR/RTL layout example!
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
This is sample text. The writing direction can be changed by pressing the button below.
|
||||
</Text>
|
||||
<Text style={[ styles.text, styles.ltrText ]}>
|
||||
This is text that will always display LTR.
|
||||
</Text>
|
||||
<Text style={[ styles.text, styles.rtlText ]}>
|
||||
This is text that will always display RTL.
|
||||
</Text>
|
||||
<TouchableHighlight
|
||||
onPress={this._handleToggle}
|
||||
style={styles.toggle}
|
||||
underlayColor='rgba(0,0,0,0.25)'
|
||||
>
|
||||
<Text>Toggle LTR/RTL</Text>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
_handleToggle = () => {
|
||||
this._isRTL = !this._isRTL
|
||||
I18nManager.setPreferredLanguageRTL(this._isRTL)
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: '#F5FCFF',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
padding: 10
|
||||
},
|
||||
welcome: {
|
||||
fontSize: 28,
|
||||
marginVertical: 10
|
||||
},
|
||||
text: {
|
||||
color: '#333333',
|
||||
fontSize: 18,
|
||||
marginBottom: 5
|
||||
},
|
||||
ltrText: {
|
||||
textAlign$noI18n: 'left',
|
||||
writingDirection$noI18n: 'ltr'
|
||||
},
|
||||
rtlText: {
|
||||
textAlign$noI18n: 'right',
|
||||
writingDirection$noI18n: 'rtl'
|
||||
},
|
||||
toggle: {
|
||||
alignSelf: 'center',
|
||||
borderColor: 'black',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: 1,
|
||||
marginTop: 10,
|
||||
padding: 10
|
||||
}
|
||||
})
|
||||
|
||||
storiesOf('api: I18nManager', module)
|
||||
.add('RTL layout', () => (
|
||||
<RTLExample />
|
||||
))
|
||||
@@ -113,5 +113,5 @@ var styles = StyleSheet.create({
|
||||
});
|
||||
|
||||
|
||||
storiesOf('PanResponder', module)
|
||||
storiesOf('api: PanResponder', module)
|
||||
.add('example', () => <PanResponderExample />)
|
||||
@@ -59,31 +59,15 @@ const ToggleAnimatingActivityIndicator = React.createClass({
|
||||
|
||||
const examples = [
|
||||
{
|
||||
title: 'Default (small, white)',
|
||||
title: 'Default',
|
||||
render() {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
style={[styles.centering, styles.gray]}
|
||||
color="white"
|
||||
style={[styles.centering]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Gray',
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<ActivityIndicator
|
||||
style={[styles.centering]}
|
||||
/>
|
||||
<ActivityIndicator
|
||||
style={[styles.centering, {backgroundColor: '#eeeeee'}]}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Custom colors',
|
||||
render() {
|
||||
@@ -144,10 +128,13 @@ const examples = [
|
||||
title: 'Custom size',
|
||||
render() {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
style={[styles.centering, {transform: [{scale: 1.5}]}]}
|
||||
size="large"
|
||||
/>
|
||||
<View style={[styles.horizontal, styles.centering]}>
|
||||
<ActivityIndicator size="40" />
|
||||
<ActivityIndicator
|
||||
style={{ marginLeft: 20, transform: [ {scale: 1.5} ] }}
|
||||
size="large"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -170,6 +157,6 @@ const styles = StyleSheet.create({
|
||||
});
|
||||
|
||||
examples.forEach((example) => {
|
||||
storiesOf('<ActivityIndicator>', module)
|
||||
storiesOf('component: ActivityIndicator', module)
|
||||
.add(example.title, () => example.render())
|
||||
})
|
||||
@@ -651,7 +651,7 @@ var styles = StyleSheet.create({
|
||||
});
|
||||
|
||||
examples.forEach((example) => {
|
||||
storiesOf('<Image>', module)
|
||||
storiesOf('component: Image', module)
|
||||
.addDecorator((renderStory) => <View>{renderStory()}</View>)
|
||||
.add(example.title, () => example.render())
|
||||
})
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 850 B After Width: | Height: | Size: 850 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
96
examples/components/ProgressBar/ProgressBarExample.js
Normal file
96
examples/components/ProgressBar/ProgressBarExample.js
Normal file
@@ -0,0 +1,96 @@
|
||||
import { ProgressBar, StyleSheet, View } from 'react-native'
|
||||
import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import TimerMixin from 'react-timer-mixin';
|
||||
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
var ProgressBarExample = React.createClass({
|
||||
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}>
|
||||
<ProgressBar style={styles.progressView} color="purple" progress={this.getProgress(0.2)} />
|
||||
<ProgressBar style={styles.progressView} color="red" progress={this.getProgress(0.4)} />
|
||||
<ProgressBar style={styles.progressView} color="orange" progress={this.getProgress(0.6)} />
|
||||
<ProgressBar style={styles.progressView} color="yellow" progress={this.getProgress(0.8)} />
|
||||
</View>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const examples = [{
|
||||
title: 'progress',
|
||||
render() {
|
||||
return (
|
||||
<ProgressBarExample />
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'indeterminate',
|
||||
render() {
|
||||
return (
|
||||
<ProgressBar indeterminate style={styles.progressView} trackColor='#D1E3F6' />
|
||||
);
|
||||
}
|
||||
}];
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
minWidth: 200,
|
||||
marginTop: -20,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
progressView: {
|
||||
marginTop: 20,
|
||||
minWidth: 200
|
||||
}
|
||||
});
|
||||
|
||||
examples.forEach((example) => {
|
||||
storiesOf('component: ProgressBar', module)
|
||||
.add(example.title, () => example.render())
|
||||
})
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import { ScrollView, StyleSheet, Text, View } from 'react-native'
|
||||
|
||||
storiesOf('<ScrollView>', module)
|
||||
storiesOf('component: ScrollView', module)
|
||||
.add('vertical', () => (
|
||||
<View style={styles.scrollViewContainer}>
|
||||
<ScrollView
|
||||
@@ -52,6 +52,7 @@ const styles = StyleSheet.create({
|
||||
borderWidth: '1px'
|
||||
},
|
||||
scrollViewContentContainerStyle: {
|
||||
backgroundColor: '#eee',
|
||||
padding: '10px'
|
||||
}
|
||||
})
|
||||
190
examples/components/Switch/SwitchExample.js
Normal file
190
examples/components/Switch/SwitchExample.js
Normal file
@@ -0,0 +1,190 @@
|
||||
import { Platform, Switch, Text, View } from 'react-native'
|
||||
import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
var BasicSwitchExample = React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
trueSwitchIsOn: true,
|
||||
falseSwitchIsOn: false,
|
||||
};
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Switch
|
||||
onValueChange={(value) => this.setState({falseSwitchIsOn: value})}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.falseSwitchIsOn}
|
||||
/>
|
||||
<Switch
|
||||
onValueChange={(value) => this.setState({trueSwitchIsOn: value})}
|
||||
value={this.state.trueSwitchIsOn}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var DisabledSwitchExample = React.createClass({
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Switch
|
||||
disabled={true}
|
||||
style={{marginBottom: 10}}
|
||||
value={true} />
|
||||
<Switch
|
||||
disabled={true}
|
||||
value={false} />
|
||||
</View>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var ColorSwitchExample = React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
colorTrueSwitchIsOn: true,
|
||||
colorFalseSwitchIsOn: false,
|
||||
};
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Switch
|
||||
activeThumbColor="#428bca"
|
||||
activeTrackColor="#A0C4E3"
|
||||
onValueChange={(value) => this.setState({colorFalseSwitchIsOn: value})}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.colorFalseSwitchIsOn}
|
||||
/>
|
||||
<Switch
|
||||
activeThumbColor="#5CB85C"
|
||||
activeTrackColor="#ADDAAD"
|
||||
onValueChange={(value) => this.setState({colorTrueSwitchIsOn: value})}
|
||||
thumbColor="#EBA9A7"
|
||||
trackColor="#D9534F"
|
||||
value={this.state.colorTrueSwitchIsOn}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var EventSwitchExample = React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
eventSwitchIsOn: false,
|
||||
eventSwitchRegressionIsOn: true,
|
||||
};
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-around' }}>
|
||||
<View>
|
||||
<Switch
|
||||
onValueChange={(value) => this.setState({eventSwitchIsOn: value})}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.eventSwitchIsOn} />
|
||||
<Switch
|
||||
onValueChange={(value) => this.setState({eventSwitchIsOn: value})}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.eventSwitchIsOn} />
|
||||
<Text>{this.state.eventSwitchIsOn ? 'On' : 'Off'}</Text>
|
||||
</View>
|
||||
<View>
|
||||
<Switch
|
||||
onValueChange={(value) => this.setState({eventSwitchRegressionIsOn: value})}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.eventSwitchRegressionIsOn} />
|
||||
<Switch
|
||||
onValueChange={(value) => this.setState({eventSwitchRegressionIsOn: value})}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.eventSwitchRegressionIsOn} />
|
||||
<Text>{this.state.eventSwitchRegressionIsOn ? 'On' : 'Off'}</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var SizeSwitchExample = React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
trueSwitchIsOn: true,
|
||||
falseSwitchIsOn: false,
|
||||
};
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Switch
|
||||
onValueChange={(value) => this.setState({falseSwitchIsOn: value})}
|
||||
style={{marginBottom: 10, height: '3rem' }}
|
||||
value={this.state.falseSwitchIsOn}
|
||||
/>
|
||||
<Switch
|
||||
onValueChange={(value) => this.setState({trueSwitchIsOn: value})}
|
||||
style={{marginBottom: 10, width: 150 }}
|
||||
value={this.state.trueSwitchIsOn}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var examples = [
|
||||
{
|
||||
title: 'set to true or false',
|
||||
render(): ReactElement<any> { return <BasicSwitchExample />; }
|
||||
},
|
||||
{
|
||||
title: 'disabled',
|
||||
render(): ReactElement<any> { return <DisabledSwitchExample />; }
|
||||
},
|
||||
{
|
||||
title: 'change events',
|
||||
render(): ReactElement<any> { return <EventSwitchExample />; }
|
||||
},
|
||||
{
|
||||
title: 'custom colors',
|
||||
render(): ReactElement<any> { return <ColorSwitchExample />; }
|
||||
},
|
||||
{
|
||||
title: 'custom size',
|
||||
render(): ReactElement<any> { return <SizeSwitchExample />; }
|
||||
},
|
||||
{
|
||||
title: 'controlled component',
|
||||
render(): ReactElement<any> { return <Switch />; }
|
||||
}
|
||||
];
|
||||
|
||||
examples.forEach((example) => {
|
||||
storiesOf('component: Switch', module)
|
||||
.add(example.title, () => example.render())
|
||||
})
|
||||
@@ -466,7 +466,7 @@ var styles = StyleSheet.create({
|
||||
});
|
||||
|
||||
examples.forEach((example) => {
|
||||
storiesOf('<Text>', module)
|
||||
storiesOf('component: Text', module)
|
||||
.addDecorator((renderStory) => <View style={{ width: 320 }}>{renderStory()}</View>)
|
||||
.add(example.title, () => example.render())
|
||||
})
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import { StyleSheet, TextInput, View } from 'react-native'
|
||||
|
||||
storiesOf('<TextInput>', module)
|
||||
storiesOf('component: TextInput', module)
|
||||
.add('tbd', () => (
|
||||
<View>
|
||||
<TextInput
|
||||
@@ -14,6 +14,7 @@ storiesOf('<TextInput>', module)
|
||||
onFocus={(e) => { console.log('TextInput.onFocus', e) }}
|
||||
onSelectionChange={(e) => { console.log('TextInput.onSelectionChange', e) }}
|
||||
/>
|
||||
<TextInput keyboardType='search' style={styles.textInput} />
|
||||
<TextInput secureTextEntry style={styles.textInput} />
|
||||
<TextInput defaultValue='read only' editable={false} style={styles.textInput} />
|
||||
<TextInput
|
||||
@@ -445,6 +445,6 @@ var styles = StyleSheet.create({
|
||||
});
|
||||
|
||||
examples.forEach((example) => {
|
||||
storiesOf('<Touchable*>', module)
|
||||
storiesOf('component: Touchable*', module)
|
||||
.add(example.title, () => example.render())
|
||||
})
|
||||
@@ -245,6 +245,6 @@ const examples = [
|
||||
];
|
||||
|
||||
examples.forEach((example) => {
|
||||
storiesOf('<View>', module)
|
||||
storiesOf('component: View', module)
|
||||
.add(example.title, () => example.render())
|
||||
})
|
||||
@@ -281,6 +281,6 @@ const examples = [
|
||||
];
|
||||
|
||||
examples.forEach((example) => {
|
||||
storiesOf('<View> transforms', module)
|
||||
storiesOf('component: View (transforms)', module)
|
||||
.add(example.title, () => example.render())
|
||||
})
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import Game2048 from './Game2048'
|
||||
|
||||
storiesOf('Game2048', module)
|
||||
storiesOf('demo: Game2048', module)
|
||||
.add('the game', () => (
|
||||
<Game2048 />
|
||||
))
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import TicTacToe from './TicTacToe'
|
||||
|
||||
storiesOf('TicTacToe', module)
|
||||
storiesOf('demo: TicTacToe', module)
|
||||
.add('the game', () => (
|
||||
<TicTacToe />
|
||||
))
|
||||
56
package.json
56
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native-web",
|
||||
"version": "0.0.43",
|
||||
"version": "0.0.45",
|
||||
"description": "React Native for Web",
|
||||
"main": "dist/index.js",
|
||||
"files": [
|
||||
@@ -11,7 +11,7 @@
|
||||
"build:examples": "build-storybook -o dist-examples -c ./examples/.storybook",
|
||||
"build:umd": "webpack --config webpack.config.js --sort-assets-by --progress",
|
||||
"deploy:examples": "git checkout gh-pages && rm -rf ./storybook && mv dist-examples storybook && git add -A && git commit -m \"Storybook deploy\" && git push origin gh-pages && git checkout -",
|
||||
"examples": "start-storybook -p 9001 -c ./examples/.storybook",
|
||||
"examples": "start-storybook -p 9001 -c ./examples/.storybook --dont-track",
|
||||
"lint": "eslint src",
|
||||
"prepublish": "npm run build && npm run build:umd",
|
||||
"test": "karma start karma.config.js",
|
||||
@@ -19,47 +19,45 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"animated": "^0.1.3",
|
||||
"babel-runtime": "^6.9.2",
|
||||
"fbjs": "^0.8.1",
|
||||
"inline-style-prefixer": "^2.0.0",
|
||||
"lodash": "^4.13.1",
|
||||
"react-dom": "^15.1.0",
|
||||
"react-textarea-autosize": "^4.0.2",
|
||||
"babel-runtime": "^6.11.6",
|
||||
"fbjs": "^0.8.4",
|
||||
"inline-style-prefixer": "^2.0.1",
|
||||
"lodash": "^4.15.0",
|
||||
"react-dom": "^15.3.1",
|
||||
"react-textarea-autosize": "^4.0.4",
|
||||
"react-timer-mixin": "^0.13.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kadira/storybook": "^1.38.0",
|
||||
"babel-cli": "^6.10.1",
|
||||
"babel-core": "^6.10.4",
|
||||
"babel-eslint": "^6.1.0",
|
||||
"babel-loader": "^6.2.4",
|
||||
"@kadira/storybook": "^2.5.1",
|
||||
"babel-cli": "^6.14.0",
|
||||
"babel-core": "^6.14.0",
|
||||
"babel-eslint": "^6.1.2",
|
||||
"babel-loader": "^6.2.5",
|
||||
"babel-preset-react-native": "^1.9.0",
|
||||
"del-cli": "^0.2.0",
|
||||
"enzyme": "^2.3.0",
|
||||
"eslint": "^2.12.0",
|
||||
"eslint-config-standard": "^5.3.1",
|
||||
"eslint-config-standard-react": "^2.4.0",
|
||||
"eslint-plugin-promise": "^1.3.2",
|
||||
"eslint-plugin-react": "^5.1.1",
|
||||
"eslint-plugin-standard": "^1.3.2",
|
||||
"enzyme": "^2.4.1",
|
||||
"eslint": "^3.4.0",
|
||||
"eslint-plugin-jsx-a11y": "^2.2.0",
|
||||
"eslint-plugin-promise": "^2.0.1",
|
||||
"eslint-plugin-react": "^6.1.2",
|
||||
"file-loader": "^0.9.0",
|
||||
"karma": "^0.13.22",
|
||||
"karma": "^1.2.0",
|
||||
"karma-browserstack-launcher": "^1.0.1",
|
||||
"karma-chrome-launcher": "^1.0.1",
|
||||
"karma-chrome-launcher": "^2.0.0",
|
||||
"karma-firefox-launcher": "^1.0.0",
|
||||
"karma-mocha": "^1.1.1",
|
||||
"karma-mocha-reporter": "^2.0.4",
|
||||
"karma-mocha-reporter": "^2.1.0",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-webpack": "^1.7.0",
|
||||
"mocha": "^2.5.3",
|
||||
"karma-webpack": "^1.8.0",
|
||||
"mocha": "^3.0.2",
|
||||
"node-libs-browser": "^0.5.3",
|
||||
"react": "^15.2.0",
|
||||
"react-addons-test-utils": "^15.2.0",
|
||||
"react": "^15.3.1",
|
||||
"react-addons-test-utils": "^15.3.1",
|
||||
"url-loader": "^0.5.7",
|
||||
"webpack": "^1.13.1"
|
||||
"webpack": "^1.13.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^15.1.0"
|
||||
"react": "^15.3.1"
|
||||
},
|
||||
"author": "Nicolas Gallagher",
|
||||
"license": "BSD-3-Clause",
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import Animated from 'animated'
|
||||
import StyleSheet from '../StyleSheet'
|
||||
import Image from '../../components/Image'
|
||||
import Text from '../../components/Text'
|
||||
import View from '../../components/View'
|
||||
import Animated from 'animated';
|
||||
import Image from '../../components/Image';
|
||||
import StyleSheet from '../StyleSheet';
|
||||
import Text from '../../components/Text';
|
||||
import View from '../../components/View';
|
||||
|
||||
Animated.inject.FlattenStyle(StyleSheet.flatten)
|
||||
Animated.inject.FlattenStyle(StyleSheet.flatten);
|
||||
|
||||
module.exports = {
|
||||
...Animated,
|
||||
Image: Animated.createAnimatedComponent(Image),
|
||||
Text: Animated.createAnimatedComponent(Text),
|
||||
View: Animated.createAnimatedComponent(View)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import StyleSheet from '../StyleSheet'
|
||||
import View from '../../components/View'
|
||||
import StyleSheet from '../StyleSheet';
|
||||
import View from '../../components/View';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
class ReactNativeApp extends Component {
|
||||
static propTypes = {
|
||||
@@ -10,13 +10,13 @@ class ReactNativeApp extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { initialProps, rootComponent: RootComponent, rootTag } = this.props
|
||||
const { initialProps, rootComponent: RootComponent, rootTag } = this.props;
|
||||
|
||||
return (
|
||||
<View style={styles.appContainer}>
|
||||
<RootComponent {...initialProps} rootTag={rootTag} />
|
||||
</View>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,6 @@ const styles = StyleSheet.create({
|
||||
right: 0,
|
||||
bottom: 0
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
module.exports = ReactNativeApp
|
||||
module.exports = ReactNativeApp;
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
import assert from 'assert'
|
||||
import { prerenderApplication } from '../renderApplication'
|
||||
import React from 'react'
|
||||
import assert from 'assert';
|
||||
import { prerenderApplication } from '../renderApplication';
|
||||
import React from 'react';
|
||||
|
||||
const component = () => <div />
|
||||
const component = () => <div />;
|
||||
|
||||
suite('apis/AppRegistry/renderApplication', () => {
|
||||
test('prerenderApplication', () => {
|
||||
const { html, styleElement } = prerenderApplication(component, {})
|
||||
const { html, styleElement } = prerenderApplication(component, {});
|
||||
|
||||
assert.ok(html.indexOf('<div ') > -1)
|
||||
assert.equal(styleElement.type, 'style')
|
||||
})
|
||||
})
|
||||
assert.ok(html.indexOf('<div ') > -1);
|
||||
assert.equal(styleElement.type, 'style');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import { Component } from 'react'
|
||||
import invariant from 'fbjs/lib/invariant'
|
||||
import ReactDOM from 'react-dom'
|
||||
import renderApplication, { prerenderApplication } from './renderApplication'
|
||||
import { Component } from 'react';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import ReactDOM from 'react-dom';
|
||||
import renderApplication, { prerenderApplication } from './renderApplication';
|
||||
|
||||
const runnables = {}
|
||||
const runnables = {};
|
||||
|
||||
type ComponentProvider = () => Component<any, any, any>
|
||||
|
||||
@@ -26,7 +26,7 @@ type AppConfig = {
|
||||
*/
|
||||
class AppRegistry {
|
||||
static getAppKeys(): Array<string> {
|
||||
return Object.keys(runnables)
|
||||
return Object.keys(runnables);
|
||||
}
|
||||
|
||||
static prerenderApplication(appKey: string, appParameters?: Object): string {
|
||||
@@ -34,59 +34,59 @@ class AppRegistry {
|
||||
runnables[appKey] && runnables[appKey].prerender,
|
||||
`Application ${appKey} has not been registered. ` +
|
||||
'This is either due to an import error during initialization or failure to call AppRegistry.registerComponent.'
|
||||
)
|
||||
);
|
||||
|
||||
return runnables[appKey].prerender(appParameters)
|
||||
return runnables[appKey].prerender(appParameters);
|
||||
}
|
||||
|
||||
static registerComponent(appKey: string, getComponentFunc: ComponentProvider): string {
|
||||
runnables[appKey] = {
|
||||
run: ({ initialProps, rootTag }) => renderApplication(getComponentFunc(), initialProps, rootTag),
|
||||
prerender: ({ initialProps } = {}) => prerenderApplication(getComponentFunc(), initialProps)
|
||||
}
|
||||
return appKey
|
||||
};
|
||||
return appKey;
|
||||
}
|
||||
|
||||
static registerConfig(config: Array<AppConfig>) {
|
||||
config.forEach(({ appKey, component, run }) => {
|
||||
if (run) {
|
||||
AppRegistry.registerRunnable(appKey, run)
|
||||
AppRegistry.registerRunnable(appKey, run);
|
||||
} else {
|
||||
invariant(component, 'No component provider passed in')
|
||||
AppRegistry.registerComponent(appKey, component)
|
||||
invariant(component, 'No component provider passed in');
|
||||
AppRegistry.registerComponent(appKey, component);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: fix style sheet creation when using this method
|
||||
static registerRunnable(appKey: string, run: Function): string {
|
||||
runnables[appKey] = { run }
|
||||
return appKey
|
||||
runnables[appKey] = { run };
|
||||
return appKey;
|
||||
}
|
||||
|
||||
static runApplication(appKey: string, appParameters?: Object): void {
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||
const params = { ...appParameters }
|
||||
params.rootTag = `#${params.rootTag.id}`
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||
const params = { ...appParameters };
|
||||
params.rootTag = `#${params.rootTag.id}`;
|
||||
|
||||
console.log(
|
||||
`Running application "${appKey}" with appParams: ${JSON.stringify(params)}. ` +
|
||||
`development-level warnings are ${isDevelopment ? 'ON' : 'OFF'}, ` +
|
||||
`performance optimizations are ${isDevelopment ? 'OFF' : 'ON'}`
|
||||
)
|
||||
);
|
||||
|
||||
invariant(
|
||||
runnables[appKey] && runnables[appKey].run,
|
||||
`Application "${appKey}" has not been registered. ` +
|
||||
'This is either due to an import error during initialization or failure to call AppRegistry.registerComponent.'
|
||||
)
|
||||
);
|
||||
|
||||
runnables[appKey].run(appParameters)
|
||||
runnables[appKey].run(appParameters);
|
||||
}
|
||||
|
||||
static unmountApplicationComponentAtRootTag(rootTag) {
|
||||
ReactDOM.unmountComponentAtNode(rootTag)
|
||||
ReactDOM.unmountComponentAtNode(rootTag);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AppRegistry
|
||||
module.exports = AppRegistry;
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import invariant from 'fbjs/lib/invariant'
|
||||
import React, { Component } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import ReactDOMServer from 'react-dom/server'
|
||||
import ReactNativeApp from './ReactNativeApp'
|
||||
import StyleSheet from '../../apis/StyleSheet'
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ReactDOMServer from 'react-dom/server';
|
||||
import ReactNativeApp from './ReactNativeApp';
|
||||
import StyleSheet from '../../apis/StyleSheet';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
export default function renderApplication(RootComponent: Component, initialProps: Object, rootTag: any) {
|
||||
invariant(rootTag, 'Expect to have a valid rootTag, instead got ', rootTag)
|
||||
invariant(rootTag, 'Expect to have a valid rootTag, instead got ', rootTag);
|
||||
|
||||
const component = (
|
||||
<ReactNativeApp
|
||||
@@ -22,8 +22,8 @@ export default function renderApplication(RootComponent: Component, initialProps
|
||||
rootComponent={RootComponent}
|
||||
rootTag={rootTag}
|
||||
/>
|
||||
)
|
||||
ReactDOM.render(component, rootTag)
|
||||
);
|
||||
ReactDOM.render(component, rootTag);
|
||||
}
|
||||
|
||||
export function prerenderApplication(RootComponent: Component, initialProps: Object): string {
|
||||
@@ -32,8 +32,8 @@ export function prerenderApplication(RootComponent: Component, initialProps: Obj
|
||||
initialProps={initialProps}
|
||||
rootComponent={RootComponent}
|
||||
/>
|
||||
)
|
||||
const html = ReactDOMServer.renderToString(component)
|
||||
const styleElement = StyleSheet.render()
|
||||
return { html, styleElement }
|
||||
);
|
||||
const html = ReactDOMServer.renderToString(component);
|
||||
const styleElement = StyleSheet.render();
|
||||
return { html, styleElement };
|
||||
}
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
import AppState from '..'
|
||||
import assert from 'assert'
|
||||
import AppState from '..';
|
||||
import assert from 'assert';
|
||||
|
||||
suite('apis/AppState', () => {
|
||||
const handler = () => {}
|
||||
const handler = () => {};
|
||||
|
||||
teardown(() => {
|
||||
try { AppState.removeEventListener('change', handler) } catch (e) {}
|
||||
})
|
||||
try { AppState.removeEventListener('change', handler); } catch (e) {}
|
||||
});
|
||||
|
||||
suite('addEventListener', () => {
|
||||
test('throws if the provided "eventType" is not supported', () => {
|
||||
assert.throws(() => AppState.addEventListener('foo', handler))
|
||||
assert.doesNotThrow(() => AppState.addEventListener('change', handler))
|
||||
})
|
||||
})
|
||||
assert.throws(() => AppState.addEventListener('foo', handler));
|
||||
assert.doesNotThrow(() => AppState.addEventListener('change', handler));
|
||||
});
|
||||
});
|
||||
|
||||
suite('removeEventListener', () => {
|
||||
test('throws if the handler is not registered', () => {
|
||||
assert.throws(() => AppState.removeEventListener('change', handler))
|
||||
})
|
||||
assert.throws(() => AppState.removeEventListener('change', handler));
|
||||
});
|
||||
|
||||
test('throws if the provided "eventType" is not supported', () => {
|
||||
AppState.addEventListener('change', handler)
|
||||
assert.throws(() => AppState.removeEventListener('foo', handler))
|
||||
assert.doesNotThrow(() => AppState.removeEventListener('change', handler))
|
||||
})
|
||||
})
|
||||
})
|
||||
AppState.addEventListener('change', handler);
|
||||
assert.throws(() => AppState.removeEventListener('foo', handler));
|
||||
assert.doesNotThrow(() => AppState.removeEventListener('change', handler));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment'
|
||||
import findIndex from 'lodash/findIndex'
|
||||
import invariant from 'fbjs/lib/invariant'
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
|
||||
import findIndex from 'lodash/findIndex';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
const EVENT_TYPES = [ 'change' ]
|
||||
const VISIBILITY_CHANGE_EVENT = 'visibilitychange'
|
||||
const EVENT_TYPES = [ 'change' ];
|
||||
const VISIBILITY_CHANGE_EVENT = 'visibilitychange';
|
||||
|
||||
const AppStates = {
|
||||
BACKGROUND: 'background',
|
||||
ACTIVE: 'active'
|
||||
}
|
||||
};
|
||||
|
||||
const listeners = []
|
||||
const listeners = [];
|
||||
|
||||
class AppState {
|
||||
static isSupported = ExecutionEnvironment.canUseDOM && document.visibilityState
|
||||
|
||||
static get currentState() {
|
||||
if (!AppState.isSupported) {
|
||||
return AppState.ACTIVE
|
||||
return AppState.ACTIVE;
|
||||
}
|
||||
|
||||
switch (document.visibilityState) {
|
||||
case 'hidden':
|
||||
case 'prerender':
|
||||
case 'unloaded':
|
||||
return AppStates.BACKGROUND
|
||||
return AppStates.BACKGROUND;
|
||||
default:
|
||||
return AppStates.ACTIVE
|
||||
return AppStates.ACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
static addEventListener(type: string, handler: Function) {
|
||||
if (AppState.isSupported) {
|
||||
invariant(EVENT_TYPES.indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type)
|
||||
const callback = () => handler(AppState.currentState)
|
||||
listeners.push([ handler, callback ])
|
||||
document.addEventListener(VISIBILITY_CHANGE_EVENT, callback, false)
|
||||
invariant(EVENT_TYPES.indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type);
|
||||
const callback = () => handler(AppState.currentState);
|
||||
listeners.push([ handler, callback ]);
|
||||
document.addEventListener(VISIBILITY_CHANGE_EVENT, callback, false);
|
||||
}
|
||||
}
|
||||
|
||||
static removeEventListener(type: string, handler: Function) {
|
||||
if (AppState.isSupported) {
|
||||
invariant(EVENT_TYPES.indexOf(type) !== -1, 'Trying to remove listener for unknown event: "%s"', type)
|
||||
const listenerIndex = findIndex(listeners, (pair) => pair[0] === handler)
|
||||
invariant(listenerIndex !== -1, 'Trying to remove AppState listener for unregistered handler')
|
||||
const callback = listeners[listenerIndex][1]
|
||||
document.removeEventListener(VISIBILITY_CHANGE_EVENT, callback, false)
|
||||
listeners.splice(listenerIndex, 1)
|
||||
invariant(EVENT_TYPES.indexOf(type) !== -1, 'Trying to remove listener for unknown event: "%s"', type);
|
||||
const listenerIndex = findIndex(listeners, (pair) => pair[0] === handler);
|
||||
invariant(listenerIndex !== -1, 'Trying to remove AppState listener for unregistered handler');
|
||||
const callback = listeners[listenerIndex][1];
|
||||
document.removeEventListener(VISIBILITY_CHANGE_EVENT, callback, false);
|
||||
listeners.splice(listenerIndex, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AppState
|
||||
module.exports = AppState;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
suite('apis/AsyncStorage', () => {
|
||||
test.skip('NO TEST COVERAGE', () => {})
|
||||
})
|
||||
test.skip('NO TEST COVERAGE', () => {});
|
||||
});
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
*/
|
||||
|
||||
const mergeLocalStorageItem = (key, value) => {
|
||||
const oldValue = window.localStorage.getItem(key)
|
||||
const oldObject = JSON.parse(oldValue)
|
||||
const newObject = JSON.parse(value)
|
||||
const nextValue = JSON.stringify({ ...oldObject, ...newObject })
|
||||
window.localStorage.setItem(key, nextValue)
|
||||
}
|
||||
const oldValue = window.localStorage.getItem(key);
|
||||
const oldObject = JSON.parse(oldValue);
|
||||
const newObject = JSON.parse(value);
|
||||
const nextValue = JSON.stringify({ ...oldObject, ...newObject });
|
||||
window.localStorage.setItem(key, nextValue);
|
||||
};
|
||||
|
||||
class AsyncStorage {
|
||||
/**
|
||||
@@ -19,12 +19,12 @@ class AsyncStorage {
|
||||
static clear() {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
window.localStorage.clear()
|
||||
resolve(null)
|
||||
window.localStorage.clear();
|
||||
resolve(null);
|
||||
} catch (err) {
|
||||
reject(err)
|
||||
reject(err);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,17 +33,17 @@ class AsyncStorage {
|
||||
static getAllKeys() {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const numberOfKeys = window.localStorage.length
|
||||
const keys = []
|
||||
const numberOfKeys = window.localStorage.length;
|
||||
const keys = [];
|
||||
for (let i = 0; i < numberOfKeys; i += 1) {
|
||||
const key = window.localStorage.key(i)
|
||||
keys.push(key)
|
||||
const key = window.localStorage.key(i);
|
||||
keys.push(key);
|
||||
}
|
||||
resolve(keys)
|
||||
resolve(keys);
|
||||
} catch (err) {
|
||||
reject(err)
|
||||
reject(err);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,12 +52,12 @@ class AsyncStorage {
|
||||
static getItem(key: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const value = window.localStorage.getItem(key)
|
||||
resolve(value)
|
||||
const value = window.localStorage.getItem(key);
|
||||
resolve(value);
|
||||
} catch (err) {
|
||||
reject(err)
|
||||
reject(err);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,12 +66,12 @@ class AsyncStorage {
|
||||
static mergeItem(key: string, value: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
mergeLocalStorageItem(key, value)
|
||||
resolve(null)
|
||||
mergeLocalStorageItem(key, value);
|
||||
resolve(null);
|
||||
} catch (err) {
|
||||
reject(err)
|
||||
reject(err);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,12 +81,12 @@ class AsyncStorage {
|
||||
* multiGet(['k1', 'k2']) -> [['k1', 'val1'], ['k2', 'val2']]
|
||||
*/
|
||||
static multiGet(keys: Array<string>) {
|
||||
const promises = keys.map((key) => AsyncStorage.getItem(key))
|
||||
const promises = keys.map((key) => AsyncStorage.getItem(key));
|
||||
|
||||
return Promise.all(promises).then(
|
||||
(result) => Promise.resolve(result.map((value, i) => [ keys[i], value ])),
|
||||
(error) => Promise.reject(error)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,24 +96,24 @@ class AsyncStorage {
|
||||
* multiMerge([['k1', 'val1'], ['k2', 'val2']])
|
||||
*/
|
||||
static multiMerge(keyValuePairs: Array<Array<string>>) {
|
||||
const promises = keyValuePairs.map((item) => AsyncStorage.mergeItem(item[0], item[1]))
|
||||
const promises = keyValuePairs.map((item) => AsyncStorage.mergeItem(item[0], item[1]));
|
||||
|
||||
return Promise.all(promises).then(
|
||||
() => Promise.resolve(null),
|
||||
(error) => Promise.reject(error)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all the keys in the `keys` array.
|
||||
*/
|
||||
static multiRemove(keys: Array<string>) {
|
||||
const promises = keys.map((key) => AsyncStorage.removeItem(key))
|
||||
const promises = keys.map((key) => AsyncStorage.removeItem(key));
|
||||
|
||||
return Promise.all(promises).then(
|
||||
() => Promise.resolve(null),
|
||||
(error) => Promise.reject(error)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,12 +121,12 @@ class AsyncStorage {
|
||||
* multiSet([['k1', 'val1'], ['k2', 'val2']])
|
||||
*/
|
||||
static multiSet(keyValuePairs: Array<Array<string>>) {
|
||||
const promises = keyValuePairs.map((item) => AsyncStorage.setItem(item[0], item[1]))
|
||||
const promises = keyValuePairs.map((item) => AsyncStorage.setItem(item[0], item[1]));
|
||||
|
||||
return Promise.all(promises).then(
|
||||
() => Promise.resolve(null),
|
||||
(error) => Promise.reject(error)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,12 +135,12 @@ class AsyncStorage {
|
||||
static removeItem(key: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
window.localStorage.removeItem(key)
|
||||
resolve(null)
|
||||
window.localStorage.removeItem(key);
|
||||
resolve(null);
|
||||
} catch (err) {
|
||||
reject(err)
|
||||
reject(err);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,13 +149,13 @@ class AsyncStorage {
|
||||
static setItem(key: string, value: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
window.localStorage.setItem(key, value)
|
||||
resolve(null)
|
||||
window.localStorage.setItem(key, value);
|
||||
resolve(null);
|
||||
} catch (err) {
|
||||
reject(err)
|
||||
reject(err);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AsyncStorage
|
||||
module.exports = AsyncStorage;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
suite('apis/Dimensions', () => {
|
||||
test.skip('NO TEST COVERAGE', () => {})
|
||||
})
|
||||
test.skip('NO TEST COVERAGE', () => {});
|
||||
});
|
||||
|
||||
@@ -6,18 +6,18 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import debounce from 'lodash/debounce'
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment'
|
||||
import invariant from 'fbjs/lib/invariant'
|
||||
import debounce from 'lodash/debounce';
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
const win = ExecutionEnvironment.canUseDOM ? window : { screen: {} }
|
||||
const win = ExecutionEnvironment.canUseDOM ? window : { screen: {} };
|
||||
|
||||
const dimensions = {}
|
||||
const dimensions = {};
|
||||
|
||||
class Dimensions {
|
||||
static get(dimension: string): Object {
|
||||
invariant(dimensions[dimension], 'No dimension set for key ' + dimension)
|
||||
return dimensions[dimension]
|
||||
invariant(dimensions[dimension], `No dimension set for key ${dimension}`);
|
||||
return dimensions[dimension];
|
||||
}
|
||||
|
||||
static set(): void {
|
||||
@@ -26,18 +26,18 @@ class Dimensions {
|
||||
height: win.innerHeight,
|
||||
scale: win.devicePixelRatio || 1,
|
||||
width: win.innerWidth
|
||||
}
|
||||
};
|
||||
|
||||
dimensions.screen = {
|
||||
fontScale: 1,
|
||||
height: win.screen.height,
|
||||
scale: win.devicePixelRatio || 1,
|
||||
width: win.screen.width
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Dimensions.set()
|
||||
ExecutionEnvironment.canUseDOM && window.addEventListener('resize', debounce(Dimensions.set, 50))
|
||||
Dimensions.set();
|
||||
ExecutionEnvironment.canUseDOM && window.addEventListener('resize', debounce(Dimensions.set, 50));
|
||||
|
||||
module.exports = Dimensions
|
||||
module.exports = Dimensions;
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
import assert from 'assert'
|
||||
import I18nManager from '..'
|
||||
import assert from 'assert';
|
||||
import I18nManager from '..';
|
||||
|
||||
suite('apis/I18nManager', () => {
|
||||
suite('when RTL not enabled', () => {
|
||||
setup(() => {
|
||||
I18nManager.setPreferredLanguageRTL(false)
|
||||
})
|
||||
I18nManager.setPreferredLanguageRTL(false);
|
||||
});
|
||||
|
||||
test('is "false" by default', () => {
|
||||
assert.equal(I18nManager.isRTL, false)
|
||||
assert.equal(document.documentElement.getAttribute('dir'), 'ltr')
|
||||
})
|
||||
assert.equal(I18nManager.isRTL, false);
|
||||
assert.equal(document.documentElement.getAttribute('dir'), 'ltr');
|
||||
});
|
||||
|
||||
test('is "true" when forced', () => {
|
||||
I18nManager.forceRTL(true)
|
||||
assert.equal(I18nManager.isRTL, true)
|
||||
assert.equal(document.documentElement.getAttribute('dir'), 'rtl')
|
||||
I18nManager.forceRTL(false)
|
||||
})
|
||||
})
|
||||
I18nManager.forceRTL(true);
|
||||
assert.equal(I18nManager.isRTL, true);
|
||||
assert.equal(document.documentElement.getAttribute('dir'), 'rtl');
|
||||
I18nManager.forceRTL(false);
|
||||
});
|
||||
});
|
||||
|
||||
suite('when RTL is enabled', () => {
|
||||
setup(() => {
|
||||
I18nManager.setPreferredLanguageRTL(true)
|
||||
})
|
||||
I18nManager.setPreferredLanguageRTL(true);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
I18nManager.setPreferredLanguageRTL(false)
|
||||
})
|
||||
I18nManager.setPreferredLanguageRTL(false);
|
||||
});
|
||||
|
||||
test('is "true" by default', () => {
|
||||
assert.equal(I18nManager.isRTL, true)
|
||||
assert.equal(document.documentElement.getAttribute('dir'), 'rtl')
|
||||
})
|
||||
assert.equal(I18nManager.isRTL, true);
|
||||
assert.equal(document.documentElement.getAttribute('dir'), 'rtl');
|
||||
});
|
||||
|
||||
test('is "false" when not allowed', () => {
|
||||
I18nManager.allowRTL(false)
|
||||
assert.equal(I18nManager.isRTL, false)
|
||||
assert.equal(document.documentElement.getAttribute('dir'), 'ltr')
|
||||
I18nManager.allowRTL(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
I18nManager.allowRTL(false);
|
||||
assert.equal(I18nManager.isRTL, false);
|
||||
assert.equal(document.documentElement.getAttribute('dir'), 'ltr');
|
||||
I18nManager.allowRTL(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment'
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
|
||||
|
||||
type I18nManagerStatus = {
|
||||
allowRTL: (allowRTL: boolean) => {},
|
||||
@@ -7,39 +7,39 @@ type I18nManagerStatus = {
|
||||
isRTL: boolean
|
||||
}
|
||||
|
||||
let isPreferredLanguageRTL = false
|
||||
let isRTLAllowed = true
|
||||
let isRTLForced = false
|
||||
let isPreferredLanguageRTL = false;
|
||||
let isRTLAllowed = true;
|
||||
let isRTLForced = false;
|
||||
|
||||
const isRTL = () => {
|
||||
if (isRTLForced) {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
return isRTLAllowed && isPreferredLanguageRTL
|
||||
}
|
||||
return isRTLAllowed && isPreferredLanguageRTL;
|
||||
};
|
||||
|
||||
const onChange = () => {
|
||||
if (ExecutionEnvironment.canUseDOM) {
|
||||
document.documentElement.setAttribute('dir', isRTL() ? 'rtl' : 'ltr')
|
||||
document.documentElement.setAttribute('dir', isRTL() ? 'rtl' : 'ltr');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const I18nManager: I18nManagerStatus = {
|
||||
allowRTL(bool) {
|
||||
isRTLAllowed = bool
|
||||
onChange()
|
||||
isRTLAllowed = bool;
|
||||
onChange();
|
||||
},
|
||||
forceRTL(bool) {
|
||||
isRTLForced = bool
|
||||
onChange()
|
||||
isRTLForced = bool;
|
||||
onChange();
|
||||
},
|
||||
setPreferredLanguageRTL(bool) {
|
||||
isPreferredLanguageRTL = bool
|
||||
onChange()
|
||||
isPreferredLanguageRTL = bool;
|
||||
onChange();
|
||||
},
|
||||
get isRTL() {
|
||||
return isRTL()
|
||||
return isRTL();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = I18nManager
|
||||
module.exports = I18nManager;
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import keyMirror from 'fbjs/lib/keyMirror'
|
||||
import invariant from 'fbjs/lib/invariant'
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import keyMirror from 'fbjs/lib/keyMirror';
|
||||
|
||||
const InteractionManager = {
|
||||
Events: keyMirror({
|
||||
@@ -21,15 +21,15 @@ const InteractionManager = {
|
||||
invariant(
|
||||
typeof callback === 'function',
|
||||
'Must specify a function to schedule.'
|
||||
)
|
||||
callback()
|
||||
);
|
||||
callback();
|
||||
},
|
||||
|
||||
/**
|
||||
* Notify manager that an interaction has started.
|
||||
*/
|
||||
createInteractionHandle() {
|
||||
return 1
|
||||
return 1;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -39,10 +39,10 @@ const InteractionManager = {
|
||||
invariant(
|
||||
!!handle,
|
||||
'Must provide a handle to clear.'
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
addListener: () => {}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = InteractionManager
|
||||
module.exports = InteractionManager;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
suite('apis/NetInfo', () => {
|
||||
test.skip('NO TEST COVERAGE', () => {})
|
||||
})
|
||||
test.skip('NO TEST COVERAGE', () => {});
|
||||
});
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment'
|
||||
import invariant from 'fbjs/lib/invariant'
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
const connection = ExecutionEnvironment.canUseDOM && (
|
||||
window.navigator.connection ||
|
||||
window.navigator.mozConnection ||
|
||||
window.navigator.webkitConnection
|
||||
)
|
||||
);
|
||||
|
||||
const eventTypes = [ 'change' ]
|
||||
const eventTypes = [ 'change' ];
|
||||
|
||||
/**
|
||||
* Navigator online: https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLine
|
||||
@@ -23,63 +23,63 @@ const eventTypes = [ 'change' ]
|
||||
*/
|
||||
const NetInfo = {
|
||||
addEventListener(type: string, handler: Function): { remove: () => void } {
|
||||
invariant(eventTypes.indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type)
|
||||
invariant(eventTypes.indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type);
|
||||
if (!connection) {
|
||||
console.error('Network Connection API is not supported. Not listening for connection type changes.')
|
||||
console.error('Network Connection API is not supported. Not listening for connection type changes.');
|
||||
return {
|
||||
remove: () => {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
connection.addEventListener(type, handler)
|
||||
connection.addEventListener(type, handler);
|
||||
return {
|
||||
remove: () => NetInfo.removeEventListener(type, handler)
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
removeEventListener(type: string, handler: Function): void {
|
||||
invariant(eventTypes.indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type)
|
||||
if (!connection) { return }
|
||||
connection.removeEventListener(type, handler)
|
||||
invariant(eventTypes.indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type);
|
||||
if (!connection) { return; }
|
||||
connection.removeEventListener(type, handler);
|
||||
},
|
||||
|
||||
fetch(): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
resolve(connection.type)
|
||||
resolve(connection.type);
|
||||
} catch (err) {
|
||||
resolve('unknown')
|
||||
resolve('unknown');
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
isConnected: {
|
||||
addEventListener(type: string, handler: Function): { remove: () => void } {
|
||||
invariant(eventTypes.indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type)
|
||||
window.addEventListener('online', handler.bind(null, true), false)
|
||||
window.addEventListener('offline', handler.bind(null, false), false)
|
||||
invariant(eventTypes.indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type);
|
||||
window.addEventListener('online', handler.bind(null, true), false);
|
||||
window.addEventListener('offline', handler.bind(null, false), false);
|
||||
|
||||
return {
|
||||
remove: () => NetInfo.isConnected.removeEventListener(type, handler)
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
removeEventListener(type: string, handler: Function): void {
|
||||
invariant(eventTypes.indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type)
|
||||
window.removeEventListener('online', handler.bind(null, true), false)
|
||||
window.removeEventListener('offline', handler.bind(null, false), false)
|
||||
invariant(eventTypes.indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type);
|
||||
window.removeEventListener('online', handler.bind(null, true), false);
|
||||
window.removeEventListener('offline', handler.bind(null, false), false);
|
||||
},
|
||||
|
||||
fetch(): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
resolve(window.navigator.onLine)
|
||||
resolve(window.navigator.onLine);
|
||||
} catch (err) {
|
||||
resolve(true)
|
||||
resolve(true);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = NetInfo
|
||||
module.exports = NetInfo;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
suite('apis/PixelRatio', () => {
|
||||
test.skip('NO TEST COVERAGE', () => {})
|
||||
})
|
||||
test.skip('NO TEST COVERAGE', () => {});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import Dimensions from '../Dimensions'
|
||||
import Dimensions from '../Dimensions';
|
||||
|
||||
/**
|
||||
* PixelRatio gives access to the device pixel density.
|
||||
@@ -16,14 +16,14 @@ class PixelRatio {
|
||||
* Returns the device pixel density.
|
||||
*/
|
||||
static get(): number {
|
||||
return Dimensions.get('window').scale
|
||||
return Dimensions.get('window').scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* No equivalent for Web
|
||||
*/
|
||||
static getFontScale(): number {
|
||||
return Dimensions.get('window').fontScale || PixelRatio.get()
|
||||
return Dimensions.get('window').fontScale || PixelRatio.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,7 +31,7 @@ class PixelRatio {
|
||||
* Guaranteed to return an integer number.
|
||||
*/
|
||||
static getPixelSizeForLayoutSize(layoutSize: number): number {
|
||||
return Math.round(layoutSize * PixelRatio.get())
|
||||
return Math.round(layoutSize * PixelRatio.get());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,9 +41,9 @@ class PixelRatio {
|
||||
* exactly (8.33 * 3) = 25 pixels.
|
||||
*/
|
||||
static roundToNearestPixel(layoutSize: number): number {
|
||||
const ratio = PixelRatio.get()
|
||||
return Math.round(layoutSize * ratio) / ratio
|
||||
const ratio = PixelRatio.get();
|
||||
return Math.round(layoutSize * ratio) / ratio;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PixelRatio
|
||||
module.exports = PixelRatio;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const Platform = {
|
||||
OS: 'web',
|
||||
select: (obj: Object) => obj.web
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Platform
|
||||
module.exports = Platform;
|
||||
|
||||
@@ -23,17 +23,18 @@ class StyleSheetValidation {
|
||||
var message2 = '\nValid style props: ' +
|
||||
JSON.stringify(Object.keys(allStylePropTypes).sort(), null, ' ');
|
||||
styleError(message1, style, caller, message2);
|
||||
}
|
||||
var error = allStylePropTypes[prop](
|
||||
style,
|
||||
prop,
|
||||
caller,
|
||||
ReactPropTypeLocations.prop,
|
||||
null,
|
||||
ReactPropTypesSecret
|
||||
);
|
||||
if (error) {
|
||||
styleError(error.message, style, caller);
|
||||
} else {
|
||||
var error = allStylePropTypes[prop](
|
||||
style,
|
||||
prop,
|
||||
caller,
|
||||
ReactPropTypeLocations.prop,
|
||||
null,
|
||||
ReactPropTypesSecret
|
||||
);
|
||||
if (error) {
|
||||
styleError(error.message, style, caller);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,7 +74,8 @@ StyleSheetValidation.addValidStylePropTypes({
|
||||
display: PropTypes.string,
|
||||
float: PropTypes.oneOf([ 'left', 'none', 'right' ]),
|
||||
font: PropTypes.string, /* @private */
|
||||
listStyle: PropTypes.string
|
||||
listStyle: PropTypes.string,
|
||||
WebkitOverflowScrolling: PropTypes.string /* @private */
|
||||
})
|
||||
|
||||
module.exports = StyleSheetValidation
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
import assert from 'assert'
|
||||
import createReactStyleObject from '../createReactStyleObject'
|
||||
import assert from 'assert';
|
||||
import createReactStyleObject from '../createReactStyleObject';
|
||||
|
||||
suite('apis/StyleSheet/createReactStyleObject', () => {
|
||||
test('converts ReactNative style to ReactDOM style', () => {
|
||||
const reactNativeStyle = { display: 'flex', marginVertical: 0, opacity: 0 }
|
||||
const expectedStyle = { display: 'flex', marginTop: '0px', marginBottom: '0px', opacity: 0 }
|
||||
const reactNativeStyle = { display: 'flex', marginVertical: 0, opacity: 0 };
|
||||
const expectedStyle = { display: 'flex', marginTop: '0px', marginBottom: '0px', opacity: 0 };
|
||||
|
||||
assert.deepEqual(createReactStyleObject(reactNativeStyle), expectedStyle)
|
||||
})
|
||||
})
|
||||
assert.deepEqual(createReactStyleObject(reactNativeStyle), expectedStyle);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
import assert from 'assert'
|
||||
import expandStyle from '../expandStyle'
|
||||
import assert from 'assert';
|
||||
import expandStyle from '../expandStyle';
|
||||
|
||||
suite('apis/StyleSheet/expandStyle', () => {
|
||||
test('shortform -> longform', () => {
|
||||
@@ -14,7 +14,7 @@ suite('apis/StyleSheet/expandStyle', () => {
|
||||
marginTop: 50,
|
||||
marginVertical: 25,
|
||||
margin: 10
|
||||
}
|
||||
};
|
||||
|
||||
const expected = {
|
||||
borderBottomStyle: 'solid',
|
||||
@@ -31,36 +31,36 @@ suite('apis/StyleSheet/expandStyle', () => {
|
||||
marginBottom: '25px',
|
||||
marginLeft: '10px',
|
||||
marginRight: '10px'
|
||||
}
|
||||
};
|
||||
|
||||
assert.deepEqual(expandStyle(initial), expected)
|
||||
})
|
||||
assert.deepEqual(expandStyle(initial), expected);
|
||||
});
|
||||
|
||||
test('textAlignVertical', () => {
|
||||
const initial = {
|
||||
textAlignVertical: 'center'
|
||||
}
|
||||
};
|
||||
|
||||
const expected = {
|
||||
verticalAlign: 'middle'
|
||||
}
|
||||
};
|
||||
|
||||
assert.deepEqual(expandStyle(initial), expected)
|
||||
})
|
||||
assert.deepEqual(expandStyle(initial), expected);
|
||||
});
|
||||
|
||||
test('flex', () => {
|
||||
const value = 10
|
||||
const value = 10;
|
||||
|
||||
const initial = {
|
||||
flex: value
|
||||
}
|
||||
};
|
||||
|
||||
const expected = {
|
||||
flexGrow: value,
|
||||
flexShrink: 1,
|
||||
flexBasis: 'auto'
|
||||
}
|
||||
};
|
||||
|
||||
assert.deepEqual(expandStyle(initial), expected)
|
||||
})
|
||||
})
|
||||
assert.deepEqual(expandStyle(initial), expected);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
import assert from 'assert'
|
||||
import I18nManager from '../../I18nManager'
|
||||
import i18nStyle from '../i18nStyle'
|
||||
import assert from 'assert';
|
||||
import I18nManager from '../../I18nManager';
|
||||
import i18nStyle from '../i18nStyle';
|
||||
|
||||
const initial = {
|
||||
borderLeftColor: 'red',
|
||||
@@ -24,13 +24,13 @@ const initial = {
|
||||
textAlign: 'left',
|
||||
textShadowOffset: { width: '1rem', height: 10 },
|
||||
writingDirection: 'ltr'
|
||||
}
|
||||
};
|
||||
|
||||
const initialNoI18n = Object.keys(initial).reduce((acc, prop) => {
|
||||
const newProp = `${prop}$noI18n`
|
||||
acc[newProp] = initial[prop]
|
||||
return acc
|
||||
}, {})
|
||||
const newProp = `${prop}$noI18n`;
|
||||
acc[newProp] = initial[prop];
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const expected = {
|
||||
borderLeftColor: 'blue',
|
||||
@@ -52,40 +52,40 @@ const expected = {
|
||||
textAlign: 'right',
|
||||
textShadowOffset: { width: '-1rem', height: 10 },
|
||||
writingDirection: 'rtl'
|
||||
}
|
||||
};
|
||||
|
||||
suite('apis/StyleSheet/i18nStyle', () => {
|
||||
suite('LTR mode', () => {
|
||||
setup(() => {
|
||||
I18nManager.allowRTL(false)
|
||||
})
|
||||
I18nManager.allowRTL(false);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
I18nManager.allowRTL(true)
|
||||
})
|
||||
I18nManager.allowRTL(true);
|
||||
});
|
||||
|
||||
test('does not auto-flip', () => {
|
||||
assert.deepEqual(i18nStyle(initial), initial)
|
||||
})
|
||||
assert.deepEqual(i18nStyle(initial), initial);
|
||||
});
|
||||
test('normalizes properties', () => {
|
||||
assert.deepEqual(i18nStyle(initialNoI18n), initial)
|
||||
})
|
||||
})
|
||||
assert.deepEqual(i18nStyle(initialNoI18n), initial);
|
||||
});
|
||||
});
|
||||
|
||||
suite('RTL mode', () => {
|
||||
setup(() => {
|
||||
I18nManager.forceRTL(true)
|
||||
})
|
||||
I18nManager.forceRTL(true);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
I18nManager.forceRTL(false)
|
||||
})
|
||||
I18nManager.forceRTL(false);
|
||||
});
|
||||
|
||||
test('does auto-flip', () => {
|
||||
assert.deepEqual(i18nStyle(initial), expected)
|
||||
})
|
||||
assert.deepEqual(i18nStyle(initial), expected);
|
||||
});
|
||||
test('normalizes properties', () => {
|
||||
assert.deepEqual(i18nStyle(initialNoI18n), initial)
|
||||
})
|
||||
})
|
||||
})
|
||||
assert.deepEqual(i18nStyle(initialNoI18n), initial);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,52 +1,52 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
import assert from 'assert'
|
||||
import { getDefaultStyleSheet } from '../css'
|
||||
import isPlainObject from 'lodash/isPlainObject'
|
||||
import StyleSheet from '..'
|
||||
import assert from 'assert';
|
||||
import { getDefaultStyleSheet } from '../css';
|
||||
import isPlainObject from 'lodash/isPlainObject';
|
||||
import StyleSheet from '..';
|
||||
|
||||
suite('apis/StyleSheet', () => {
|
||||
setup(() => {
|
||||
StyleSheet._reset()
|
||||
})
|
||||
StyleSheet._reset();
|
||||
});
|
||||
|
||||
test('absoluteFill', () => {
|
||||
assert(Number.isInteger(StyleSheet.absoluteFill) === true)
|
||||
})
|
||||
assert(Number.isInteger(StyleSheet.absoluteFill) === true);
|
||||
});
|
||||
|
||||
test('absoluteFillObject', () => {
|
||||
assert.ok(isPlainObject(StyleSheet.absoluteFillObject) === true)
|
||||
})
|
||||
assert.ok(isPlainObject(StyleSheet.absoluteFillObject) === true);
|
||||
});
|
||||
|
||||
suite('create', () => {
|
||||
test('replaces styles with numbers', () => {
|
||||
const style = StyleSheet.create({ root: { opacity: 1 } })
|
||||
assert(Number.isInteger(style.root) === true)
|
||||
})
|
||||
const style = StyleSheet.create({ root: { opacity: 1 } });
|
||||
assert(Number.isInteger(style.root) === true);
|
||||
});
|
||||
|
||||
test('renders a style sheet in the browser', () => {
|
||||
StyleSheet.create({ root: { color: 'red' } })
|
||||
StyleSheet.create({ root: { color: 'red' } });
|
||||
assert.equal(
|
||||
document.getElementById('__react-native-style').textContent,
|
||||
getDefaultStyleSheet()
|
||||
)
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('flatten', () => {
|
||||
assert(typeof StyleSheet.flatten === 'function')
|
||||
})
|
||||
assert(typeof StyleSheet.flatten === 'function');
|
||||
});
|
||||
|
||||
test('hairlineWidth', () => {
|
||||
assert(Number.isInteger(StyleSheet.hairlineWidth) === true)
|
||||
})
|
||||
assert(Number.isInteger(StyleSheet.hairlineWidth) === true);
|
||||
});
|
||||
|
||||
test('render', () => {
|
||||
assert.equal(
|
||||
StyleSheet.render().props.dangerouslySetInnerHTML.__html,
|
||||
getDefaultStyleSheet()
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('resolve', () => {
|
||||
assert.deepEqual(
|
||||
@@ -66,6 +66,6 @@ suite('apis/StyleSheet', () => {
|
||||
pointerEvents: null
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
import assert from 'assert'
|
||||
import normalizeValue from '../normalizeValue'
|
||||
import assert from 'assert';
|
||||
import normalizeValue from '../normalizeValue';
|
||||
|
||||
suite('apis/StyleSheet/normalizeValue', () => {
|
||||
test('normalizes property values requiring units', () => {
|
||||
assert.deepEqual(normalizeValue('margin', 0), '0px')
|
||||
})
|
||||
assert.deepEqual(normalizeValue('margin', 0), '0px');
|
||||
});
|
||||
test('ignores unitless property values', () => {
|
||||
assert.deepEqual(normalizeValue('flexGrow', 1), 1)
|
||||
assert.deepEqual(normalizeValue('scale', 2), 2)
|
||||
})
|
||||
})
|
||||
assert.deepEqual(normalizeValue('flexGrow', 1), 1);
|
||||
assert.deepEqual(normalizeValue('scale', 2), 2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
import assert from 'assert'
|
||||
import processTextShadow from '../processTextShadow'
|
||||
import assert from 'assert';
|
||||
import processTextShadow from '../processTextShadow';
|
||||
|
||||
suite('apis/StyleSheet/processTextShadow', () => {
|
||||
test('textShadowOffset', () => {
|
||||
@@ -9,7 +9,7 @@ suite('apis/StyleSheet/processTextShadow', () => {
|
||||
textShadowColor: 'red',
|
||||
textShadowOffset: { width: 2, height: 2 },
|
||||
textShadowRadius: 5
|
||||
}
|
||||
};
|
||||
|
||||
assert.deepEqual(
|
||||
processTextShadow(style),
|
||||
@@ -19,6 +19,6 @@ suite('apis/StyleSheet/processTextShadow', () => {
|
||||
textShadowOffset: null,
|
||||
textShadowRadius: null
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
import assert from 'assert'
|
||||
import processTransform from '../processTransform'
|
||||
import assert from 'assert';
|
||||
import processTransform from '../processTransform';
|
||||
|
||||
suite('apis/StyleSheet/processTransform', () => {
|
||||
test('transform', () => {
|
||||
@@ -11,18 +11,18 @@ suite('apis/StyleSheet/processTransform', () => {
|
||||
{ translateX: 20 },
|
||||
{ rotate: '20deg' }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
assert.deepEqual(
|
||||
processTransform(style),
|
||||
{ transform: 'scaleX(20) translateX(20px) rotate(20deg)' }
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('transformMatrix', () => {
|
||||
const style = {
|
||||
transformMatrix: [ 1, 2, 3, 4, 5, 6 ]
|
||||
}
|
||||
};
|
||||
|
||||
assert.deepEqual(
|
||||
processTransform(style),
|
||||
@@ -30,6 +30,6 @@ suite('apis/StyleSheet/processTransform', () => {
|
||||
transform: 'matrix3d(1,2,3,4,5,6)',
|
||||
transformMatrix: null
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
import assert from 'assert'
|
||||
import processVendorPrefixes from '../processVendorPrefixes'
|
||||
import assert from 'assert';
|
||||
import processVendorPrefixes from '../processVendorPrefixes';
|
||||
|
||||
suite('apis/StyleSheet/processVendorPrefixes', () => {
|
||||
test('handles array values', () => {
|
||||
const style = {
|
||||
display: [ '-webkit-flex', 'flex' ]
|
||||
}
|
||||
};
|
||||
|
||||
assert.deepEqual(
|
||||
processVendorPrefixes(style),
|
||||
{ display: 'flex' }
|
||||
)
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import expandStyle from './expandStyle'
|
||||
import flattenStyle from '../../modules/flattenStyle'
|
||||
import i18nStyle from './i18nStyle'
|
||||
import processTextShadow from './processTextShadow'
|
||||
import processTransform from './processTransform'
|
||||
import processVendorPrefixes from './processVendorPrefixes'
|
||||
import expandStyle from './expandStyle';
|
||||
import flattenStyle from '../../modules/flattenStyle';
|
||||
import i18nStyle from './i18nStyle';
|
||||
import processTextShadow from './processTextShadow';
|
||||
import processTransform from './processTransform';
|
||||
import processVendorPrefixes from './processVendorPrefixes';
|
||||
|
||||
const plugins = [
|
||||
const processors = [
|
||||
processTextShadow,
|
||||
processTransform,
|
||||
processVendorPrefixes
|
||||
]
|
||||
];
|
||||
|
||||
const applyPlugins = (style) => {
|
||||
return plugins.reduce((style, plugin) => plugin(style), style)
|
||||
}
|
||||
const applyProcessors = (style) => processors.reduce((style, processor) => processor(style), style);
|
||||
|
||||
const createReactDOMStyleObject = (reactNativeStyle) => applyPlugins(expandStyle(i18nStyle(flattenStyle(reactNativeStyle))))
|
||||
const createReactDOMStyleObject = (reactNativeStyle) => applyProcessors(
|
||||
expandStyle(i18nStyle(flattenStyle(reactNativeStyle)))
|
||||
);
|
||||
|
||||
module.exports = createReactDOMStyleObject
|
||||
module.exports = createReactDOMStyleObject;
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
const DISPLAY_FLEX_CLASSNAME = '__style_df'
|
||||
const POINTER_EVENTS_AUTO_CLASSNAME = '__style_pea'
|
||||
const POINTER_EVENTS_BOX_NONE_CLASSNAME = '__style_pebn'
|
||||
const POINTER_EVENTS_BOX_ONLY_CLASSNAME = '__style_pebo'
|
||||
const POINTER_EVENTS_NONE_CLASSNAME = '__style_pen'
|
||||
const DISPLAY_FLEX_CLASSNAME = '__style_df';
|
||||
const POINTER_EVENTS_AUTO_CLASSNAME = '__style_pea';
|
||||
const POINTER_EVENTS_BOX_NONE_CLASSNAME = '__style_pebn';
|
||||
const POINTER_EVENTS_BOX_ONLY_CLASSNAME = '__style_pebo';
|
||||
const POINTER_EVENTS_NONE_CLASSNAME = '__style_pen';
|
||||
|
||||
/* eslint-disable max-len */
|
||||
const CSS_RESET =
|
||||
// reset unwanted styles
|
||||
'/* React Native */\n' +
|
||||
'html {font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}\n' +
|
||||
'body {margin:0}\n' +
|
||||
'button::-moz-focus-inner, input::-moz-focus-inner {border:0;padding:0}\n' +
|
||||
'input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration {display:none}'
|
||||
'html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}\n' +
|
||||
'body{margin:0}\n' +
|
||||
'button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}\n' +
|
||||
'input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration {display:none}';
|
||||
|
||||
const CSS_HELPERS =
|
||||
// vendor prefix 'display:flex' until React supports fallback values for inline styles
|
||||
`.${DISPLAY_FLEX_CLASSNAME} {display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}\n` +
|
||||
// implement React Native's pointer event values
|
||||
`.${POINTER_EVENTS_AUTO_CLASSNAME}, .${POINTER_EVENTS_BOX_ONLY_CLASSNAME}, .${POINTER_EVENTS_BOX_NONE_CLASSNAME} * {pointer-events:auto}\n` +
|
||||
`.${POINTER_EVENTS_NONE_CLASSNAME}, .${POINTER_EVENTS_BOX_ONLY_CLASSNAME} *, .${POINTER_EVENTS_NONE_CLASSNAME} {pointer-events:none}`
|
||||
`.${POINTER_EVENTS_NONE_CLASSNAME}, .${POINTER_EVENTS_BOX_ONLY_CLASSNAME} *, .${POINTER_EVENTS_NONE_CLASSNAME} {pointer-events:none}`;
|
||||
/* eslint-enable max-len */
|
||||
|
||||
const styleAsClassName = {
|
||||
display: {
|
||||
@@ -29,10 +31,10 @@ const styleAsClassName = {
|
||||
'box-only': POINTER_EVENTS_BOX_ONLY_CLASSNAME,
|
||||
'none': POINTER_EVENTS_NONE_CLASSNAME
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const getDefaultStyleSheet = () => `${CSS_RESET}\n${CSS_HELPERS}`
|
||||
export const getDefaultStyleSheet = () => `${CSS_RESET}\n${CSS_HELPERS}`;
|
||||
|
||||
export const getStyleAsHelperClassName = (prop, value) => {
|
||||
return styleAsClassName[prop] && styleAsClassName[prop][value]
|
||||
}
|
||||
return styleAsClassName[prop] && styleAsClassName[prop][value];
|
||||
};
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
* longfrom equivalents.
|
||||
*/
|
||||
|
||||
import normalizeValue from './normalizeValue'
|
||||
import normalizeValue from './normalizeValue';
|
||||
|
||||
const emptyObject = {}
|
||||
const emptyObject = {};
|
||||
const styleShortFormProperties = {
|
||||
borderColor: [ 'borderTopColor', 'borderRightColor', 'borderBottomColor', 'borderLeftColor' ],
|
||||
borderRadius: [ 'borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomRightRadius', 'borderBottomLeftRadius' ],
|
||||
@@ -26,48 +26,48 @@ const styleShortFormProperties = {
|
||||
paddingVertical: [ 'paddingTop', 'paddingBottom' ],
|
||||
textDecorationLine: [ 'textDecoration' ],
|
||||
writingDirection: [ 'direction' ]
|
||||
}
|
||||
};
|
||||
|
||||
const alphaSort = (arr) => arr.sort((a, b) => {
|
||||
if (a < b) { return -1 }
|
||||
if (a > b) { return 1 }
|
||||
return 0
|
||||
})
|
||||
if (a < b) { return -1; }
|
||||
if (a > b) { return 1; }
|
||||
return 0;
|
||||
});
|
||||
|
||||
const createStyleReducer = (originalStyle) => {
|
||||
const originalStyleProps = Object.keys(originalStyle)
|
||||
const originalStyleProps = Object.keys(originalStyle);
|
||||
|
||||
return (style, prop) => {
|
||||
const value = normalizeValue(prop, originalStyle[prop])
|
||||
const longFormProperties = styleShortFormProperties[prop]
|
||||
const value = normalizeValue(prop, originalStyle[prop]);
|
||||
const longFormProperties = styleShortFormProperties[prop];
|
||||
|
||||
// React Native treats `flex:1` like `flex:1 1 auto`
|
||||
if (prop === 'flex') {
|
||||
style.flexGrow = value
|
||||
style.flexShrink = 1
|
||||
style.flexBasis = 'auto'
|
||||
style.flexGrow = value;
|
||||
style.flexShrink = 1;
|
||||
style.flexBasis = 'auto';
|
||||
// React Native accepts 'center' as a value
|
||||
} else if (prop === 'textAlignVertical') {
|
||||
style.verticalAlign = (value === 'center' ? 'middle' : value)
|
||||
style.verticalAlign = (value === 'center' ? 'middle' : value);
|
||||
} else if (longFormProperties) {
|
||||
longFormProperties.forEach((longForm, i) => {
|
||||
// the value of any longform property in the original styles takes
|
||||
// precedence over the shortform's value
|
||||
if (originalStyleProps.indexOf(longForm) === -1) {
|
||||
style[longForm] = value
|
||||
style[longForm] = value;
|
||||
}
|
||||
})
|
||||
});
|
||||
} else {
|
||||
style[prop] = value
|
||||
style[prop] = value;
|
||||
}
|
||||
return style
|
||||
}
|
||||
}
|
||||
return style;
|
||||
};
|
||||
};
|
||||
|
||||
const expandStyle = (style = emptyObject) => {
|
||||
const sortedStyleProps = alphaSort(Object.keys(style))
|
||||
const styleReducer = createStyleReducer(style)
|
||||
return sortedStyleProps.reduce(styleReducer, {})
|
||||
}
|
||||
const sortedStyleProps = alphaSort(Object.keys(style));
|
||||
const styleReducer = createStyleReducer(style);
|
||||
return sortedStyleProps.reduce(styleReducer, {});
|
||||
};
|
||||
|
||||
module.exports = expandStyle
|
||||
module.exports = expandStyle;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import I18nManager from '../I18nManager'
|
||||
|
||||
const CSS_UNIT_RE = /^[+-]?\d*(?:\.\d+)?(?:[Ee][+-]?\d+)?(\w*)/
|
||||
import I18nManager from '../I18nManager';
|
||||
import multiplyStyleLengthValue from '../../modules/multiplyStyleLengthValue';
|
||||
|
||||
/**
|
||||
* Map of property names to their BiDi equivalent.
|
||||
@@ -22,103 +21,86 @@ const PROPERTIES_TO_SWAP = {
|
||||
'paddingLeft': 'paddingRight',
|
||||
'paddingRight': 'paddingLeft',
|
||||
'right': 'left'
|
||||
}
|
||||
};
|
||||
|
||||
const PROPERTIES_SWAP_LEFT_RIGHT = {
|
||||
'clear': true,
|
||||
'float': true,
|
||||
'textAlign': true
|
||||
}
|
||||
};
|
||||
|
||||
const PROPERTIES_SWAP_LTR_RTL = {
|
||||
'writingDirection': true
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Invert the sign of a numeric-like value
|
||||
*/
|
||||
const additiveInverse = (value: String | Number) => {
|
||||
if (typeof value === 'string') {
|
||||
const number = parseFloat(value, 10) * -1
|
||||
const unit = getUnit(value)
|
||||
return `${number}${unit}`
|
||||
} else if (isNumeric(value)) {
|
||||
return value * -1
|
||||
}
|
||||
}
|
||||
const additiveInverse = (value: String | Number) => multiplyStyleLengthValue(value, -1);
|
||||
|
||||
/**
|
||||
* BiDi flip the given property.
|
||||
*/
|
||||
const flipProperty = (prop:String): String => {
|
||||
return PROPERTIES_TO_SWAP.hasOwnProperty(prop) ? PROPERTIES_TO_SWAP[prop] : prop
|
||||
}
|
||||
return PROPERTIES_TO_SWAP.hasOwnProperty(prop) ? PROPERTIES_TO_SWAP[prop] : prop;
|
||||
};
|
||||
|
||||
/**
|
||||
* BiDi flip translateX
|
||||
*/
|
||||
const flipTransform = (transform: Object): Object => {
|
||||
const translateX = transform.translateX
|
||||
const translateX = transform.translateX;
|
||||
if (translateX != null) {
|
||||
transform.translateX = additiveInverse(translateX)
|
||||
transform.translateX = additiveInverse(translateX);
|
||||
}
|
||||
return transform
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CSS unit for string values
|
||||
*/
|
||||
const getUnit = (str) => str.match(CSS_UNIT_RE)[1]
|
||||
|
||||
const isNumeric = (n) => {
|
||||
return !isNaN(parseFloat(n)) && isFinite(n)
|
||||
}
|
||||
return transform;
|
||||
};
|
||||
|
||||
const swapLeftRight = (value:String): String => {
|
||||
return value === 'left' ? 'right' : value === 'right' ? 'left' : value
|
||||
}
|
||||
return value === 'left' ? 'right' : value === 'right' ? 'left' : value;
|
||||
};
|
||||
|
||||
const swapLtrRtl = (value:String): String => {
|
||||
return value === 'ltr' ? 'rtl' : value === 'rtl' ? 'ltr' : value
|
||||
}
|
||||
return value === 'ltr' ? 'rtl' : value === 'rtl' ? 'ltr' : value;
|
||||
};
|
||||
|
||||
const i18nStyle = (style = {}) => {
|
||||
const newStyle = {}
|
||||
const newStyle = {};
|
||||
for (const prop in style) {
|
||||
if (style.hasOwnProperty(prop)) {
|
||||
const indexOfNoFlip = prop.indexOf('$noI18n')
|
||||
const indexOfNoFlip = prop.indexOf('$noI18n');
|
||||
|
||||
if (I18nManager.isRTL) {
|
||||
if (PROPERTIES_TO_SWAP[prop]) {
|
||||
const newProp = flipProperty(prop)
|
||||
newStyle[newProp] = style[prop]
|
||||
const newProp = flipProperty(prop);
|
||||
newStyle[newProp] = style[prop];
|
||||
} else if (PROPERTIES_SWAP_LEFT_RIGHT[prop]) {
|
||||
newStyle[prop] = swapLeftRight(style[prop])
|
||||
newStyle[prop] = swapLeftRight(style[prop]);
|
||||
} else if (PROPERTIES_SWAP_LTR_RTL[prop]) {
|
||||
newStyle[prop] = swapLtrRtl(style[prop])
|
||||
newStyle[prop] = swapLtrRtl(style[prop]);
|
||||
} else if (prop === 'textShadowOffset') {
|
||||
newStyle[prop] = style[prop]
|
||||
newStyle[prop].width = additiveInverse(style[prop].width)
|
||||
newStyle[prop] = style[prop];
|
||||
newStyle[prop].width = additiveInverse(style[prop].width);
|
||||
} else if (prop === 'transform') {
|
||||
newStyle[prop] = style[prop].map(flipTransform)
|
||||
newStyle[prop] = style[prop].map(flipTransform);
|
||||
} else if (indexOfNoFlip > -1) {
|
||||
const newProp = prop.substring(0, indexOfNoFlip)
|
||||
newStyle[newProp] = style[prop]
|
||||
const newProp = prop.substring(0, indexOfNoFlip);
|
||||
newStyle[newProp] = style[prop];
|
||||
} else {
|
||||
newStyle[prop] = style[prop]
|
||||
newStyle[prop] = style[prop];
|
||||
}
|
||||
} else {
|
||||
if (indexOfNoFlip > -1) {
|
||||
const newProp = prop.substring(0, indexOfNoFlip)
|
||||
newStyle[newProp] = style[prop]
|
||||
const newProp = prop.substring(0, indexOfNoFlip);
|
||||
newStyle[newProp] = style[prop];
|
||||
} else {
|
||||
newStyle[prop] = style[prop]
|
||||
newStyle[prop] = style[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newStyle
|
||||
}
|
||||
return newStyle;
|
||||
};
|
||||
|
||||
module.exports = i18nStyle
|
||||
module.exports = i18nStyle;
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import * as css from './css'
|
||||
import createReactStyleObject from './createReactStyleObject'
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment'
|
||||
import flattenStyle from '../../modules/flattenStyle'
|
||||
import React from 'react'
|
||||
import ReactNativePropRegistry from '../../modules/ReactNativePropRegistry'
|
||||
import StyleSheetValidation from './StyleSheetValidation'
|
||||
import * as css from './css';
|
||||
import createReactStyleObject from './createReactStyleObject';
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
|
||||
import flattenStyle from '../../modules/flattenStyle';
|
||||
import React from 'react';
|
||||
import ReactNativePropRegistry from '../../modules/ReactNativePropRegistry';
|
||||
import StyleSheetValidation from './StyleSheetValidation';
|
||||
|
||||
let styleElement
|
||||
let shouldInsertStyleSheet = ExecutionEnvironment.canUseDOM
|
||||
let styleElement;
|
||||
let shouldInsertStyleSheet = ExecutionEnvironment.canUseDOM;
|
||||
|
||||
const STYLE_SHEET_ID = '__react-native-style'
|
||||
const STYLE_SHEET_ID = '__react-native-style';
|
||||
|
||||
const absoluteFillObject = { position: 'absolute', left: 0, right: 0, top: 0, bottom: 0 }
|
||||
const absoluteFillObject = { position: 'absolute', left: 0, right: 0, top: 0, bottom: 0 };
|
||||
|
||||
const defaultStyleSheet = css.getDefaultStyleSheet()
|
||||
const defaultStyleSheet = css.getDefaultStyleSheet();
|
||||
|
||||
const insertStyleSheet = () => {
|
||||
// check if the server rendered the style sheet
|
||||
styleElement = document.getElementById(STYLE_SHEET_ID)
|
||||
styleElement = document.getElementById(STYLE_SHEET_ID);
|
||||
// if not, inject the style sheet
|
||||
if (!styleElement) {
|
||||
document.head.insertAdjacentHTML(
|
||||
'afterbegin',
|
||||
`<style id="${STYLE_SHEET_ID}">${defaultStyleSheet}</style>`
|
||||
)
|
||||
shouldInsertStyleSheet = false
|
||||
);
|
||||
shouldInsertStyleSheet = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
@@ -35,9 +35,9 @@ module.exports = {
|
||||
*/
|
||||
_reset() {
|
||||
if (styleElement) {
|
||||
document.head.removeChild(styleElement)
|
||||
styleElement = null
|
||||
shouldInsertStyleSheet = true
|
||||
document.head.removeChild(styleElement);
|
||||
styleElement = null;
|
||||
shouldInsertStyleSheet = true;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -47,15 +47,15 @@ module.exports = {
|
||||
|
||||
create(styles) {
|
||||
if (shouldInsertStyleSheet) {
|
||||
insertStyleSheet()
|
||||
insertStyleSheet();
|
||||
}
|
||||
|
||||
const result = {}
|
||||
const result = {};
|
||||
for (const key in styles) {
|
||||
StyleSheetValidation.validateStyle(key, styles)
|
||||
result[key] = ReactNativePropRegistry.register(styles[key])
|
||||
StyleSheetValidation.validateStyle(key, styles);
|
||||
result[key] = ReactNativePropRegistry.register(styles[key]);
|
||||
}
|
||||
return result
|
||||
return result;
|
||||
},
|
||||
|
||||
hairlineWidth: 1,
|
||||
@@ -64,7 +64,7 @@ module.exports = {
|
||||
|
||||
/* @platform web */
|
||||
render() {
|
||||
return <style dangerouslySetInnerHTML={{ __html: defaultStyleSheet }} id={STYLE_SHEET_ID} />
|
||||
return <style dangerouslySetInnerHTML={{ __html: defaultStyleSheet }} id={STYLE_SHEET_ID} />;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -72,17 +72,17 @@ module.exports = {
|
||||
* @platform web
|
||||
*/
|
||||
resolve(props) {
|
||||
let className = props.className || ''
|
||||
let style = createReactStyleObject(props.style)
|
||||
let className = props.className || '';
|
||||
const style = createReactStyleObject(props.style);
|
||||
for (const prop in style) {
|
||||
const value = style[prop]
|
||||
const replacementClassName = css.getStyleAsHelperClassName(prop, value)
|
||||
const value = style[prop];
|
||||
const replacementClassName = css.getStyleAsHelperClassName(prop, value);
|
||||
if (replacementClassName) {
|
||||
className += ` ${replacementClassName}`
|
||||
style[prop] = null
|
||||
className += ` ${replacementClassName}`;
|
||||
style[prop] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return { className, style }
|
||||
return { className, style };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -25,13 +25,13 @@ const unitlessNumbers = {
|
||||
scaleX: true,
|
||||
scaleY: true,
|
||||
scaleZ: true
|
||||
}
|
||||
};
|
||||
|
||||
const normalizeValue = (property, value) => {
|
||||
if (!unitlessNumbers[property] && typeof value === 'number') {
|
||||
value = `${value}px`
|
||||
value = `${value}px`;
|
||||
}
|
||||
return value
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
module.exports = normalizeValue
|
||||
module.exports = normalizeValue;
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import normalizeValue from './normalizeValue'
|
||||
import normalizeValue from './normalizeValue';
|
||||
|
||||
const processTextShadow = (style) => {
|
||||
if (style && style.textShadowOffset) {
|
||||
const { height, width } = style.textShadowOffset
|
||||
const offsetX = normalizeValue(null, height || 0)
|
||||
const offsetY = normalizeValue(null, width || 0)
|
||||
const blurRadius = normalizeValue(null, style.textShadowRadius || 0)
|
||||
const color = style.textShadowColor || 'currentcolor'
|
||||
const { height, width } = style.textShadowOffset;
|
||||
const offsetX = normalizeValue(null, height || 0);
|
||||
const offsetY = normalizeValue(null, width || 0);
|
||||
const blurRadius = normalizeValue(null, style.textShadowRadius || 0);
|
||||
const color = style.textShadowColor || 'currentcolor';
|
||||
|
||||
style.textShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`
|
||||
style.textShadowColor = null
|
||||
style.textShadowOffset = null
|
||||
style.textShadowRadius = null
|
||||
style.textShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`;
|
||||
style.textShadowColor = null;
|
||||
style.textShadowOffset = null;
|
||||
style.textShadowRadius = null;
|
||||
}
|
||||
return style
|
||||
}
|
||||
return style;
|
||||
};
|
||||
|
||||
module.exports = processTextShadow
|
||||
module.exports = processTextShadow;
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
import normalizeValue from './normalizeValue'
|
||||
import normalizeValue from './normalizeValue';
|
||||
|
||||
// { scale: 2 } => 'scale(2)'
|
||||
// { translateX: 20 } => 'translateX(20px)'
|
||||
const mapTransform = (transform) => {
|
||||
const type = Object.keys(transform)[0]
|
||||
const value = normalizeValue(type, transform[type])
|
||||
return `${type}(${value})`
|
||||
}
|
||||
const type = Object.keys(transform)[0];
|
||||
const value = normalizeValue(type, transform[type]);
|
||||
return `${type}(${value})`;
|
||||
};
|
||||
|
||||
// [1,2,3,4,5,6] => 'matrix3d(1,2,3,4,5,6)'
|
||||
const convertTransformMatrix = (transformMatrix) => {
|
||||
var matrix = transformMatrix.join(',')
|
||||
return `matrix3d(${matrix})`
|
||||
}
|
||||
const matrix = transformMatrix.join(',');
|
||||
return `matrix3d(${matrix})`;
|
||||
};
|
||||
|
||||
const processTransform = (style) => {
|
||||
if (style) {
|
||||
if (style.transform) {
|
||||
style.transform = style.transform.map(mapTransform).join(' ')
|
||||
style.transform = style.transform.map(mapTransform).join(' ');
|
||||
} else if (style.transformMatrix) {
|
||||
style.transform = convertTransformMatrix(style.transformMatrix)
|
||||
style.transformMatrix = null
|
||||
style.transform = convertTransformMatrix(style.transformMatrix);
|
||||
style.transformMatrix = null;
|
||||
}
|
||||
}
|
||||
return style
|
||||
}
|
||||
return style;
|
||||
};
|
||||
|
||||
module.exports = processTransform
|
||||
module.exports = processTransform;
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import prefixAll from 'inline-style-prefixer/static'
|
||||
import prefixAll from 'inline-style-prefixer/static';
|
||||
|
||||
const processVendorPrefixes = (style) => {
|
||||
let prefixedStyles = prefixAll(style)
|
||||
const prefixedStyles = prefixAll(style);
|
||||
// React@15 removed undocumented support for fallback values in
|
||||
// inline-styles. Revert array values to the standard CSS value
|
||||
for (const prop in prefixedStyles) {
|
||||
const value = prefixedStyles[prop]
|
||||
const value = prefixedStyles[prop];
|
||||
if (Array.isArray(value)) {
|
||||
prefixedStyles[prop] = value[value.length - 1]
|
||||
prefixedStyles[prop] = value[value.length - 1];
|
||||
}
|
||||
}
|
||||
return prefixedStyles
|
||||
}
|
||||
return prefixedStyles;
|
||||
};
|
||||
|
||||
module.exports = processVendorPrefixes
|
||||
module.exports = processVendorPrefixes;
|
||||
|
||||
@@ -1,97 +1,97 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
import assert from 'assert'
|
||||
import UIManager from '..'
|
||||
import assert from 'assert';
|
||||
import UIManager from '..';
|
||||
|
||||
const createNode = (style = {}) => {
|
||||
const root = document.createElement('div')
|
||||
const root = document.createElement('div');
|
||||
Object.keys(style).forEach((prop) => {
|
||||
root.style[prop] = style[prop]
|
||||
})
|
||||
return root
|
||||
}
|
||||
root.style[prop] = style[prop];
|
||||
});
|
||||
return root;
|
||||
};
|
||||
|
||||
let defaultBodyMargin
|
||||
let defaultBodyMargin;
|
||||
|
||||
suite('apis/UIManager', () => {
|
||||
setup(() => {
|
||||
// remove default body margin so we can predict the measured offsets
|
||||
defaultBodyMargin = document.body.style.margin
|
||||
document.body.style.margin = 0
|
||||
})
|
||||
defaultBodyMargin = document.body.style.margin;
|
||||
document.body.style.margin = 0;
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
document.body.style.margin = defaultBodyMargin
|
||||
})
|
||||
document.body.style.margin = defaultBodyMargin;
|
||||
});
|
||||
|
||||
suite('measure', () => {
|
||||
test('provides correct layout to callback', () => {
|
||||
const node = createNode({ height: '5000px', left: '100px', position: 'relative', top: '100px', width: '5000px' })
|
||||
document.body.appendChild(node)
|
||||
const node = createNode({ height: '5000px', left: '100px', position: 'relative', top: '100px', width: '5000px' });
|
||||
document.body.appendChild(node);
|
||||
|
||||
UIManager.measure(node, (x, y, width, height, pageX, pageY) => {
|
||||
assert.equal(x, 100)
|
||||
assert.equal(y, 100)
|
||||
assert.equal(width, 5000)
|
||||
assert.equal(height, 5000)
|
||||
assert.equal(pageX, 100)
|
||||
assert.equal(pageY, 100)
|
||||
})
|
||||
assert.equal(x, 100);
|
||||
assert.equal(y, 100);
|
||||
assert.equal(width, 5000);
|
||||
assert.equal(height, 5000);
|
||||
assert.equal(pageX, 100);
|
||||
assert.equal(pageY, 100);
|
||||
});
|
||||
|
||||
// test values account for scroll position
|
||||
window.scrollTo(200, 200)
|
||||
window.scrollTo(200, 200);
|
||||
UIManager.measure(node, (x, y, width, height, pageX, pageY) => {
|
||||
assert.equal(x, 100)
|
||||
assert.equal(y, 100)
|
||||
assert.equal(width, 5000)
|
||||
assert.equal(height, 5000)
|
||||
assert.equal(pageX, -100)
|
||||
assert.equal(pageY, -100)
|
||||
})
|
||||
assert.equal(x, 100);
|
||||
assert.equal(y, 100);
|
||||
assert.equal(width, 5000);
|
||||
assert.equal(height, 5000);
|
||||
assert.equal(pageX, -100);
|
||||
assert.equal(pageY, -100);
|
||||
});
|
||||
|
||||
document.body.removeChild(node)
|
||||
})
|
||||
})
|
||||
document.body.removeChild(node);
|
||||
});
|
||||
});
|
||||
|
||||
suite('measureLayout', () => {
|
||||
test('provides correct layout to onSuccess callback', () => {
|
||||
const node = createNode({ height: '10px', width: '10px' })
|
||||
const middle = createNode({ padding: '20px' })
|
||||
const context = createNode({ padding: '20px' })
|
||||
middle.appendChild(node)
|
||||
context.appendChild(middle)
|
||||
document.body.appendChild(context)
|
||||
const node = createNode({ height: '10px', width: '10px' });
|
||||
const middle = createNode({ padding: '20px' });
|
||||
const context = createNode({ padding: '20px' });
|
||||
middle.appendChild(node);
|
||||
context.appendChild(middle);
|
||||
document.body.appendChild(context);
|
||||
|
||||
UIManager.measureLayout(node, context, () => {}, (x, y, width, height) => {
|
||||
assert.equal(x, 40)
|
||||
assert.equal(y, 40)
|
||||
assert.equal(width, 10)
|
||||
assert.equal(height, 10)
|
||||
})
|
||||
assert.equal(x, 40);
|
||||
assert.equal(y, 40);
|
||||
assert.equal(width, 10);
|
||||
assert.equal(height, 10);
|
||||
});
|
||||
|
||||
document.body.removeChild(context)
|
||||
})
|
||||
})
|
||||
document.body.removeChild(context);
|
||||
});
|
||||
});
|
||||
|
||||
suite('measureInWindow', () => {
|
||||
test('provides correct layout to callback', () => {
|
||||
const node = createNode({ height: '10px', width: '10px' })
|
||||
const middle = createNode({ padding: '20px' })
|
||||
const context = createNode({ padding: '20px' })
|
||||
middle.appendChild(node)
|
||||
context.appendChild(middle)
|
||||
document.body.appendChild(context)
|
||||
const node = createNode({ height: '10px', width: '10px' });
|
||||
const middle = createNode({ padding: '20px' });
|
||||
const context = createNode({ padding: '20px' });
|
||||
middle.appendChild(node);
|
||||
context.appendChild(middle);
|
||||
document.body.appendChild(context);
|
||||
|
||||
UIManager.measureInWindow(node, (x, y, width, height) => {
|
||||
assert.equal(x, 40)
|
||||
assert.equal(y, 40)
|
||||
assert.equal(width, 10)
|
||||
assert.equal(height, 10)
|
||||
})
|
||||
assert.equal(x, 40);
|
||||
assert.equal(y, 40);
|
||||
assert.equal(width, 10);
|
||||
assert.equal(height, 10);
|
||||
});
|
||||
|
||||
document.body.removeChild(context)
|
||||
})
|
||||
})
|
||||
document.body.removeChild(context);
|
||||
});
|
||||
});
|
||||
|
||||
suite('updateView', () => {
|
||||
const componentStub = {
|
||||
@@ -99,42 +99,42 @@ suite('apis/UIManager', () => {
|
||||
_currentElement: { _owner: {} },
|
||||
_debugID: 1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
test('add new className to existing className', () => {
|
||||
const node = createNode()
|
||||
node.className = 'existing'
|
||||
const props = { className: 'extra' }
|
||||
UIManager.updateView(node, props, componentStub)
|
||||
assert.equal(node.getAttribute('class'), 'existing extra')
|
||||
})
|
||||
const node = createNode();
|
||||
node.className = 'existing';
|
||||
const props = { className: 'extra' };
|
||||
UIManager.updateView(node, props, componentStub);
|
||||
assert.equal(node.getAttribute('class'), 'existing extra');
|
||||
});
|
||||
|
||||
test('adds correct DOM styles to existing style', () => {
|
||||
const node = createNode({ color: 'red' })
|
||||
const props = { style: { marginVertical: 0, opacity: 0 } }
|
||||
UIManager.updateView(node, props, componentStub)
|
||||
assert.equal(node.getAttribute('style'), 'color: red; margin-top: 0px; margin-bottom: 0px; opacity: 0;')
|
||||
})
|
||||
const node = createNode({ color: 'red' });
|
||||
const props = { style: { marginVertical: 0, opacity: 0 } };
|
||||
UIManager.updateView(node, props, componentStub);
|
||||
assert.equal(node.getAttribute('style'), 'color: red; margin-top: 0px; margin-bottom: 0px; opacity: 0;');
|
||||
});
|
||||
|
||||
test('replaces input and textarea text', () => {
|
||||
const node = createNode()
|
||||
node.value = 'initial'
|
||||
const textProp = { text: 'expected-text' }
|
||||
const valueProp = { value: 'expected-value' }
|
||||
const node = createNode();
|
||||
node.value = 'initial';
|
||||
const textProp = { text: 'expected-text' };
|
||||
const valueProp = { value: 'expected-value' };
|
||||
|
||||
UIManager.updateView(node, textProp)
|
||||
assert.equal(node.value, 'expected-text')
|
||||
UIManager.updateView(node, textProp);
|
||||
assert.equal(node.value, 'expected-text');
|
||||
|
||||
UIManager.updateView(node, valueProp)
|
||||
assert.equal(node.value, 'expected-value')
|
||||
})
|
||||
UIManager.updateView(node, valueProp);
|
||||
assert.equal(node.value, 'expected-value');
|
||||
});
|
||||
|
||||
test('sets other attribute values', () => {
|
||||
const node = createNode()
|
||||
const props = { 'aria-level': '4', 'data-of-type': 'string' }
|
||||
UIManager.updateView(node, props)
|
||||
assert.equal(node.getAttribute('aria-level'), '4')
|
||||
assert.equal(node.getAttribute('data-of-type'), 'string')
|
||||
})
|
||||
})
|
||||
})
|
||||
const node = createNode();
|
||||
const props = { 'aria-level': '4', 'data-of-type': 'string' };
|
||||
UIManager.updateView(node, props);
|
||||
assert.equal(node.getAttribute('aria-level'), '4');
|
||||
assert.equal(node.getAttribute('data-of-type'), 'string');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
import createReactStyleObject from '../StyleSheet/createReactStyleObject'
|
||||
import CSSPropertyOperations from 'react/lib/CSSPropertyOperations'
|
||||
import createReactStyleObject from '../StyleSheet/createReactStyleObject';
|
||||
import CSSPropertyOperations from 'react/lib/CSSPropertyOperations';
|
||||
|
||||
const _measureLayout = (node, relativeToNativeNode, callback) => {
|
||||
const relativeNode = relativeToNativeNode || node.parentNode
|
||||
const relativeRect = relativeNode.getBoundingClientRect()
|
||||
const { height, left, top, width } = node.getBoundingClientRect()
|
||||
const x = left - relativeRect.left
|
||||
const y = top - relativeRect.top
|
||||
callback(x, y, width, height, left, top)
|
||||
}
|
||||
const relativeNode = relativeToNativeNode || node.parentNode;
|
||||
const relativeRect = relativeNode.getBoundingClientRect();
|
||||
const { height, left, top, width } = node.getBoundingClientRect();
|
||||
const x = left - relativeRect.left;
|
||||
const y = top - relativeRect.top;
|
||||
callback(x, y, width, height, left, top);
|
||||
};
|
||||
|
||||
const UIManager = {
|
||||
blur(node) {
|
||||
try { node.blur() } catch (err) {}
|
||||
try { node.blur(); } catch (err) {}
|
||||
},
|
||||
|
||||
focus(node) {
|
||||
try { node.focus() } catch (err) {}
|
||||
try { node.focus(); } catch (err) {}
|
||||
},
|
||||
|
||||
measure(node, callback) {
|
||||
_measureLayout(node, null, callback)
|
||||
_measureLayout(node, null, callback);
|
||||
},
|
||||
|
||||
measureInWindow(node, callback) {
|
||||
const { height, left, top, width } = node.getBoundingClientRect()
|
||||
callback(left, top, width, height)
|
||||
const { height, left, top, width } = node.getBoundingClientRect();
|
||||
callback(left, top, width, height);
|
||||
},
|
||||
|
||||
measureLayout(node, relativeToNativeNode, onFail, onSuccess) {
|
||||
const relativeTo = relativeToNativeNode || node.parentNode
|
||||
_measureLayout(node, relativeTo, onSuccess)
|
||||
const relativeTo = relativeToNativeNode || node.parentNode;
|
||||
_measureLayout(node, relativeTo, onSuccess);
|
||||
},
|
||||
|
||||
updateView(node, props, component /* only needed to surpress React errors in development */) {
|
||||
for (const prop in props) {
|
||||
const value = props[prop]
|
||||
const value = props[prop];
|
||||
|
||||
switch (prop) {
|
||||
case 'style':
|
||||
@@ -44,26 +44,26 @@ const UIManager = {
|
||||
node,
|
||||
createReactStyleObject(value),
|
||||
component._reactInternalInstance
|
||||
)
|
||||
break
|
||||
);
|
||||
break;
|
||||
case 'class':
|
||||
case 'className': {
|
||||
const nativeProp = 'class'
|
||||
const nativeProp = 'class';
|
||||
// prevent class names managed by React Native from being replaced
|
||||
const className = node.getAttribute(nativeProp) + ' ' + value
|
||||
node.setAttribute(nativeProp, className)
|
||||
break
|
||||
const className = `${node.getAttribute(nativeProp)} ${value}`;
|
||||
node.setAttribute(nativeProp, className);
|
||||
break;
|
||||
}
|
||||
case 'text':
|
||||
case 'value':
|
||||
// native platforms use `text` prop to replace text input value
|
||||
node.value = value
|
||||
break
|
||||
node.value = value;
|
||||
break;
|
||||
default:
|
||||
node.setAttribute(prop, value)
|
||||
node.setAttribute(prop, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = UIManager
|
||||
module.exports = UIManager;
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
const vibrate = (pattern) => {
|
||||
if ('vibrate' in window.navigator) {
|
||||
if (typeof pattern === 'number' || Array.isArray(pattern)) {
|
||||
window.navigator.vibrate(pattern)
|
||||
window.navigator.vibrate(pattern);
|
||||
} else {
|
||||
throw new Error('Vibration pattern should be a number or array')
|
||||
throw new Error('Vibration pattern should be a number or array');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const Vibration = {
|
||||
cancel() {
|
||||
vibrate(0)
|
||||
vibrate(0);
|
||||
},
|
||||
vibrate(pattern) {
|
||||
vibrate(pattern)
|
||||
vibrate(pattern);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Vibration
|
||||
module.exports = Vibration;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
suite('components/ActivityIndicator', () => {
|
||||
test.skip('NO TEST COVERAGE', () => {})
|
||||
})
|
||||
test.skip('NO TEST COVERAGE', () => {});
|
||||
});
|
||||
|
||||
@@ -1,44 +1,43 @@
|
||||
import Animated from '../../apis/Animated'
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods'
|
||||
import Easing from 'animated/lib/Easing'
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import StyleSheet from '../../apis/StyleSheet'
|
||||
import View from '../View'
|
||||
import Animated from '../../apis/Animated';
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods';
|
||||
import Easing from 'animated/lib/Easing';
|
||||
import StyleSheet from '../../apis/StyleSheet';
|
||||
import View from '../View';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
const GRAY = '#999999'
|
||||
const opacityInterpolation = { inputRange: [ 0, 1 ], outputRange: [ 0.5, 1 ] }
|
||||
const scaleInterpolation = { inputRange: [ 0, 1 ], outputRange: [ 0.95, 1 ] }
|
||||
const rotationInterpolation = { inputRange: [ 0, 1 ], outputRange: [ '0deg', '360deg' ] };
|
||||
|
||||
class ActivityIndicator extends Component {
|
||||
static displayName = 'ActivityIndicator';
|
||||
|
||||
static propTypes = {
|
||||
...View.propTypes,
|
||||
animating: PropTypes.bool,
|
||||
color: PropTypes.string,
|
||||
hidesWhenStopped: PropTypes.bool,
|
||||
size: PropTypes.oneOf([ 'small', 'large' ]),
|
||||
style: View.propTypes.style
|
||||
size: PropTypes.oneOfType([ PropTypes.oneOf([ 'small', 'large' ]), PropTypes.number ])
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
animating: true,
|
||||
color: GRAY,
|
||||
color: '#1976D2',
|
||||
hidesWhenStopped: true,
|
||||
size: 'small',
|
||||
style: {}
|
||||
size: 'small'
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
super(props);
|
||||
this.state = {
|
||||
animation: new Animated.Value(1)
|
||||
}
|
||||
animation: new Animated.Value(0)
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._manageAnimation()
|
||||
this._manageAnimation();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this._manageAnimation()
|
||||
this._manageAnimation();
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -49,53 +48,85 @@ class ActivityIndicator extends Component {
|
||||
size,
|
||||
style,
|
||||
...other
|
||||
} = this.props
|
||||
} = this.props;
|
||||
|
||||
const { animation } = this.state
|
||||
const { animation } = this.state;
|
||||
|
||||
const svg = (
|
||||
<svg height='100%' viewBox='0 0 32 32' width='100%'>
|
||||
<circle
|
||||
cx='16'
|
||||
cy='16'
|
||||
fill='none'
|
||||
r='14'
|
||||
strokeWidth='4'
|
||||
style={{
|
||||
stroke: color,
|
||||
opacity: 0.2
|
||||
}}
|
||||
/>
|
||||
<circle
|
||||
cx='16'
|
||||
cy='16'
|
||||
fill='none'
|
||||
r='14'
|
||||
strokeWidth='4'
|
||||
style={{
|
||||
stroke: color,
|
||||
strokeDasharray: 80,
|
||||
strokeDashoffset: 60
|
||||
}}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
return (
|
||||
<View {...other} style={[ styles.container, style ]}>
|
||||
<View {...other}
|
||||
accessibilityRole='progressbar'
|
||||
aria-valuemax='1'
|
||||
aria-valuemin='0'
|
||||
style={[
|
||||
styles.container,
|
||||
style,
|
||||
size && { height: size, width: size }
|
||||
]}
|
||||
>
|
||||
<Animated.View
|
||||
children={svg}
|
||||
style={[
|
||||
indicatorStyles[size],
|
||||
hidesWhenStopped && !animating && styles.hidesWhenStopped,
|
||||
{
|
||||
borderColor: color,
|
||||
opacity: animation.interpolate(opacityInterpolation),
|
||||
transform: [ { scale: animation.interpolate(scaleInterpolation) } ]
|
||||
transform: [
|
||||
{ rotate: animation.interpolate(rotationInterpolation) }
|
||||
]
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
_manageAnimation() {
|
||||
const { animation } = this.state
|
||||
const { animation } = this.state;
|
||||
|
||||
const cycleAnimation = () => {
|
||||
Animated.sequence([
|
||||
Animated.timing(animation, {
|
||||
duration: 600,
|
||||
easing: Easing.inOut(Easing.ease),
|
||||
toValue: 0
|
||||
}),
|
||||
Animated.timing(animation, {
|
||||
duration: 600,
|
||||
easing: Easing.inOut(Easing.ease),
|
||||
toValue: 1
|
||||
})
|
||||
]).start((event) => {
|
||||
animation.setValue(0);
|
||||
Animated.timing(animation, {
|
||||
duration: 750,
|
||||
easing: Easing.inOut(Easing.linear),
|
||||
toValue: 1
|
||||
}).start((event) => {
|
||||
if (event.finished) {
|
||||
cycleAnimation()
|
||||
cycleAnimation();
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (this.props.animating) {
|
||||
cycleAnimation()
|
||||
cycleAnimation();
|
||||
} else {
|
||||
animation.stopAnimation()
|
||||
animation.stopAnimation();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,21 +139,17 @@ const styles = StyleSheet.create({
|
||||
hidesWhenStopped: {
|
||||
visibility: 'hidden'
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const indicatorStyles = StyleSheet.create({
|
||||
small: {
|
||||
borderRadius: 100,
|
||||
borderWidth: 3,
|
||||
width: 20,
|
||||
height: 20
|
||||
},
|
||||
large: {
|
||||
borderRadius: 100,
|
||||
borderWidth: 4,
|
||||
width: 36,
|
||||
height: 36
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
module.exports = applyNativeMethods(ActivityIndicator)
|
||||
module.exports = applyNativeMethods(ActivityIndicator);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import keyMirror from 'fbjs/lib/keyMirror'
|
||||
import keyMirror from 'fbjs/lib/keyMirror';
|
||||
|
||||
const ImageResizeMode = keyMirror({
|
||||
center: null,
|
||||
@@ -7,6 +7,6 @@ const ImageResizeMode = keyMirror({
|
||||
none: null,
|
||||
repeat: null,
|
||||
stretch: null
|
||||
})
|
||||
});
|
||||
|
||||
module.exports = ImageResizeMode
|
||||
module.exports = ImageResizeMode;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { PropTypes } from 'react'
|
||||
import BorderPropTypes from '../../propTypes/BorderPropTypes'
|
||||
import ColorPropType from '../../propTypes/ColorPropType'
|
||||
import LayoutPropTypes from '../../propTypes/LayoutPropTypes'
|
||||
import TransformPropTypes from '../../propTypes/TransformPropTypes'
|
||||
import ImageResizeMode from './ImageResizeMode'
|
||||
import BorderPropTypes from '../../propTypes/BorderPropTypes';
|
||||
import ColorPropType from '../../propTypes/ColorPropType';
|
||||
import ImageResizeMode from './ImageResizeMode';
|
||||
import LayoutPropTypes from '../../propTypes/LayoutPropTypes';
|
||||
import { PropTypes } from 'react';
|
||||
import TransformPropTypes from '../../propTypes/TransformPropTypes';
|
||||
|
||||
const hiddenOrVisible = PropTypes.oneOf([ 'hidden', 'visible' ])
|
||||
const hiddenOrVisible = PropTypes.oneOf([ 'hidden', 'visible' ]);
|
||||
|
||||
module.exports = {
|
||||
...BorderPropTypes,
|
||||
@@ -24,4 +24,4 @@ module.exports = {
|
||||
* @platform web
|
||||
*/
|
||||
visibility: hiddenOrVisible
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,162 +1,161 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
import { mount, shallow } from 'enzyme'
|
||||
import assert from 'assert'
|
||||
import React from 'react'
|
||||
import StyleSheet from '../../../apis/StyleSheet'
|
||||
|
||||
import Image from '../'
|
||||
import assert from 'assert';
|
||||
import Image from '../';
|
||||
import React from 'react';
|
||||
import StyleSheet from '../../../apis/StyleSheet';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
|
||||
suite('components/Image', () => {
|
||||
test('sets correct accessibility role"', () => {
|
||||
const image = shallow(<Image />)
|
||||
assert.equal(image.prop('accessibilityRole'), 'img')
|
||||
})
|
||||
const image = shallow(<Image />);
|
||||
assert.equal(image.prop('accessibilityRole'), 'img');
|
||||
});
|
||||
|
||||
test('prop "accessibilityLabel"', () => {
|
||||
const accessibilityLabel = 'accessibilityLabel'
|
||||
const image = shallow(<Image accessibilityLabel={accessibilityLabel} />)
|
||||
assert.equal(image.prop('accessibilityLabel'), accessibilityLabel)
|
||||
})
|
||||
const accessibilityLabel = 'accessibilityLabel';
|
||||
const image = shallow(<Image accessibilityLabel={accessibilityLabel} />);
|
||||
assert.equal(image.prop('accessibilityLabel'), accessibilityLabel);
|
||||
});
|
||||
|
||||
test('prop "accessible"', () => {
|
||||
const accessible = false
|
||||
const image = shallow(<Image accessible={accessible} />)
|
||||
assert.equal(image.prop('accessible'), accessible)
|
||||
})
|
||||
const accessible = false;
|
||||
const image = shallow(<Image accessible={accessible} />);
|
||||
assert.equal(image.prop('accessible'), accessible);
|
||||
});
|
||||
|
||||
test('prop "children"', () => {
|
||||
const children = <div className='unique' />
|
||||
const wrapper = shallow(<Image>{children}</Image>)
|
||||
assert.equal(wrapper.contains(children), true)
|
||||
})
|
||||
const children = <div className='unique' />;
|
||||
const wrapper = shallow(<Image>{children}</Image>);
|
||||
assert.equal(wrapper.contains(children), true);
|
||||
});
|
||||
|
||||
suite('prop "defaultSource"', () => {
|
||||
test('sets background image when value is an object', () => {
|
||||
const defaultSource = { uri: 'https://google.com/favicon.ico' }
|
||||
const image = shallow(<Image defaultSource={defaultSource} />)
|
||||
const backgroundImage = StyleSheet.flatten(image.prop('style')).backgroundImage
|
||||
assert(backgroundImage.indexOf(defaultSource.uri) > -1)
|
||||
})
|
||||
const defaultSource = { uri: 'https://google.com/favicon.ico' };
|
||||
const image = shallow(<Image defaultSource={defaultSource} />);
|
||||
const backgroundImage = StyleSheet.flatten(image.prop('style')).backgroundImage;
|
||||
assert(backgroundImage.indexOf(defaultSource.uri) > -1);
|
||||
});
|
||||
|
||||
test('sets background image when value is a string', () => {
|
||||
// emulate require-ed asset
|
||||
const defaultSource = 'https://google.com/favicon.ico'
|
||||
const image = shallow(<Image defaultSource={defaultSource} />)
|
||||
const backgroundImage = StyleSheet.flatten(image.prop('style')).backgroundImage
|
||||
assert(backgroundImage.indexOf(defaultSource) > -1)
|
||||
})
|
||||
})
|
||||
const defaultSource = 'https://google.com/favicon.ico';
|
||||
const image = shallow(<Image defaultSource={defaultSource} />);
|
||||
const backgroundImage = StyleSheet.flatten(image.prop('style')).backgroundImage;
|
||||
assert(backgroundImage.indexOf(defaultSource) > -1);
|
||||
});
|
||||
});
|
||||
|
||||
test('prop "onError"', function (done) {
|
||||
this.timeout(5000)
|
||||
mount(<Image onError={onError} source={{ uri: 'https://google.com/favicon.icox' }} />)
|
||||
this.timeout(5000);
|
||||
mount(<Image onError={onError} source={{ uri: 'https://google.com/favicon.icox' }} />);
|
||||
function onError(e) {
|
||||
assert.equal(e.nativeEvent.type, 'error')
|
||||
done()
|
||||
assert.equal(e.nativeEvent.type, 'error');
|
||||
done();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
test('prop "onLoad"', function (done) {
|
||||
this.timeout(5000)
|
||||
const image = mount(<Image onLoad={onLoad} source={{ uri: 'https://google.com/favicon.ico' }} />)
|
||||
this.timeout(5000);
|
||||
const image = mount(<Image onLoad={onLoad} source={{ uri: 'https://google.com/favicon.ico' }} />);
|
||||
function onLoad(e) {
|
||||
assert.equal(e.nativeEvent.type, 'load')
|
||||
const backgroundImage = StyleSheet.flatten(image.ref('root').prop('style')).backgroundImage
|
||||
assert.notDeepEqual(backgroundImage, undefined)
|
||||
done()
|
||||
assert.equal(e.nativeEvent.type, 'load');
|
||||
const hasBackgroundImage = (image.html()).indexOf('url("https://google.com/favicon.ico")') > -1;
|
||||
assert.equal(hasBackgroundImage, true);
|
||||
done();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
test('prop "onLoadEnd"', function (done) {
|
||||
this.timeout(5000)
|
||||
const image = mount(<Image onLoadEnd={onLoadEnd} source={{ uri: 'https://google.com/favicon.ico' }} />)
|
||||
this.timeout(5000);
|
||||
const image = mount(<Image onLoadEnd={onLoadEnd} source={{ uri: 'https://google.com/favicon.ico' }} />);
|
||||
function onLoadEnd() {
|
||||
assert.ok(true)
|
||||
const backgroundImage = StyleSheet.flatten(image.ref('root').prop('style')).backgroundImage
|
||||
assert.notDeepEqual(backgroundImage, undefined)
|
||||
done()
|
||||
assert.ok(true);
|
||||
const hasBackgroundImage = (image.html()).indexOf('url("https://google.com/favicon.ico")') > -1;
|
||||
assert.equal(hasBackgroundImage, true);
|
||||
done();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
test('prop "onLoadStart"', function (done) {
|
||||
this.timeout(5000)
|
||||
mount(<Image onLoadStart={onLoadStart} source={{ uri: 'https://google.com/favicon.ico' }} />)
|
||||
this.timeout(5000);
|
||||
mount(<Image onLoadStart={onLoadStart} source={{ uri: 'https://google.com/favicon.ico' }} />);
|
||||
function onLoadStart() {
|
||||
assert.ok(true)
|
||||
done()
|
||||
assert.ok(true);
|
||||
done();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
suite('prop "resizeMode"', () => {
|
||||
const getBackgroundSize = (image) => StyleSheet.flatten(image.prop('style')).backgroundSize
|
||||
const getBackgroundSize = (image) => StyleSheet.flatten(image.prop('style')).backgroundSize;
|
||||
|
||||
test('value "contain"', () => {
|
||||
const image = shallow(<Image resizeMode={Image.resizeMode.contain} />)
|
||||
assert.equal(getBackgroundSize(image), 'contain')
|
||||
})
|
||||
const image = shallow(<Image resizeMode={Image.resizeMode.contain} />);
|
||||
assert.equal(getBackgroundSize(image), 'contain');
|
||||
});
|
||||
|
||||
test('value "cover"', () => {
|
||||
const image = shallow(<Image resizeMode={Image.resizeMode.cover} />)
|
||||
assert.equal(getBackgroundSize(image), 'cover')
|
||||
})
|
||||
const image = shallow(<Image resizeMode={Image.resizeMode.cover} />);
|
||||
assert.equal(getBackgroundSize(image), 'cover');
|
||||
});
|
||||
|
||||
test('value "none"', () => {
|
||||
const image = shallow(<Image resizeMode={Image.resizeMode.none} />)
|
||||
assert.equal(getBackgroundSize(image), 'auto')
|
||||
})
|
||||
const image = shallow(<Image resizeMode={Image.resizeMode.none} />);
|
||||
assert.equal(getBackgroundSize(image), 'auto');
|
||||
});
|
||||
|
||||
test('value "stretch"', () => {
|
||||
const image = shallow(<Image resizeMode={Image.resizeMode.stretch} />)
|
||||
assert.equal(getBackgroundSize(image), '100% 100%')
|
||||
})
|
||||
const image = shallow(<Image resizeMode={Image.resizeMode.stretch} />);
|
||||
assert.equal(getBackgroundSize(image), '100% 100%');
|
||||
});
|
||||
|
||||
test('no value', () => {
|
||||
const image = shallow(<Image />)
|
||||
assert.equal(getBackgroundSize(image), 'cover')
|
||||
})
|
||||
})
|
||||
const image = shallow(<Image />);
|
||||
assert.equal(getBackgroundSize(image), 'cover');
|
||||
});
|
||||
});
|
||||
|
||||
suite('prop "source"', function () {
|
||||
this.timeout(5000)
|
||||
this.timeout(5000);
|
||||
|
||||
test('sets background image when value is an object', (done) => {
|
||||
const source = { uri: 'https://google.com/favicon.ico' }
|
||||
mount(<Image onLoad={onLoad} source={source} />)
|
||||
const source = { uri: 'https://google.com/favicon.ico' };
|
||||
mount(<Image onLoad={onLoad} source={source} />);
|
||||
function onLoad(e) {
|
||||
const src = e.nativeEvent.target.src
|
||||
assert.equal(src, source.uri)
|
||||
done()
|
||||
const src = e.nativeEvent.target.src;
|
||||
assert.equal(src, source.uri);
|
||||
done();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
test('sets background image when value is a string', (done) => {
|
||||
// emulate require-ed asset
|
||||
const source = 'https://google.com/favicon.ico'
|
||||
mount(<Image onLoad={onLoad} source={source} />)
|
||||
const source = 'https://google.com/favicon.ico';
|
||||
mount(<Image onLoad={onLoad} source={source} />);
|
||||
function onLoad(e) {
|
||||
const src = e.nativeEvent.target.src
|
||||
assert.equal(src, source)
|
||||
done()
|
||||
const src = e.nativeEvent.target.src;
|
||||
assert.equal(src, source);
|
||||
done();
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
suite('prop "style"', () => {
|
||||
test('converts "resizeMode" property', () => {
|
||||
const image = shallow(<Image style={{ resizeMode: Image.resizeMode.contain }} />)
|
||||
assert.equal(StyleSheet.flatten(image.prop('style')).backgroundSize, 'contain')
|
||||
})
|
||||
const image = shallow(<Image style={{ resizeMode: Image.resizeMode.contain }} />);
|
||||
assert.equal(StyleSheet.flatten(image.prop('style')).backgroundSize, 'contain');
|
||||
});
|
||||
|
||||
test('removes "resizeMode" property', () => {
|
||||
const image = shallow(<Image style={{ resizeMode: Image.resizeMode.contain }} />)
|
||||
assert.equal(StyleSheet.flatten(image.prop('style')).resizeMode, undefined)
|
||||
})
|
||||
})
|
||||
const image = shallow(<Image style={{ resizeMode: Image.resizeMode.contain }} />);
|
||||
assert.equal(StyleSheet.flatten(image.prop('style')).resizeMode, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
test('prop "testID"', () => {
|
||||
const testID = 'testID'
|
||||
const image = shallow(<Image testID={testID} />)
|
||||
assert.equal(image.prop('testID'), testID)
|
||||
})
|
||||
})
|
||||
const testID = 'testID';
|
||||
const image = shallow(<Image testID={testID} />);
|
||||
assert.equal(image.prop('testID'), testID);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
/* global window */
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods'
|
||||
import createReactDOMComponent from '../../modules/createReactDOMComponent'
|
||||
import ImageResizeMode from './ImageResizeMode'
|
||||
import ImageStylePropTypes from './ImageStylePropTypes'
|
||||
import resolveAssetSource from './resolveAssetSource'
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import StyleSheet from '../../apis/StyleSheet'
|
||||
import StyleSheetPropType from '../../propTypes/StyleSheetPropType'
|
||||
import View from '../View'
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods';
|
||||
import BaseComponentPropTypes from '../../propTypes/BaseComponentPropTypes';
|
||||
import createDOMElement from '../../modules/createDOMElement';
|
||||
import ImageResizeMode from './ImageResizeMode';
|
||||
import ImageStylePropTypes from './ImageStylePropTypes';
|
||||
import resolveAssetSource from './resolveAssetSource';
|
||||
import StyleSheet from '../../apis/StyleSheet';
|
||||
import StyleSheetPropType from '../../propTypes/StyleSheetPropType';
|
||||
import View from '../View';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
const STATUS_ERRORED = 'ERRORED'
|
||||
const STATUS_LOADED = 'LOADED'
|
||||
const STATUS_LOADING = 'LOADING'
|
||||
const STATUS_PENDING = 'PENDING'
|
||||
const STATUS_IDLE = 'IDLE'
|
||||
const STATUS_ERRORED = 'ERRORED';
|
||||
const STATUS_LOADED = 'LOADED';
|
||||
const STATUS_LOADING = 'LOADING';
|
||||
const STATUS_PENDING = 'PENDING';
|
||||
const STATUS_IDLE = 'IDLE';
|
||||
|
||||
const ImageSourcePropType = PropTypes.oneOfType([
|
||||
PropTypes.shape({
|
||||
uri: PropTypes.string.isRequired
|
||||
}),
|
||||
PropTypes.string
|
||||
])
|
||||
]);
|
||||
|
||||
class Image extends Component {
|
||||
static displayName = 'Image'
|
||||
static displayName = 'Image';
|
||||
|
||||
static propTypes = {
|
||||
accessibilityLabel: createReactDOMComponent.propTypes.accessibilityLabel,
|
||||
accessible: createReactDOMComponent.propTypes.accessible,
|
||||
...BaseComponentPropTypes,
|
||||
children: PropTypes.any,
|
||||
defaultSource: ImageSourcePropType,
|
||||
onError: PropTypes.func,
|
||||
@@ -35,10 +35,9 @@ class Image extends Component {
|
||||
onLoad: PropTypes.func,
|
||||
onLoadEnd: PropTypes.func,
|
||||
onLoadStart: PropTypes.func,
|
||||
resizeMode: PropTypes.oneOf(['center', 'contain', 'cover', 'none', 'repeat', 'stretch']),
|
||||
resizeMode: PropTypes.oneOf([ 'center', 'contain', 'cover', 'none', 'repeat', 'stretch' ]),
|
||||
source: ImageSourcePropType,
|
||||
style: StyleSheetPropType(ImageStylePropTypes),
|
||||
testID: createReactDOMComponent.propTypes.testID
|
||||
style: StyleSheetPropType(ImageStylePropTypes)
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@@ -49,37 +48,37 @@ class Image extends Component {
|
||||
static resizeMode = ImageResizeMode;
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
const uri = resolveAssetSource(props.source)
|
||||
this._imageState = uri ? STATUS_PENDING : STATUS_IDLE
|
||||
this.state = { isLoaded: false }
|
||||
super(props, context);
|
||||
const uri = resolveAssetSource(props.source);
|
||||
this._imageState = uri ? STATUS_PENDING : STATUS_IDLE;
|
||||
this.state = { isLoaded: false };
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this._imageState === STATUS_PENDING) {
|
||||
this._createImageLoader()
|
||||
this._createImageLoader();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this._imageState === STATUS_PENDING && !this.image) {
|
||||
this._createImageLoader()
|
||||
this._createImageLoader();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const nextUri = resolveAssetSource(nextProps.source)
|
||||
const nextUri = resolveAssetSource(nextProps.source);
|
||||
if (resolveAssetSource(this.props.source) !== nextUri) {
|
||||
this._updateImageState(nextUri ? STATUS_PENDING : STATUS_IDLE)
|
||||
this._updateImageState(nextUri ? STATUS_PENDING : STATUS_IDLE);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._destroyImageLoader()
|
||||
this._destroyImageLoader();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isLoaded } = this.state
|
||||
const { isLoaded } = this.state;
|
||||
const {
|
||||
accessibilityLabel,
|
||||
accessible,
|
||||
@@ -88,16 +87,16 @@ class Image extends Component {
|
||||
onLayout,
|
||||
source,
|
||||
testID
|
||||
} = this.props
|
||||
} = this.props;
|
||||
|
||||
const displayImage = resolveAssetSource(!isLoaded ? defaultSource : source)
|
||||
const backgroundImage = displayImage ? `url("${displayImage}")` : null
|
||||
let style = StyleSheet.flatten(this.props.style)
|
||||
const displayImage = resolveAssetSource(!isLoaded ? defaultSource : source);
|
||||
const backgroundImage = displayImage ? `url("${displayImage}")` : null;
|
||||
let style = StyleSheet.flatten(this.props.style);
|
||||
|
||||
const resizeMode = this.props.resizeMode || style.resizeMode || ImageResizeMode.cover
|
||||
const resizeMode = this.props.resizeMode || style.resizeMode || ImageResizeMode.cover;
|
||||
// remove 'resizeMode' style, as it is not supported by View (N.B. styles are frozen in dev)
|
||||
style = process.env.NODE_ENV !== 'production' ? { ...style } : style
|
||||
delete style.resizeMode
|
||||
style = process.env.NODE_ENV !== 'production' ? { ...style } : style;
|
||||
delete style.resizeMode;
|
||||
|
||||
/**
|
||||
* Image is a non-stretching View. The image is displayed as a background
|
||||
@@ -112,7 +111,6 @@ class Image extends Component {
|
||||
accessibilityRole='img'
|
||||
accessible={accessible}
|
||||
onLayout={onLayout}
|
||||
ref='root'
|
||||
style={[
|
||||
styles.initial,
|
||||
style,
|
||||
@@ -121,75 +119,73 @@ class Image extends Component {
|
||||
]}
|
||||
testID={testID}
|
||||
>
|
||||
{createReactDOMComponent({ component: 'img', src: displayImage, style: styles.img })}
|
||||
{createDOMElement('img', { src: displayImage, style: styles.img })}
|
||||
{children ? (
|
||||
<View children={children} pointerEvents='box-none' style={styles.children} />
|
||||
) : null}
|
||||
</View>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
_createImageLoader() {
|
||||
const uri = resolveAssetSource(this.props.source)
|
||||
const uri = resolveAssetSource(this.props.source);
|
||||
|
||||
this._destroyImageLoader()
|
||||
this.image = new window.Image()
|
||||
this.image.onerror = this._onError
|
||||
this.image.onload = this._onLoad
|
||||
this.image.src = uri
|
||||
this._onLoadStart()
|
||||
this._destroyImageLoader();
|
||||
this.image = new window.Image();
|
||||
this.image.onerror = this._onError;
|
||||
this.image.onload = this._onLoad;
|
||||
this.image.src = uri;
|
||||
this._onLoadStart();
|
||||
}
|
||||
|
||||
_destroyImageLoader() {
|
||||
if (this.image) {
|
||||
this.image.onerror = null
|
||||
this.image.onload = null
|
||||
this.image = null
|
||||
this.image.onerror = null;
|
||||
this.image.onload = null;
|
||||
this.image = null;
|
||||
}
|
||||
}
|
||||
|
||||
_onError = (e) => {
|
||||
const { onError } = this.props
|
||||
const event = { nativeEvent: e }
|
||||
const { onError } = this.props;
|
||||
const event = { nativeEvent: e };
|
||||
|
||||
this._destroyImageLoader()
|
||||
this._updateImageState(STATUS_ERRORED)
|
||||
this._onLoadEnd()
|
||||
if (onError) onError(event)
|
||||
this._destroyImageLoader();
|
||||
this._updateImageState(STATUS_ERRORED);
|
||||
this._onLoadEnd();
|
||||
if (onError) { onError(event); }
|
||||
}
|
||||
|
||||
_onLoad = (e) => {
|
||||
const { onLoad } = this.props
|
||||
const event = { nativeEvent: e }
|
||||
const { onLoad } = this.props;
|
||||
const event = { nativeEvent: e };
|
||||
|
||||
this._destroyImageLoader()
|
||||
this._updateImageState(STATUS_LOADED)
|
||||
if (onLoad) onLoad(event)
|
||||
this._onLoadEnd()
|
||||
this._destroyImageLoader();
|
||||
this._updateImageState(STATUS_LOADED);
|
||||
if (onLoad) { onLoad(event); }
|
||||
this._onLoadEnd();
|
||||
}
|
||||
|
||||
_onLoadEnd() {
|
||||
const { onLoadEnd } = this.props
|
||||
if (onLoadEnd) onLoadEnd()
|
||||
const { onLoadEnd } = this.props;
|
||||
if (onLoadEnd) { onLoadEnd(); }
|
||||
}
|
||||
|
||||
_onLoadStart() {
|
||||
const { onLoadStart } = this.props
|
||||
this._updateImageState(STATUS_LOADING)
|
||||
if (onLoadStart) onLoadStart()
|
||||
const { onLoadStart } = this.props;
|
||||
this._updateImageState(STATUS_LOADING);
|
||||
if (onLoadStart) { onLoadStart(); }
|
||||
}
|
||||
|
||||
_updateImageState(status) {
|
||||
this._imageState = status
|
||||
const isLoaded = this._imageState === STATUS_LOADED
|
||||
this._imageState = status;
|
||||
const isLoaded = this._imageState === STATUS_LOADED;
|
||||
if (isLoaded !== this.state.isLoaded) {
|
||||
this.setState({ isLoaded })
|
||||
this.setState({ isLoaded });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
applyNativeMethods(Image)
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
initial: {
|
||||
alignSelf: 'flex-start',
|
||||
@@ -212,7 +208,7 @@ const styles = StyleSheet.create({
|
||||
right: 0,
|
||||
top: 0
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const resizeModeStyles = StyleSheet.create({
|
||||
center: {
|
||||
@@ -235,6 +231,6 @@ const resizeModeStyles = StyleSheet.create({
|
||||
stretch: {
|
||||
backgroundSize: '100% 100%'
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
module.exports = Image
|
||||
module.exports = applyNativeMethods(Image);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
function resolveAssetSource(source) {
|
||||
return ((typeof source === 'object') ? source.uri : source) || null
|
||||
return ((typeof source === 'object') ? source.uri : source) || null;
|
||||
}
|
||||
|
||||
module.exports = resolveAssetSource
|
||||
module.exports = resolveAssetSource;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PropTypes } from 'react'
|
||||
import ScrollView from '../ScrollView'
|
||||
import ListViewDataSource from './ListViewDataSource'
|
||||
import ListViewDataSource from './ListViewDataSource';
|
||||
import { PropTypes } from 'react';
|
||||
import ScrollView from '../ScrollView';
|
||||
|
||||
export default {
|
||||
...ScrollView.propTypes,
|
||||
@@ -19,4 +19,4 @@ export default {
|
||||
onChangeVisibleRows: PropTypes.func,
|
||||
removeClippedSubviews: PropTypes.bool,
|
||||
stickyHeaderIndices: PropTypes.arrayOf(PropTypes.number)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
suite('components/ListView', () => {
|
||||
test('NO TEST COVERAGE')
|
||||
})
|
||||
test('NO TEST COVERAGE');
|
||||
});
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods'
|
||||
import React, { Component } from 'react'
|
||||
import ScrollView from '../ScrollView'
|
||||
import ListViewDataSource from './ListViewDataSource'
|
||||
import ListViewPropTypes from './ListViewPropTypes'
|
||||
import View from '../View'
|
||||
import pick from 'lodash/pick'
|
||||
|
||||
const SCROLLVIEW_REF = 'listviewscroll'
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods';
|
||||
import ListViewDataSource from './ListViewDataSource';
|
||||
import ListViewPropTypes from './ListViewPropTypes';
|
||||
import pick from 'lodash/pick';
|
||||
import ScrollView from '../ScrollView';
|
||||
import View from '../View';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
class ListView extends Component {
|
||||
static propTypes = ListViewPropTypes;
|
||||
@@ -23,81 +21,83 @@ class ListView extends Component {
|
||||
static DataSource = ListViewDataSource;
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
super(props);
|
||||
this.state = {
|
||||
curRenderedRowsCount: this.props.initialListSize,
|
||||
highlightedRow: {}
|
||||
}
|
||||
this.onRowHighlighted = (sectionId, rowId) => this._onRowHighlighted(sectionId, rowId)
|
||||
};
|
||||
this.onRowHighlighted = (sectionId, rowId) => this._onRowHighlighted(sectionId, rowId);
|
||||
}
|
||||
|
||||
getScrollResponder() {
|
||||
return this.refs[SCROLLVIEW_REF] && this.refs[SCROLLVIEW_REF].getScrollResponder()
|
||||
return this._scrollViewRef && this._scrollViewRef.getScrollResponder();
|
||||
}
|
||||
|
||||
scrollTo(...args) {
|
||||
return this.refs[SCROLLVIEW_REF] && this.refs[SCROLLVIEW_REF].scrollTo(...args)
|
||||
return this._scrollViewRef && this._scrollViewRef.scrollTo(...args);
|
||||
}
|
||||
|
||||
setNativeProps(props) {
|
||||
return this.refs[SCROLLVIEW_REF] && this.refs[SCROLLVIEW_REF].setNativeProps(props)
|
||||
return this._scrollViewRef && this._scrollViewRef.setNativeProps(props);
|
||||
}
|
||||
|
||||
_onRowHighlighted(sectionId, rowId) {
|
||||
this.setState({highlightedRow: {sectionId, rowId}})
|
||||
this.setState({ highlightedRow: { sectionId, rowId } });
|
||||
}
|
||||
|
||||
render() {
|
||||
const dataSource = this.props.dataSource
|
||||
const header = this.props.renderHeader ? this.props.renderHeader() : undefined
|
||||
const footer = this.props.renderFooter ? this.props.renderFooter() : undefined
|
||||
const dataSource = this.props.dataSource;
|
||||
const header = this.props.renderHeader ? this.props.renderHeader() : undefined;
|
||||
const footer = this.props.renderFooter ? this.props.renderFooter() : undefined;
|
||||
|
||||
// render sections and rows
|
||||
const children = []
|
||||
const sections = dataSource.rowIdentities
|
||||
const renderRow = this.props.renderRow
|
||||
const renderSectionHeader = this.props.renderSectionHeader
|
||||
const renderSeparator = this.props.renderSeparator
|
||||
const children = [];
|
||||
const sections = dataSource.rowIdentities;
|
||||
const renderRow = this.props.renderRow;
|
||||
const renderSectionHeader = this.props.renderSectionHeader;
|
||||
const renderSeparator = this.props.renderSeparator;
|
||||
for (let sectionIdx = 0, sectionCnt = sections.length; sectionIdx < sectionCnt; sectionIdx++) {
|
||||
const rows = sections[sectionIdx]
|
||||
const sectionId = dataSource.sectionIdentities[sectionIdx]
|
||||
const rows = sections[sectionIdx];
|
||||
const sectionId = dataSource.sectionIdentities[sectionIdx];
|
||||
|
||||
// render optional section header
|
||||
if (renderSectionHeader) {
|
||||
const section = dataSource.getSectionHeaderData(sectionIdx)
|
||||
const key = 's_' + sectionId
|
||||
const child = <View key={key}>{renderSectionHeader(section, sectionId)}</View>
|
||||
children.push(child)
|
||||
const section = dataSource.getSectionHeaderData(sectionIdx);
|
||||
const key = `s_${sectionId}`;
|
||||
const child = <View key={key}>{renderSectionHeader(section, sectionId)}</View>;
|
||||
children.push(child);
|
||||
}
|
||||
|
||||
// render rows
|
||||
for (let rowIdx = 0, rowCnt = rows.length; rowIdx < rowCnt; rowIdx++) {
|
||||
const rowId = rows[rowIdx]
|
||||
const row = dataSource.getRowData(sectionIdx, rowIdx)
|
||||
const key = 'r_' + sectionId + '_' + rowId
|
||||
const child = <View key={key}>{renderRow(row, sectionId, rowId, this.onRowHighlighted)}</View>
|
||||
children.push(child)
|
||||
const rowId = rows[rowIdx];
|
||||
const row = dataSource.getRowData(sectionIdx, rowIdx);
|
||||
const key = `r_${sectionId}_${rowId}`;
|
||||
const child = <View key={key}>{renderRow(row, sectionId, rowId, this.onRowHighlighted)}</View>;
|
||||
children.push(child);
|
||||
|
||||
// render optional separator
|
||||
if (renderSeparator && ((rowIdx !== rows.length - 1) || (sectionIdx === sections.length - 1))) {
|
||||
const adjacentRowHighlighted =
|
||||
this.state.highlightedRow.sectionID === sectionId && (
|
||||
this.state.highlightedRow.rowID === rowId ||
|
||||
this.state.highlightedRow.rowID === rows[rowIdx + 1])
|
||||
const separator = renderSeparator(sectionId, rowId, adjacentRowHighlighted)
|
||||
children.push(separator)
|
||||
this.state.highlightedRow.rowID === rows[rowIdx + 1]);
|
||||
const separator = renderSeparator(sectionId, rowId, adjacentRowHighlighted);
|
||||
children.push(separator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const props = pick(ScrollView.propTypes, this.props)
|
||||
const props = pick(ScrollView.propTypes, this.props);
|
||||
|
||||
return React.cloneElement(this.props.renderScrollComponent(props), {
|
||||
ref: SCROLLVIEW_REF
|
||||
}, header, children, footer)
|
||||
ref: this._setScrollViewRef
|
||||
}, header, children, footer);
|
||||
}
|
||||
|
||||
_setScrollViewRef(component) {
|
||||
this._scrollViewRef = component;
|
||||
}
|
||||
}
|
||||
|
||||
applyNativeMethods(ListView)
|
||||
|
||||
module.exports = ListView
|
||||
module.exports = applyNativeMethods(ListView);
|
||||
|
||||
20
src/components/ProgressBar/__tests__/index-test.js
Normal file
20
src/components/ProgressBar/__tests__/index-test.js
Normal file
@@ -0,0 +1,20 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
import assert from 'assert';
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import ProgressBar from '..';
|
||||
|
||||
suite('components/ProgressBar', () => {
|
||||
suite('progress', () => {
|
||||
test('value as percentage is set to "aria-valuenow"', () => {
|
||||
const component = shallow(<ProgressBar progress={0.5} />);
|
||||
assert(component.prop('aria-valuenow') === 50);
|
||||
});
|
||||
|
||||
test('is ignored when "indeterminate" is "true"', () => {
|
||||
const component = shallow(<ProgressBar indeterminate progress={0.5} />);
|
||||
assert(component.prop('aria-valuenow') === null);
|
||||
});
|
||||
});
|
||||
});
|
||||
120
src/components/ProgressBar/index.js
Normal file
120
src/components/ProgressBar/index.js
Normal file
@@ -0,0 +1,120 @@
|
||||
import Animated from '../../apis/Animated';
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods';
|
||||
import ColorPropType from '../../propTypes/ColorPropType';
|
||||
import StyleSheet from '../../apis/StyleSheet';
|
||||
import View from '../View';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
const indeterminateWidth = '25%';
|
||||
const translateInterpolation = { inputRange: [ 0, 1 ], outputRange: [ '-100%', '400%' ] };
|
||||
|
||||
class ProgressBar extends Component {
|
||||
static displayName = 'ProgressBar';
|
||||
|
||||
static propTypes = {
|
||||
...View.propTypes,
|
||||
color: ColorPropType,
|
||||
indeterminate: PropTypes.bool,
|
||||
progress: PropTypes.number,
|
||||
trackColor: ColorPropType
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
color: '#1976D2',
|
||||
indeterminate: false,
|
||||
progress: 0,
|
||||
trackColor: 'transparent'
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
animationTranslate: new Animated.Value(0)
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._manageAnimation();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this._manageAnimation();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
color,
|
||||
indeterminate,
|
||||
progress,
|
||||
trackColor,
|
||||
style,
|
||||
...other
|
||||
} = this.props;
|
||||
|
||||
const { animationTranslate } = this.state;
|
||||
|
||||
const percentageProgress = indeterminate ? 50 : progress * 100;
|
||||
|
||||
return (
|
||||
<View {...other}
|
||||
accessibilityRole='progressbar'
|
||||
aria-valuemax='100'
|
||||
aria-valuemin='0'
|
||||
aria-valuenow={indeterminate ? null : percentageProgress}
|
||||
style={[
|
||||
styles.track,
|
||||
style,
|
||||
{ backgroundColor: trackColor }
|
||||
]}
|
||||
>
|
||||
<Animated.View style={[
|
||||
styles.progress,
|
||||
{ backgroundColor: color },
|
||||
indeterminate ? {
|
||||
transform: [
|
||||
{ translateX: animationTranslate.interpolate(translateInterpolation) }
|
||||
],
|
||||
width: indeterminateWidth
|
||||
} : {
|
||||
width: `${percentageProgress}%`
|
||||
}
|
||||
]} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_manageAnimation() {
|
||||
const { animationTranslate } = this.state;
|
||||
|
||||
const cycleAnimation = (animation) => {
|
||||
animation.setValue(0);
|
||||
Animated.timing(animation, {
|
||||
duration: 1000,
|
||||
toValue: 1
|
||||
}).start((event) => {
|
||||
if (event.finished) {
|
||||
cycleAnimation(animation);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (this.props.indeterminate) {
|
||||
cycleAnimation(animationTranslate);
|
||||
} else {
|
||||
animationTranslate.stopAnimation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
track: {
|
||||
height: 5,
|
||||
overflow: 'hidden',
|
||||
userSelect: 'none'
|
||||
},
|
||||
progress: {
|
||||
height: '100%'
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = applyNativeMethods(ProgressBar);
|
||||
@@ -6,9 +6,9 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import debounce from 'lodash/debounce'
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import View from '../View'
|
||||
import debounce from 'lodash/debounce';
|
||||
import View from '../View';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
/**
|
||||
* Encapsulates the Web-specific scroll throttling and disabling logic
|
||||
@@ -32,63 +32,63 @@ export default class ScrollViewBase extends Component {
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this._debouncedOnScrollEnd = debounce(this._handleScrollEnd, 100)
|
||||
this._state = { isScrolling: false }
|
||||
super(props);
|
||||
this._debouncedOnScrollEnd = debounce(this._handleScrollEnd, 100);
|
||||
this._state = { isScrolling: false };
|
||||
}
|
||||
|
||||
_handlePreventableScrollEvent = (handler) => {
|
||||
return (e) => {
|
||||
if (!this.props.scrollEnabled) {
|
||||
e.preventDefault()
|
||||
e.preventDefault();
|
||||
} else {
|
||||
if (handler) handler(e)
|
||||
if (handler) { handler(e); }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
_handleScroll = (e) => {
|
||||
const { scrollEventThrottle } = this.props
|
||||
const { scrollEventThrottle } = this.props;
|
||||
// A scroll happened, so the scroll bumps the debounce.
|
||||
this._debouncedOnScrollEnd(e)
|
||||
this._debouncedOnScrollEnd(e);
|
||||
if (this._state.isScrolling) {
|
||||
// Scroll last tick may have changed, check if we need to notify
|
||||
if (this._shouldEmitScrollEvent(this._state.scrollLastTick, scrollEventThrottle)) {
|
||||
this._handleScrollTick(e)
|
||||
this._handleScrollTick(e);
|
||||
}
|
||||
} else {
|
||||
// Weren't scrolling, so we must have just started
|
||||
this._handleScrollStart(e)
|
||||
this._handleScrollStart(e);
|
||||
}
|
||||
}
|
||||
|
||||
_handleScrollStart(e) {
|
||||
this._state.isScrolling = true
|
||||
this._state.scrollLastTick = Date.now()
|
||||
this._state.isScrolling = true;
|
||||
this._state.scrollLastTick = Date.now();
|
||||
}
|
||||
|
||||
_handleScrollTick(e) {
|
||||
const { onScroll } = this.props
|
||||
this._state.scrollLastTick = Date.now()
|
||||
if (onScroll) onScroll(e)
|
||||
const { onScroll } = this.props;
|
||||
this._state.scrollLastTick = Date.now();
|
||||
if (onScroll) { onScroll(e); }
|
||||
}
|
||||
|
||||
_handleScrollEnd(e) {
|
||||
const { onScroll } = this.props
|
||||
this._state.isScrolling = false
|
||||
if (onScroll) onScroll(e)
|
||||
const { onScroll } = this.props;
|
||||
this._state.isScrolling = false;
|
||||
if (onScroll) { onScroll(e); }
|
||||
}
|
||||
|
||||
_shouldEmitScrollEvent(lastTick, eventThrottle) {
|
||||
const timeSinceLastTick = Date.now() - lastTick
|
||||
return (eventThrottle > 0 && timeSinceLastTick >= (1000 / eventThrottle))
|
||||
const timeSinceLastTick = Date.now() - lastTick;
|
||||
return (eventThrottle > 0 && timeSinceLastTick >= (1000 / eventThrottle));
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
onMomentumScrollBegin, onMomentumScrollEnd, onScrollBeginDrag, onScrollEndDrag, scrollEnabled, scrollEventThrottle, // eslint-disable-line
|
||||
...other
|
||||
} = this.props
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<View
|
||||
@@ -97,6 +97,6 @@ export default class ScrollViewBase extends Component {
|
||||
onTouchMove={this._handlePreventableScrollEvent(this.props.onTouchMove)}
|
||||
onWheel={this._handlePreventableScrollEvent(this.props.onWheel)}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
suite('components/ScrollView', () => {
|
||||
test('NO TEST COVERAGE')
|
||||
})
|
||||
test('NO TEST COVERAGE');
|
||||
});
|
||||
|
||||
@@ -6,20 +6,18 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import dismissKeyboard from '../../modules/dismissKeyboard'
|
||||
import invariant from 'fbjs/lib/invariant'
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import ScrollResponder from '../../modules/ScrollResponder'
|
||||
import ScrollViewBase from './ScrollViewBase'
|
||||
import StyleSheet from '../../apis/StyleSheet'
|
||||
import StyleSheetPropType from '../../propTypes/StyleSheetPropType'
|
||||
import View from '../View'
|
||||
import ViewStylePropTypes from '../View/ViewStylePropTypes'
|
||||
|
||||
const INNERVIEW = 'InnerScrollView'
|
||||
const SCROLLVIEW = 'ScrollView'
|
||||
import dismissKeyboard from '../../modules/dismissKeyboard';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ScrollResponder from '../../modules/ScrollResponder';
|
||||
import ScrollViewBase from './ScrollViewBase';
|
||||
import StyleSheet from '../../apis/StyleSheet';
|
||||
import StyleSheetPropType from '../../propTypes/StyleSheetPropType';
|
||||
import View from '../View';
|
||||
import ViewStylePropTypes from '../View/ViewStylePropTypes';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
/* eslint-disable react/prefer-es6-class */
|
||||
const ScrollView = React.createClass({
|
||||
propTypes: {
|
||||
...View.propTypes,
|
||||
@@ -35,14 +33,14 @@ const ScrollView = React.createClass({
|
||||
style: StyleSheetPropType(ViewStylePropTypes)
|
||||
},
|
||||
|
||||
mixins: [ScrollResponder.Mixin],
|
||||
mixins: [ ScrollResponder.Mixin ],
|
||||
|
||||
getInitialState() {
|
||||
return this.scrollResponderMixinGetInitialState()
|
||||
return this.scrollResponderMixinGetInitialState();
|
||||
},
|
||||
|
||||
setNativeProps(props: Object) {
|
||||
this.refs[SCROLLVIEW].setNativeProps(props)
|
||||
this._scrollViewRef.setNativeProps(props);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -52,15 +50,15 @@ const ScrollView = React.createClass({
|
||||
* to the underlying scroll responder's methods.
|
||||
*/
|
||||
getScrollResponder(): Component {
|
||||
return this
|
||||
return this;
|
||||
},
|
||||
|
||||
getScrollableNode(): any {
|
||||
return ReactDOM.findDOMNode(this.refs[SCROLLVIEW])
|
||||
return ReactDOM.findDOMNode(this._scrollViewRef);
|
||||
},
|
||||
|
||||
getInnerViewNode(): any {
|
||||
return ReactDOM.findDOMNode(this.refs[INNERVIEW])
|
||||
return ReactDOM.findDOMNode(this._innerViewRef);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -79,45 +77,20 @@ const ScrollView = React.createClass({
|
||||
animated?: boolean
|
||||
) {
|
||||
if (typeof y === 'number') {
|
||||
console.warn('`scrollTo(y, x, animated)` is deprecated. Use `scrollTo({x: 5, y: 5, animated: true})` instead.')
|
||||
console.warn('`scrollTo(y, x, animated)` is deprecated. Use `scrollTo({x: 5, y: 5, animated: true})` instead.');
|
||||
} else {
|
||||
({x, y, animated} = y || {})
|
||||
({ x, y, animated } = y || {});
|
||||
}
|
||||
|
||||
this.getScrollResponder().scrollResponderScrollTo({x: x || 0, y: y || 0, animated: animated !== false})
|
||||
this.getScrollResponder().scrollResponderScrollTo({ x: x || 0, y: y || 0, animated: animated !== false });
|
||||
},
|
||||
|
||||
/**
|
||||
* Deprecated, do not use.
|
||||
*/
|
||||
scrollWithoutAnimationTo(y: number = 0, x: number = 0) {
|
||||
console.warn('`scrollWithoutAnimationTo` is deprecated. Use `scrollTo` instead')
|
||||
this.scrollTo({x, y, animated: false})
|
||||
},
|
||||
|
||||
handleScroll(e: Object) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (this.props.onScroll && !this.props.scrollEventThrottle) {
|
||||
console.log(
|
||||
'You specified `onScroll` on a <ScrollView> but not ' +
|
||||
'`scrollEventThrottle`. You will only receive one event. ' +
|
||||
'Using `16` you get all the events but be aware that it may ' +
|
||||
'cause frame drops, use a bigger number if you don\'t need as ' +
|
||||
'much precision.'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (this.props.keyboardDismissMode === 'on-drag') {
|
||||
dismissKeyboard()
|
||||
}
|
||||
|
||||
this.scrollResponderHandleScroll(e)
|
||||
},
|
||||
|
||||
_handleContentOnLayout(e: Object) {
|
||||
const { width, height } = e.nativeEvent.layout
|
||||
this.props.onContentSizeChange(width, height)
|
||||
console.warn('`scrollWithoutAnimationTo` is deprecated. Use `scrollTo` instead');
|
||||
this.scrollTo({ x, y, animated: false });
|
||||
},
|
||||
|
||||
render() {
|
||||
@@ -129,23 +102,23 @@ const ScrollView = React.createClass({
|
||||
onScroll, // eslint-disable-line
|
||||
refreshControl,
|
||||
...other
|
||||
} = this.props
|
||||
} = this.props;
|
||||
|
||||
if (process.env.NODE_ENV !== 'production' && this.props.style) {
|
||||
const style = StyleSheet.flatten(this.props.style)
|
||||
const childLayoutProps = ['alignItems', 'justifyContent'].filter((prop) => style && style[prop] !== undefined)
|
||||
const style = StyleSheet.flatten(this.props.style);
|
||||
const childLayoutProps = [ 'alignItems', 'justifyContent' ].filter((prop) => style && style[prop] !== undefined);
|
||||
invariant(
|
||||
childLayoutProps.length === 0,
|
||||
'ScrollView child layout (' + JSON.stringify(childLayoutProps) +
|
||||
') must be applied through the contentContainerStyle prop.'
|
||||
)
|
||||
`ScrollView child layout (${JSON.stringify(childLayoutProps)}) ` +
|
||||
'must be applied through the contentContainerStyle prop.'
|
||||
);
|
||||
}
|
||||
|
||||
let contentSizeChangeProps = {}
|
||||
let contentSizeChangeProps = {};
|
||||
if (onContentSizeChange) {
|
||||
contentSizeChangeProps = {
|
||||
onLayout: this._handleContentOnLayout
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const contentContainer = (
|
||||
@@ -153,14 +126,14 @@ const ScrollView = React.createClass({
|
||||
{...contentSizeChangeProps}
|
||||
children={this.props.children}
|
||||
collapsable={false}
|
||||
ref={INNERVIEW}
|
||||
ref={this._setInnerViewRef}
|
||||
style={[
|
||||
styles.contentContainer,
|
||||
horizontal && styles.contentContainerHorizontal,
|
||||
contentContainerStyle
|
||||
]}
|
||||
/>
|
||||
)
|
||||
);
|
||||
|
||||
const props = {
|
||||
...other,
|
||||
@@ -179,55 +152,90 @@ const ScrollView = React.createClass({
|
||||
onStartShouldSetResponder: this.scrollResponderHandleStartShouldSetResponder,
|
||||
onStartShouldSetResponderCapture: this.scrollResponderHandleStartShouldSetResponderCapture,
|
||||
onScrollShouldSetResponder: this.scrollResponderHandleScrollShouldSetResponder,
|
||||
onScroll: this.handleScroll,
|
||||
onScroll: this._handleScroll,
|
||||
onResponderGrant: this.scrollResponderHandleResponderGrant,
|
||||
onResponderTerminationRequest: this.scrollResponderHandleTerminationRequest,
|
||||
onResponderTerminate: this.scrollResponderHandleTerminate,
|
||||
onResponderRelease: this.scrollResponderHandleResponderRelease,
|
||||
onResponderReject: this.scrollResponderHandleResponderReject
|
||||
}
|
||||
};
|
||||
|
||||
const ScrollViewClass = ScrollViewBase
|
||||
const ScrollViewClass = ScrollViewBase;
|
||||
|
||||
invariant(
|
||||
ScrollViewClass !== undefined,
|
||||
'ScrollViewClass must not be undefined'
|
||||
)
|
||||
);
|
||||
|
||||
if (refreshControl) {
|
||||
return React.cloneElement(
|
||||
refreshControl,
|
||||
{ style: props.style },
|
||||
<ScrollViewClass {...props} ref={SCROLLVIEW} style={styles.base}>
|
||||
<ScrollViewClass {...props} ref={this._setScrollViewRef} style={styles.base}>
|
||||
{contentContainer}
|
||||
</ScrollViewClass>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollViewClass {...props} ref={SCROLLVIEW} style={props.style}>
|
||||
<ScrollViewClass {...props} ref={this._setScrollViewRef} style={props.style}>
|
||||
{contentContainer}
|
||||
</ScrollViewClass>
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
_handleContentOnLayout(e: Object) {
|
||||
const { width, height } = e.nativeEvent.layout;
|
||||
this.props.onContentSizeChange(width, height);
|
||||
},
|
||||
|
||||
_handleScroll(e: Object) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (this.props.onScroll && !this.props.scrollEventThrottle) {
|
||||
console.log(
|
||||
'You specified `onScroll` on a <ScrollView> but not ' +
|
||||
'`scrollEventThrottle`. You will only receive one event. ' +
|
||||
'Using `16` you get all the events but be aware that it may ' +
|
||||
'cause frame drops, use a bigger number if you don\'t need as ' +
|
||||
'much precision.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.props.keyboardDismissMode === 'on-drag') {
|
||||
dismissKeyboard();
|
||||
}
|
||||
|
||||
this.scrollResponderHandleScroll(e);
|
||||
},
|
||||
|
||||
_setInnerViewRef(component) {
|
||||
this._innerViewRef = component;
|
||||
},
|
||||
|
||||
_setScrollViewRef(component) {
|
||||
this._scrollViewRef = component;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
base: {
|
||||
flex: 1,
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'auto'
|
||||
overflowY: 'auto',
|
||||
WebkitOverflowScrolling: 'touch'
|
||||
},
|
||||
baseHorizontal: {
|
||||
flexDirection: 'row',
|
||||
overflowX: 'auto',
|
||||
overflowY: 'hidden'
|
||||
},
|
||||
contentContainer: {
|
||||
flex: 1
|
||||
flexGrow: 1
|
||||
},
|
||||
contentContainerHorizontal: {
|
||||
flexDirection: 'row'
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
module.exports = ScrollView
|
||||
module.exports = ScrollView;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
suite('components/StaticContainer', () => {
|
||||
test.skip('NO TEST COVERAGE', () => {})
|
||||
})
|
||||
test.skip('NO TEST COVERAGE', () => {});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
/**
|
||||
* Renders static content efficiently by allowing React to short-circuit the
|
||||
@@ -30,13 +30,13 @@ class StaticContainer extends Component {
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps: { shouldUpdate: boolean }): boolean {
|
||||
return nextProps.shouldUpdate
|
||||
return nextProps.shouldUpdate;
|
||||
}
|
||||
|
||||
render() {
|
||||
const child = this.props.children
|
||||
return (child === null || child === false) ? null : React.Children.only(child)
|
||||
const child = this.props.children;
|
||||
return (child === null || child === false) ? null : React.Children.only(child);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = StaticContainer
|
||||
module.exports = StaticContainer;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
suite('components/StaticRenderer', () => {
|
||||
test.skip('NO TEST COVERAGE', () => {})
|
||||
})
|
||||
test.skip('NO TEST COVERAGE', () => {});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import { Component, PropTypes } from 'react'
|
||||
import { Component, PropTypes } from 'react';
|
||||
|
||||
/**
|
||||
* Renders static content efficiently by allowing React to short-circuit the
|
||||
@@ -29,12 +29,12 @@ class StaticRenderer extends Component {
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps: { shouldUpdate: boolean }): boolean {
|
||||
return nextProps.shouldUpdate
|
||||
return nextProps.shouldUpdate;
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.props.render()
|
||||
return this.props.render();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = StaticRenderer
|
||||
module.exports = StaticRenderer;
|
||||
|
||||
46
src/components/Switch/__tests__/index-test.js
Normal file
46
src/components/Switch/__tests__/index-test.js
Normal file
@@ -0,0 +1,46 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
import assert from 'assert';
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import Switch from '..';
|
||||
|
||||
suite('components/Switch', () => {
|
||||
suite('disabled', () => {
|
||||
test('when "false" a default checkbox is rendered', () => {
|
||||
const component = shallow(<Switch />);
|
||||
assert(component.find('input').length === 1);
|
||||
});
|
||||
|
||||
test('when "true" a disabled checkbox is rendered', () => {
|
||||
const component = shallow(<Switch disabled />);
|
||||
assert(component.find('input').prop('disabled') === true);
|
||||
});
|
||||
});
|
||||
|
||||
suite('onValueChange', () => {
|
||||
test('when value is "false" it receives "true"', () => {
|
||||
const handleValueChange = (value) => assert(value === true);
|
||||
const component = shallow(<Switch onValueChange={handleValueChange} value={false} />);
|
||||
component.find('input').simulate('click');
|
||||
});
|
||||
|
||||
test('when value is "true" it receives "false"', () => {
|
||||
const handleValueChange = (value) => assert(value === false);
|
||||
const component = shallow(<Switch onValueChange={handleValueChange} value />);
|
||||
component.find('input').simulate('click');
|
||||
});
|
||||
});
|
||||
|
||||
suite('value', () => {
|
||||
test('when "false" an unchecked checkbox is rendered', () => {
|
||||
const component = shallow(<Switch value={false} />);
|
||||
assert(component.find('input').prop('checked') === false);
|
||||
});
|
||||
|
||||
test('when "true" a checked checkbox is rendered', () => {
|
||||
const component = shallow(<Switch value />);
|
||||
assert(component.find('input').prop('checked') === true);
|
||||
});
|
||||
});
|
||||
});
|
||||
178
src/components/Switch/index.js
Normal file
178
src/components/Switch/index.js
Normal file
@@ -0,0 +1,178 @@
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods';
|
||||
import ColorPropType from '../../propTypes/ColorPropType';
|
||||
import createDOMElement from '../../modules/createDOMElement';
|
||||
import multiplyStyleLengthValue from '../../modules/multiplyStyleLengthValue';
|
||||
import StyleSheet from '../../apis/StyleSheet';
|
||||
import UIManager from '../../apis/UIManager';
|
||||
import View from '../View';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
const thumbDefaultBoxShadow = '0px 1px 3px rgba(0,0,0,0.5)';
|
||||
const thumbFocusedBoxShadow = `${thumbDefaultBoxShadow}, 0 0 0 10px rgba(0,0,0,0.1)`;
|
||||
|
||||
class Switch extends Component {
|
||||
static displayName = 'Switch';
|
||||
|
||||
static propTypes = {
|
||||
...View.propTypes,
|
||||
activeThumbColor: ColorPropType,
|
||||
activeTrackColor: ColorPropType,
|
||||
disabled: PropTypes.bool,
|
||||
onValueChange: PropTypes.func,
|
||||
thumbColor: ColorPropType,
|
||||
trackColor: ColorPropType,
|
||||
value: PropTypes.bool
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
activeThumbColor: '#009688',
|
||||
activeTrackColor: '#A3D3CF',
|
||||
disabled: false,
|
||||
style: {},
|
||||
thumbColor: '#FAFAFA',
|
||||
trackColor: '#939393',
|
||||
value: false
|
||||
};
|
||||
|
||||
blur() {
|
||||
UIManager.blur(this._checkbox);
|
||||
}
|
||||
|
||||
focus() {
|
||||
UIManager.focus(this._checkbox);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
activeThumbColor,
|
||||
activeTrackColor,
|
||||
disabled,
|
||||
onValueChange, // eslint-disable-line
|
||||
style,
|
||||
thumbColor,
|
||||
trackColor,
|
||||
value,
|
||||
// remove any iOS-only props
|
||||
onTintColor, // eslint-disable-line
|
||||
thumbTintColor, // eslint-disable-line
|
||||
tintColor, // eslint-disable-line
|
||||
...other
|
||||
} = this.props;
|
||||
|
||||
const { height: styleHeight, width: styleWidth } = StyleSheet.flatten(style);
|
||||
const height = styleHeight || 20;
|
||||
const minWidth = multiplyStyleLengthValue(height, 2);
|
||||
const width = styleWidth > minWidth ? styleWidth : minWidth;
|
||||
const trackBorderRadius = multiplyStyleLengthValue(height, 0.5);
|
||||
const trackCurrentColor = value ? activeTrackColor : trackColor;
|
||||
const thumbCurrentColor = value ? activeThumbColor : thumbColor;
|
||||
const thumbHeight = height;
|
||||
const thumbWidth = thumbHeight;
|
||||
|
||||
const rootStyle = [
|
||||
styles.root,
|
||||
style,
|
||||
{ height, width },
|
||||
disabled && styles.cursorDefault
|
||||
];
|
||||
|
||||
const trackStyle = [
|
||||
styles.track,
|
||||
{
|
||||
backgroundColor: trackCurrentColor,
|
||||
borderRadius: trackBorderRadius
|
||||
},
|
||||
disabled && styles.disabledTrack
|
||||
];
|
||||
|
||||
const thumbStyle = [
|
||||
styles.thumb,
|
||||
{
|
||||
alignSelf: value ? 'flex-end' : 'flex-start',
|
||||
backgroundColor: thumbCurrentColor,
|
||||
height: thumbHeight,
|
||||
width: thumbWidth
|
||||
},
|
||||
disabled && styles.disabledThumb
|
||||
];
|
||||
|
||||
const nativeControl = createDOMElement('input', {
|
||||
checked: value,
|
||||
disabled: disabled,
|
||||
onBlur: this._handleFocusState,
|
||||
onChange: this._handleChange,
|
||||
onFocus: this._handleFocusState,
|
||||
ref: this._setCheckboxRef,
|
||||
style: [ styles.nativeControl, styles.cursorInherit ],
|
||||
type: 'checkbox'
|
||||
});
|
||||
|
||||
return (
|
||||
<View {...other} style={rootStyle}>
|
||||
<View style={trackStyle} />
|
||||
<View ref={this._setThumbRef} style={thumbStyle} />
|
||||
{nativeControl}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_handleChange = (event: Object) => {
|
||||
const { onValueChange } = this.props;
|
||||
onValueChange && onValueChange(event.nativeEvent.target.checked);
|
||||
}
|
||||
|
||||
_handleFocusState = (event: Object) => {
|
||||
const isFocused = event.nativeEvent.type === 'focus';
|
||||
const boxShadow = isFocused ? thumbFocusedBoxShadow : thumbDefaultBoxShadow;
|
||||
this._thumb.setNativeProps({ style: { boxShadow } });
|
||||
}
|
||||
|
||||
_setCheckboxRef = (component) => {
|
||||
this._checkbox = component;
|
||||
}
|
||||
|
||||
_setThumbRef = (component) => {
|
||||
this._thumb = component;
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
cursor: 'pointer',
|
||||
userSelect: 'none'
|
||||
},
|
||||
cursorDefault: {
|
||||
cursor: 'default'
|
||||
},
|
||||
cursorInherit: {
|
||||
cursor: 'inherit'
|
||||
},
|
||||
track: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
height: '70%',
|
||||
margin: 'auto',
|
||||
transition: 'background-color 0.1s',
|
||||
width: '90%'
|
||||
},
|
||||
disabledTrack: {
|
||||
backgroundColor: '#D5D5D5'
|
||||
},
|
||||
thumb: {
|
||||
borderRadius: '100%',
|
||||
boxShadow: thumbDefaultBoxShadow,
|
||||
transition: 'background-color 0.1s'
|
||||
},
|
||||
disabledThumb: {
|
||||
backgroundColor: '#BDBDBD'
|
||||
},
|
||||
nativeControl: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
height: '100%',
|
||||
margin: 0,
|
||||
opacity: 0,
|
||||
padding: 0,
|
||||
width: '100%'
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = applyNativeMethods(Switch);
|
||||
@@ -1,7 +1,7 @@
|
||||
import TextPropTypes from '../../propTypes/TextPropTypes'
|
||||
import ViewStylePropTypes from '../View/ViewStylePropTypes'
|
||||
import TextPropTypes from '../../propTypes/TextPropTypes';
|
||||
import ViewStylePropTypes from '../View/ViewStylePropTypes';
|
||||
|
||||
module.exports = {
|
||||
...ViewStylePropTypes,
|
||||
...TextPropTypes
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
import assert from 'assert'
|
||||
import React from 'react'
|
||||
import Text from '../'
|
||||
import { mount, shallow } from 'enzyme'
|
||||
import assert from 'assert';
|
||||
import React from 'react';
|
||||
import Text from '../';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
|
||||
suite('components/Text', () => {
|
||||
test('prop "children"', () => {
|
||||
const children = 'children'
|
||||
const text = shallow(<Text>{children}</Text>)
|
||||
assert.equal(text.prop('children'), children)
|
||||
})
|
||||
const children = 'children';
|
||||
const text = shallow(<Text>{children}</Text>);
|
||||
assert.equal(text.prop('children'), children);
|
||||
});
|
||||
|
||||
test('prop "numberOfLines"')
|
||||
test('prop "numberOfLines"');
|
||||
|
||||
test('prop "onLayout"', (done) => {
|
||||
mount(<Text onLayout={onLayout} />)
|
||||
mount(<Text onLayout={onLayout} />);
|
||||
function onLayout(e) {
|
||||
const { layout } = e.nativeEvent
|
||||
assert.deepEqual(layout, { x: 0, y: 0, width: 0, height: 0 })
|
||||
done()
|
||||
const { layout } = e.nativeEvent;
|
||||
assert.deepEqual(layout, { x: 0, y: 0, width: 0, height: 0 });
|
||||
done();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
test('prop "onPress"', (done) => {
|
||||
const text = mount(<Text onPress={onPress} />)
|
||||
text.simulate('click')
|
||||
const text = mount(<Text onPress={onPress} />);
|
||||
text.simulate('click');
|
||||
function onPress(e) {
|
||||
assert.ok(e.nativeEvent)
|
||||
done()
|
||||
assert.ok(e.nativeEvent);
|
||||
done();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
test('prop "selectable"', () => {
|
||||
let text = shallow(<Text />)
|
||||
assert.equal(text.prop('style').userSelect, undefined)
|
||||
text = shallow(<Text selectable={false} />)
|
||||
assert.equal(text.prop('style').userSelect, 'none')
|
||||
})
|
||||
})
|
||||
let text = shallow(<Text />);
|
||||
assert.equal(text.prop('style').userSelect, undefined);
|
||||
text = shallow(<Text selectable={false} />);
|
||||
assert.equal(text.prop('style').userSelect, 'none');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
import applyLayout from '../../modules/applyLayout'
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods'
|
||||
import createReactDOMComponent from '../../modules/createReactDOMComponent'
|
||||
import { Component, PropTypes } from 'react'
|
||||
import StyleSheet from '../../apis/StyleSheet'
|
||||
import StyleSheetPropType from '../../propTypes/StyleSheetPropType'
|
||||
import TextStylePropTypes from './TextStylePropTypes'
|
||||
import applyLayout from '../../modules/applyLayout';
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods';
|
||||
import BaseComponentPropTypes from '../../propTypes/BaseComponentPropTypes';
|
||||
import createDOMElement from '../../modules/createDOMElement';
|
||||
import StyleSheet from '../../apis/StyleSheet';
|
||||
import StyleSheetPropType from '../../propTypes/StyleSheetPropType';
|
||||
import TextStylePropTypes from './TextStylePropTypes';
|
||||
import { Component, PropTypes } from 'react';
|
||||
|
||||
class Text extends Component {
|
||||
static displayName = 'Text'
|
||||
static displayName = 'Text';
|
||||
|
||||
static propTypes = {
|
||||
accessibilityLabel: createReactDOMComponent.propTypes.accessibilityLabel,
|
||||
...BaseComponentPropTypes,
|
||||
accessibilityRole: PropTypes.oneOf([ 'heading', 'link' ]),
|
||||
accessible: createReactDOMComponent.propTypes.accessible,
|
||||
children: PropTypes.any,
|
||||
numberOfLines: PropTypes.number,
|
||||
onLayout: PropTypes.func,
|
||||
onPress: PropTypes.func,
|
||||
selectable: PropTypes.bool,
|
||||
style: StyleSheetPropType(TextStylePropTypes),
|
||||
testID: createReactDOMComponent.propTypes.testID
|
||||
style: StyleSheetPropType(TextStylePropTypes)
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@@ -35,11 +34,10 @@ class Text extends Component {
|
||||
selectable,
|
||||
style,
|
||||
...other
|
||||
} = this.props
|
||||
} = this.props;
|
||||
|
||||
return createReactDOMComponent({
|
||||
return createDOMElement('span', {
|
||||
...other,
|
||||
component: 'span',
|
||||
onClick: this._onPress,
|
||||
style: [
|
||||
styles.initial,
|
||||
@@ -47,16 +45,14 @@ class Text extends Component {
|
||||
!selectable && styles.notSelectable,
|
||||
numberOfLines === 1 && styles.singleLineStyle
|
||||
]
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
_onPress = (e) => {
|
||||
if (this.props.onPress) this.props.onPress(e)
|
||||
if (this.props.onPress) { this.props.onPress(e); }
|
||||
}
|
||||
}
|
||||
|
||||
applyLayout(applyNativeMethods(Text))
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
initial: {
|
||||
color: 'inherit',
|
||||
@@ -76,6 +72,6 @@ const styles = StyleSheet.create({
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap'
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
module.exports = Text
|
||||
module.exports = applyLayout(applyNativeMethods(Text));
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import UIManager from '../../apis/UIManager'
|
||||
import UIManager from '../../apis/UIManager';
|
||||
|
||||
/**
|
||||
* This class is responsible for coordinating the "focused"
|
||||
@@ -24,7 +24,7 @@ const TextInputState = {
|
||||
* If no text field is focused it returns null
|
||||
*/
|
||||
currentlyFocusedField(): ?Object {
|
||||
return this._currentlyFocusedNode
|
||||
return this._currentlyFocusedNode;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -34,8 +34,8 @@ const TextInputState = {
|
||||
*/
|
||||
focusTextInput(textFieldNode: ?Object) {
|
||||
if (this._currentlyFocusedNode !== textFieldNode && textFieldNode !== null) {
|
||||
this._currentlyFocusedNode = textFieldNode
|
||||
UIManager.focus(textFieldNode)
|
||||
this._currentlyFocusedNode = textFieldNode;
|
||||
UIManager.focus(textFieldNode);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -46,10 +46,10 @@ const TextInputState = {
|
||||
*/
|
||||
blurTextInput(textFieldNode: ?Object) {
|
||||
if (this._currentlyFocusedNode === textFieldNode && textFieldNode !== null) {
|
||||
this._currentlyFocusedNode = null
|
||||
UIManager.blur(textFieldNode)
|
||||
this._currentlyFocusedNode = null;
|
||||
UIManager.blur(textFieldNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = TextInputState
|
||||
module.exports = TextInputState;
|
||||
|
||||
@@ -1,104 +1,104 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
import assert from 'assert'
|
||||
import React from 'react'
|
||||
import StyleSheet from '../../../apis/StyleSheet'
|
||||
import TextareaAutosize from 'react-textarea-autosize'
|
||||
import TextInput from '..'
|
||||
import { mount, shallow } from 'enzyme'
|
||||
import assert from 'assert';
|
||||
import React from 'react';
|
||||
import StyleSheet from '../../../apis/StyleSheet';
|
||||
import TextareaAutosize from 'react-textarea-autosize';
|
||||
import TextInput from '..';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
|
||||
const placeholderText = 'placeholderText'
|
||||
const findNativeInput = (wrapper) => wrapper.find('input')
|
||||
const findNativeTextarea = (wrapper) => wrapper.find(TextareaAutosize)
|
||||
const findPlaceholder = (wrapper) => wrapper.find({ children: placeholderText })
|
||||
const placeholderText = 'placeholderText';
|
||||
const findNativeInput = (wrapper) => wrapper.find('input');
|
||||
const findNativeTextarea = (wrapper) => wrapper.find(TextareaAutosize);
|
||||
const findPlaceholder = (wrapper) => wrapper.find({ children: placeholderText });
|
||||
|
||||
const testIfDocumentIsFocused = (message, fn) => {
|
||||
if (document.hasFocus && document.hasFocus()) {
|
||||
test(message, fn)
|
||||
test(message, fn);
|
||||
} else {
|
||||
test.skip(`${message} – document is not focused`)
|
||||
test.skip(`${message} – document is not focused`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
suite('components/TextInput', () => {
|
||||
test('prop "autoComplete"', () => {
|
||||
// off
|
||||
let input = findNativeInput(shallow(<TextInput />))
|
||||
assert.equal(input.prop('autoComplete'), undefined)
|
||||
let input = findNativeInput(shallow(<TextInput />));
|
||||
assert.equal(input.prop('autoComplete'), undefined);
|
||||
// on
|
||||
input = findNativeInput(shallow(<TextInput autoComplete />))
|
||||
assert.equal(input.prop('autoComplete'), 'on')
|
||||
})
|
||||
input = findNativeInput(shallow(<TextInput autoComplete />));
|
||||
assert.equal(input.prop('autoComplete'), 'on');
|
||||
});
|
||||
|
||||
test('prop "autoFocus"', () => {
|
||||
// false
|
||||
let input = findNativeInput(mount(<TextInput />))
|
||||
assert.equal(input.prop('autoFocus'), undefined)
|
||||
let input = findNativeInput(mount(<TextInput />));
|
||||
assert.equal(input.prop('autoFocus'), undefined);
|
||||
// true
|
||||
input = findNativeInput(mount(<TextInput autoFocus />))
|
||||
assert.equal(input.prop('autoFocus'), true)
|
||||
})
|
||||
input = findNativeInput(mount(<TextInput autoFocus />));
|
||||
assert.equal(input.prop('autoFocus'), true);
|
||||
});
|
||||
|
||||
testIfDocumentIsFocused('prop "clearTextOnFocus"', () => {
|
||||
const defaultValue = 'defaultValue'
|
||||
const defaultValue = 'defaultValue';
|
||||
// false
|
||||
let input = findNativeInput(mount(<TextInput defaultValue={defaultValue} />))
|
||||
input.simulate('focus')
|
||||
assert.equal(input.node.value, defaultValue)
|
||||
let input = findNativeInput(mount(<TextInput defaultValue={defaultValue} />));
|
||||
input.simulate('focus');
|
||||
assert.equal(input.node.value, defaultValue);
|
||||
// true
|
||||
input = findNativeInput(mount(<TextInput clearTextOnFocus defaultValue={defaultValue} />))
|
||||
input.simulate('focus')
|
||||
assert.equal(input.node.value, '')
|
||||
})
|
||||
input = findNativeInput(mount(<TextInput clearTextOnFocus defaultValue={defaultValue} />));
|
||||
input.simulate('focus');
|
||||
assert.equal(input.node.value, '');
|
||||
});
|
||||
|
||||
test('prop "defaultValue"', () => {
|
||||
const defaultValue = 'defaultValue'
|
||||
const input = findNativeInput(shallow(<TextInput defaultValue={defaultValue} />))
|
||||
assert.equal(input.prop('defaultValue'), defaultValue)
|
||||
})
|
||||
const defaultValue = 'defaultValue';
|
||||
const input = findNativeInput(shallow(<TextInput defaultValue={defaultValue} />));
|
||||
assert.equal(input.prop('defaultValue'), defaultValue);
|
||||
});
|
||||
|
||||
test('prop "editable"', () => {
|
||||
// true
|
||||
let input = findNativeInput(shallow(<TextInput />))
|
||||
assert.equal(input.prop('readOnly'), false)
|
||||
let input = findNativeInput(shallow(<TextInput />));
|
||||
assert.equal(input.prop('readOnly'), false);
|
||||
// false
|
||||
input = findNativeInput(shallow(<TextInput editable={false} />))
|
||||
assert.equal(input.prop('readOnly'), true)
|
||||
})
|
||||
input = findNativeInput(shallow(<TextInput editable={false} />));
|
||||
assert.equal(input.prop('readOnly'), true);
|
||||
});
|
||||
|
||||
test('prop "keyboardType"', () => {
|
||||
// default
|
||||
let input = findNativeInput(shallow(<TextInput />))
|
||||
assert.equal(input.prop('type'), undefined)
|
||||
input = findNativeInput(shallow(<TextInput keyboardType='default' />))
|
||||
assert.equal(input.prop('type'), undefined)
|
||||
let input = findNativeInput(shallow(<TextInput />));
|
||||
assert.equal(input.prop('type'), 'text');
|
||||
input = findNativeInput(shallow(<TextInput keyboardType='default' />));
|
||||
assert.equal(input.prop('type'), 'text');
|
||||
// email-address
|
||||
input = findNativeInput(shallow(<TextInput keyboardType='email-address' />))
|
||||
assert.equal(input.prop('type'), 'email')
|
||||
input = findNativeInput(shallow(<TextInput keyboardType='email-address' />));
|
||||
assert.equal(input.prop('type'), 'email');
|
||||
// numeric
|
||||
input = findNativeInput(shallow(<TextInput keyboardType='numeric' />))
|
||||
assert.equal(input.prop('type'), 'number')
|
||||
input = findNativeInput(shallow(<TextInput keyboardType='numeric' />));
|
||||
assert.equal(input.prop('type'), 'number');
|
||||
// phone-pad
|
||||
input = findNativeInput(shallow(<TextInput keyboardType='phone-pad' />))
|
||||
assert.equal(input.prop('type'), 'tel')
|
||||
input = findNativeInput(shallow(<TextInput keyboardType='phone-pad' />));
|
||||
assert.equal(input.prop('type'), 'tel');
|
||||
// url
|
||||
input = findNativeInput(shallow(<TextInput keyboardType='url' />))
|
||||
assert.equal(input.prop('type'), 'url')
|
||||
})
|
||||
input = findNativeInput(shallow(<TextInput keyboardType='url' />));
|
||||
assert.equal(input.prop('type'), 'url');
|
||||
});
|
||||
|
||||
test('prop "maxLength"', () => {
|
||||
let input = findNativeInput(shallow(<TextInput />))
|
||||
assert.equal(input.prop('maxLength'), undefined)
|
||||
input = findNativeInput(shallow(<TextInput maxLength={10} />))
|
||||
assert.equal(input.prop('maxLength'), '10')
|
||||
})
|
||||
let input = findNativeInput(shallow(<TextInput />));
|
||||
assert.equal(input.prop('maxLength'), undefined);
|
||||
input = findNativeInput(shallow(<TextInput maxLength={10} />));
|
||||
assert.equal(input.prop('maxLength'), '10');
|
||||
});
|
||||
|
||||
test('prop "maxNumberOfLines"', () => {
|
||||
const generateValue = () => {
|
||||
let str = ''
|
||||
while (str.length < 100) str += 'x'
|
||||
return str
|
||||
}
|
||||
let str = '';
|
||||
while (str.length < 100) { str += 'x'; }
|
||||
return str;
|
||||
};
|
||||
|
||||
const input = findNativeTextarea(shallow(
|
||||
<TextInput
|
||||
@@ -106,122 +106,124 @@ suite('components/TextInput', () => {
|
||||
multiline
|
||||
value={generateValue()}
|
||||
/>
|
||||
))
|
||||
assert.equal(input.prop('maxRows'), 3)
|
||||
})
|
||||
));
|
||||
assert.equal(input.prop('maxRows'), 3);
|
||||
});
|
||||
|
||||
test('prop "multiline"', () => {
|
||||
// false
|
||||
let input = findNativeInput(shallow(<TextInput />))
|
||||
assert.equal(input.length, 1)
|
||||
let input = findNativeInput(shallow(<TextInput />));
|
||||
assert.equal(input.length, 1);
|
||||
// true
|
||||
input = findNativeTextarea(shallow(<TextInput multiline />))
|
||||
assert.equal(input.length, 1)
|
||||
})
|
||||
input = findNativeTextarea(shallow(<TextInput multiline />));
|
||||
assert.equal(input.length, 1);
|
||||
});
|
||||
|
||||
test('prop "numberOfLines"', () => {
|
||||
// missing multiline
|
||||
let input = findNativeInput(shallow(<TextInput numberOfLines={2} />))
|
||||
assert.equal(input.length, 1)
|
||||
let input = findNativeInput(shallow(<TextInput numberOfLines={2} />));
|
||||
assert.equal(input.length, 1);
|
||||
// with multiline
|
||||
input = findNativeTextarea(shallow(<TextInput multiline numberOfLines={2} />))
|
||||
assert.equal(input.length, 1)
|
||||
input = findNativeTextarea(shallow(<TextInput multiline numberOfLines={2} />));
|
||||
assert.equal(input.length, 1);
|
||||
|
||||
input = findNativeTextarea(shallow(
|
||||
<TextInput
|
||||
multiline
|
||||
numberOfLines={3}
|
||||
/>
|
||||
))
|
||||
assert.equal(input.prop('minRows'), 3)
|
||||
})
|
||||
));
|
||||
assert.equal(input.prop('minRows'), 3);
|
||||
});
|
||||
|
||||
test('prop "onBlur"', (done) => {
|
||||
const input = findNativeInput(mount(<TextInput onBlur={onBlur} />))
|
||||
input.simulate('blur')
|
||||
const input = findNativeInput(mount(<TextInput onBlur={onBlur} />));
|
||||
input.simulate('blur');
|
||||
function onBlur(e) {
|
||||
assert.ok(e)
|
||||
done()
|
||||
assert.ok(e);
|
||||
done();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
test('prop "onChange"', (done) => {
|
||||
const input = findNativeInput(mount(<TextInput onChange={onChange} />))
|
||||
input.simulate('change')
|
||||
const input = findNativeInput(mount(<TextInput onChange={onChange} />));
|
||||
input.simulate('change');
|
||||
function onChange(e) {
|
||||
assert.ok(e)
|
||||
done()
|
||||
assert.ok(e);
|
||||
done();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
test('prop "onChangeText"', (done) => {
|
||||
const newText = 'newText'
|
||||
const input = findNativeInput(mount(<TextInput onChangeText={onChangeText} />))
|
||||
input.simulate('change', { target: { value: newText } })
|
||||
const newText = 'newText';
|
||||
const input = findNativeInput(mount(<TextInput onChangeText={onChangeText} />));
|
||||
input.simulate('change', { target: { value: newText } });
|
||||
function onChangeText(text) {
|
||||
assert.equal(text, newText)
|
||||
done()
|
||||
assert.equal(text, newText);
|
||||
done();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
test('prop "onFocus"', (done) => {
|
||||
const input = findNativeInput(mount(<TextInput onFocus={onFocus} />))
|
||||
input.simulate('focus')
|
||||
const input = findNativeInput(mount(<TextInput onFocus={onFocus} />));
|
||||
input.simulate('focus');
|
||||
function onFocus(e) {
|
||||
assert.ok(e)
|
||||
done()
|
||||
assert.ok(e);
|
||||
done();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
test('prop "onLayout"')
|
||||
test('prop "onLayout"');
|
||||
|
||||
test('prop "onSelectionChange"', (done) => {
|
||||
const input = findNativeInput(mount(<TextInput defaultValue='12345' onSelectionChange={onSelectionChange} />))
|
||||
input.simulate('select', { target: { selectionStart: 0, selectionEnd: 3 } })
|
||||
const input = findNativeInput(mount(<TextInput defaultValue='12345' onSelectionChange={onSelectionChange} />));
|
||||
input.simulate('select', { target: { selectionStart: 0, selectionEnd: 3 } });
|
||||
function onSelectionChange(e) {
|
||||
assert.equal(e.selectionEnd, 3)
|
||||
assert.equal(e.selectionStart, 0)
|
||||
done()
|
||||
assert.equal(e.selectionEnd, 3);
|
||||
assert.equal(e.selectionStart, 0);
|
||||
done();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
test('prop "placeholder"', () => {
|
||||
let textInput = shallow(<TextInput />)
|
||||
assert.equal(findPlaceholder(textInput).length, 0)
|
||||
let textInput = shallow(<TextInput />);
|
||||
assert.equal(findPlaceholder(textInput).length, 0);
|
||||
|
||||
textInput = shallow(<TextInput placeholder={placeholderText} />)
|
||||
assert.equal(findPlaceholder(textInput).length, 1)
|
||||
})
|
||||
textInput = shallow(<TextInput placeholder={placeholderText} />);
|
||||
assert.equal(findPlaceholder(textInput).length, 1);
|
||||
});
|
||||
|
||||
test('prop "placeholderTextColor"', () => {
|
||||
let placeholderElement = findPlaceholder(shallow(<TextInput placeholder={placeholderText} />))
|
||||
assert.equal(StyleSheet.flatten(placeholderElement.prop('style')).color, 'darkgray')
|
||||
let placeholderElement = findPlaceholder(shallow(<TextInput placeholder={placeholderText} />));
|
||||
assert.equal(StyleSheet.flatten(placeholderElement.prop('style')).color, 'darkgray');
|
||||
|
||||
placeholderElement = findPlaceholder(shallow(<TextInput placeholder={placeholderText} placeholderTextColor='red' />))
|
||||
assert.equal(StyleSheet.flatten(placeholderElement.prop('style')).color, 'red')
|
||||
})
|
||||
placeholderElement = findPlaceholder(
|
||||
shallow(<TextInput placeholder={placeholderText} placeholderTextColor='red' />)
|
||||
);
|
||||
assert.equal(StyleSheet.flatten(placeholderElement.prop('style')).color, 'red');
|
||||
});
|
||||
|
||||
test('prop "secureTextEntry"', () => {
|
||||
let input = findNativeInput(shallow(<TextInput secureTextEntry />))
|
||||
assert.equal(input.prop('type'), 'password')
|
||||
let input = findNativeInput(shallow(<TextInput secureTextEntry />));
|
||||
assert.equal(input.prop('type'), 'password');
|
||||
// ignored for multiline
|
||||
input = findNativeTextarea(shallow(<TextInput multiline secureTextEntry />))
|
||||
assert.equal(input.prop('type'), undefined)
|
||||
})
|
||||
input = findNativeTextarea(shallow(<TextInput multiline secureTextEntry />));
|
||||
assert.equal(input.prop('type'), undefined);
|
||||
});
|
||||
|
||||
testIfDocumentIsFocused('prop "selectTextOnFocus"', () => {
|
||||
const text = 'Text'
|
||||
const text = 'Text';
|
||||
// false
|
||||
let input = findNativeInput(mount(<TextInput defaultValue={text} />))
|
||||
input.node.focus()
|
||||
assert.equal(input.node.selectionEnd, 4)
|
||||
assert.equal(input.node.selectionStart, 4)
|
||||
let input = findNativeInput(mount(<TextInput defaultValue={text} />));
|
||||
input.node.focus();
|
||||
assert.equal(input.node.selectionEnd, 4);
|
||||
assert.equal(input.node.selectionStart, 4);
|
||||
// true
|
||||
// input = findNativeInput(mount(<TextInput defaultValue={text} selectTextOnFocus />))
|
||||
input = findNativeInput(mount(<TextInput defaultValue={text} selectTextOnFocus />));
|
||||
// input.node.focus()
|
||||
// assert.equal(input.node.selectionEnd, 4)
|
||||
// assert.equal(input.node.selectionStart, 0)
|
||||
})
|
||||
});
|
||||
|
||||
test('prop "style"', () => {
|
||||
const styles = StyleSheet.create({
|
||||
@@ -229,16 +231,17 @@ suite('components/TextInput', () => {
|
||||
borderWidth: 1,
|
||||
textAlign: 'center'
|
||||
}
|
||||
})
|
||||
const textInput = shallow(<TextInput style={styles.root} />)
|
||||
const input = findNativeInput(textInput)
|
||||
assert.equal(StyleSheet.flatten(textInput.prop('style')).borderWidth, 1, 'expected View styles to be applied to the "View"')
|
||||
assert.equal(input.prop('style').textAlign, 'center', 'expected Text styles to be applied to the "input"')
|
||||
})
|
||||
});
|
||||
const textInput = shallow(<TextInput style={styles.root} />);
|
||||
const input = findNativeInput(textInput);
|
||||
const borderWidth = StyleSheet.flatten(textInput.prop('style')).borderWidth;
|
||||
assert.equal(borderWidth, 1, 'expected View styles to be applied to the "View"');
|
||||
assert.equal(input.prop('style').textAlign, 'center', 'expected Text styles to be applied to the "input"');
|
||||
});
|
||||
|
||||
test('prop "value"', () => {
|
||||
const value = 'value'
|
||||
const input = findNativeInput(shallow(<TextInput value={value} />))
|
||||
assert.equal(input.prop('value'), value)
|
||||
})
|
||||
})
|
||||
const value = 'value';
|
||||
const input = findNativeInput(shallow(<TextInput value={value} />));
|
||||
assert.equal(input.prop('value'), value);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods'
|
||||
import createReactDOMComponent from '../../modules/createReactDOMComponent'
|
||||
import omit from 'lodash/omit'
|
||||
import pick from 'lodash/pick'
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import StyleSheet from '../../apis/StyleSheet'
|
||||
import Text from '../Text'
|
||||
import TextareaAutosize from 'react-textarea-autosize'
|
||||
import TextInputState from './TextInputState'
|
||||
import UIManager from '../../apis/UIManager'
|
||||
import View from '../View'
|
||||
import ViewStylePropTypes from '../View/ViewStylePropTypes'
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods';
|
||||
import createDOMElement from '../../modules/createDOMElement';
|
||||
import omit from 'lodash/omit';
|
||||
import pick from 'lodash/pick';
|
||||
import ReactDOM from 'react-dom';
|
||||
import StyleSheet from '../../apis/StyleSheet';
|
||||
import Text from '../Text';
|
||||
import TextareaAutosize from 'react-textarea-autosize';
|
||||
import TextInputState from './TextInputState';
|
||||
import UIManager from '../../apis/UIManager';
|
||||
import View from '../View';
|
||||
import ViewStylePropTypes from '../View/ViewStylePropTypes';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
const viewStyleProps = Object.keys(ViewStylePropTypes)
|
||||
const viewStyleProps = Object.keys(ViewStylePropTypes);
|
||||
|
||||
class TextInput extends Component {
|
||||
static displayName = 'TextInput';
|
||||
|
||||
static propTypes = {
|
||||
...View.propTypes,
|
||||
autoComplete: PropTypes.bool,
|
||||
@@ -22,7 +24,9 @@ class TextInput extends Component {
|
||||
clearTextOnFocus: PropTypes.bool,
|
||||
defaultValue: PropTypes.string,
|
||||
editable: PropTypes.bool,
|
||||
keyboardType: PropTypes.oneOf(['default', 'email-address', 'numeric', 'phone-pad', 'url']),
|
||||
keyboardType: PropTypes.oneOf([
|
||||
'default', 'email-address', 'numeric', 'phone-pad', 'search', 'url', 'web-search'
|
||||
]),
|
||||
maxLength: PropTypes.number,
|
||||
maxNumberOfLines: PropTypes.number,
|
||||
multiline: PropTypes.bool,
|
||||
@@ -51,24 +55,24 @@ class TextInput extends Component {
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
this.state = { showPlaceholder: !props.value && !props.defaultValue }
|
||||
super(props, context);
|
||||
this.state = { showPlaceholder: !props.value && !props.defaultValue };
|
||||
}
|
||||
|
||||
blur() {
|
||||
TextInputState.blurTextInput(ReactDOM.findDOMNode(this.refs.input))
|
||||
TextInputState.blurTextInput(ReactDOM.findDOMNode(this._inputRef));
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.setNativeProps({ text: '' })
|
||||
this.setNativeProps({ text: '' });
|
||||
}
|
||||
|
||||
focus() {
|
||||
TextInputState.focusTextInput(ReactDOM.findDOMNode(this.refs.input))
|
||||
TextInputState.focusTextInput(ReactDOM.findDOMNode(this._inputRef));
|
||||
}
|
||||
|
||||
setNativeProps(props) {
|
||||
UIManager.updateView(this.refs.input, props, this)
|
||||
UIManager.updateView(this._inputRef, props, this);
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -91,39 +95,41 @@ class TextInput extends Component {
|
||||
style,
|
||||
testID,
|
||||
value
|
||||
} = this.props
|
||||
} = this.props;
|
||||
|
||||
let type
|
||||
let type;
|
||||
|
||||
switch (keyboardType) {
|
||||
case 'email-address':
|
||||
type = 'email'
|
||||
break
|
||||
type = 'email';
|
||||
break;
|
||||
case 'numeric':
|
||||
type = 'number'
|
||||
break
|
||||
type = 'number';
|
||||
break;
|
||||
case 'phone-pad':
|
||||
type = 'tel'
|
||||
break
|
||||
type = 'tel';
|
||||
break;
|
||||
case 'search':
|
||||
case 'web-search':
|
||||
type = 'search'
|
||||
break
|
||||
type = 'search';
|
||||
break;
|
||||
case 'url':
|
||||
type = 'url'
|
||||
break
|
||||
type = 'url';
|
||||
break;
|
||||
default:
|
||||
type = 'text';
|
||||
}
|
||||
|
||||
if (secureTextEntry) {
|
||||
type = 'password'
|
||||
type = 'password';
|
||||
}
|
||||
|
||||
// In order to support 'Text' styles on 'TextInput', we split the 'Text'
|
||||
// and 'View' styles and apply them to the 'Text' and 'View' components
|
||||
// used in the implementation
|
||||
const flattenedStyle = StyleSheet.flatten(style)
|
||||
const rootStyles = pick(flattenedStyle, viewStyleProps)
|
||||
const textStyles = omit(flattenedStyle, viewStyleProps)
|
||||
const flattenedStyle = StyleSheet.flatten(style);
|
||||
const rootStyles = pick(flattenedStyle, viewStyleProps);
|
||||
const textStyles = omit(flattenedStyle, viewStyleProps);
|
||||
|
||||
const propsCommon = {
|
||||
autoComplete: autoComplete && 'on',
|
||||
@@ -135,24 +141,24 @@ class TextInput extends Component {
|
||||
onFocus: this._handleFocus,
|
||||
onSelect: onSelectionChange && this._handleSelectionChange,
|
||||
readOnly: !editable,
|
||||
ref: this._setInputRef,
|
||||
style: [ styles.input, textStyles, { outline: style.outline } ],
|
||||
value
|
||||
}
|
||||
};
|
||||
|
||||
const propsMultiline = {
|
||||
...propsCommon,
|
||||
component: TextareaAutosize,
|
||||
maxRows: maxNumberOfLines || numberOfLines,
|
||||
minRows: numberOfLines
|
||||
}
|
||||
};
|
||||
|
||||
const propsSingleline = {
|
||||
...propsCommon,
|
||||
component: 'input',
|
||||
type
|
||||
}
|
||||
};
|
||||
|
||||
const props = multiline ? propsMultiline : propsSingleline
|
||||
const component = multiline ? TextareaAutosize : 'input';
|
||||
const props = multiline ? propsMultiline : propsSingleline;
|
||||
|
||||
const optionalPlaceholder = placeholder && this.state.showPlaceholder && (
|
||||
<View pointerEvents='none' style={styles.placeholder}>
|
||||
@@ -165,7 +171,7 @@ class TextInput extends Component {
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<View
|
||||
@@ -176,64 +182,68 @@ class TextInput extends Component {
|
||||
testID={testID}
|
||||
>
|
||||
<View style={styles.wrapper}>
|
||||
{createReactDOMComponent({ ...props, ref: 'input' })}
|
||||
{createDOMElement(component, props)}
|
||||
{optionalPlaceholder}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
_handleBlur = (e) => {
|
||||
const { onBlur } = this.props
|
||||
const text = e.target.value
|
||||
this.setState({ showPlaceholder: text === '' })
|
||||
this.blur()
|
||||
if (onBlur) onBlur(e)
|
||||
const { onBlur } = this.props;
|
||||
const text = e.target.value;
|
||||
this.setState({ showPlaceholder: text === '' });
|
||||
this.blur();
|
||||
if (onBlur) { onBlur(e); }
|
||||
}
|
||||
|
||||
_handleChange = (e) => {
|
||||
const { onChange, onChangeText } = this.props
|
||||
const text = e.target.value
|
||||
this.setState({ showPlaceholder: text === '' })
|
||||
if (onChange) onChange(e)
|
||||
if (onChangeText) onChangeText(text)
|
||||
if (!this.refs.input) {
|
||||
const { onChange, onChangeText } = this.props;
|
||||
const text = e.target.value;
|
||||
this.setState({ showPlaceholder: text === '' });
|
||||
if (onChange) { onChange(e); }
|
||||
if (onChangeText) { onChangeText(text); }
|
||||
if (!this._inputRef) {
|
||||
// calling `this.props.onChange` or `this.props.onChangeText`
|
||||
// may clean up the input itself. Exits here.
|
||||
return
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_handleClick = (e) => {
|
||||
this.focus()
|
||||
this.focus();
|
||||
}
|
||||
|
||||
_handleFocus = (e) => {
|
||||
const { clearTextOnFocus, onFocus, selectTextOnFocus } = this.props
|
||||
const node = ReactDOM.findDOMNode(this.refs.input)
|
||||
const text = e.target.value
|
||||
if (onFocus) onFocus(e)
|
||||
if (clearTextOnFocus) this.clear()
|
||||
if (selectTextOnFocus) node.select()
|
||||
this.setState({ showPlaceholder: text === '' })
|
||||
const { clearTextOnFocus, onFocus, selectTextOnFocus } = this.props;
|
||||
const node = ReactDOM.findDOMNode(this._inputRef);
|
||||
const text = e.target.value;
|
||||
if (onFocus) { onFocus(e); }
|
||||
if (clearTextOnFocus) { this.clear(); }
|
||||
if (selectTextOnFocus) { node.select(); }
|
||||
this.setState({ showPlaceholder: text === '' });
|
||||
}
|
||||
|
||||
_handleSelectionChange = (e) => {
|
||||
const { onSelectionChange } = this.props
|
||||
const { onSelectionChange } = this.props;
|
||||
try {
|
||||
const { selectionDirection, selectionEnd, selectionStart } = e.target
|
||||
const { selectionDirection, selectionEnd, selectionStart } = e.target;
|
||||
const event = {
|
||||
selectionDirection,
|
||||
selectionEnd,
|
||||
selectionStart,
|
||||
nativeEvent: e.nativeEvent
|
||||
}
|
||||
if (onSelectionChange) onSelectionChange(event)
|
||||
};
|
||||
if (onSelectionChange) { onSelectionChange(event); }
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
_setInputRef = (component) => {
|
||||
this._inputRef = component;
|
||||
}
|
||||
}
|
||||
|
||||
applyNativeMethods(TextInput)
|
||||
applyNativeMethods(TextInput);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
initial: {
|
||||
@@ -267,6 +277,6 @@ const styles = StyleSheet.create({
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'pre'
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
module.exports = TextInput
|
||||
module.exports = TextInput;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
suite('components/Touchable', () => {
|
||||
test.skip('NO TEST COVERAGE', () => {})
|
||||
})
|
||||
test.skip('NO TEST COVERAGE', () => {});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { PropTypes } from 'react'
|
||||
import BorderPropTypes from '../../propTypes/BorderPropTypes'
|
||||
import ColorPropType from '../../propTypes/ColorPropType'
|
||||
import LayoutPropTypes from '../../propTypes/LayoutPropTypes'
|
||||
import TransformPropTypes from '../../propTypes/TransformPropTypes'
|
||||
import BorderPropTypes from '../../propTypes/BorderPropTypes';
|
||||
import ColorPropType from '../../propTypes/ColorPropType';
|
||||
import LayoutPropTypes from '../../propTypes/LayoutPropTypes';
|
||||
import { PropTypes } from 'react';
|
||||
import TransformPropTypes from '../../propTypes/TransformPropTypes';
|
||||
|
||||
const { number, oneOf, string } = PropTypes
|
||||
const autoOrHiddenOrVisible = oneOf([ 'auto', 'hidden', 'visible' ])
|
||||
const hiddenOrVisible = oneOf([ 'hidden', 'visible' ])
|
||||
const { number, oneOf, string } = PropTypes;
|
||||
const autoOrHiddenOrVisible = oneOf([ 'auto', 'hidden', 'visible' ]);
|
||||
const hiddenOrVisible = oneOf([ 'hidden', 'visible' ]);
|
||||
|
||||
module.exports = {
|
||||
...BorderPropTypes,
|
||||
@@ -16,6 +16,7 @@ module.exports = {
|
||||
backgroundColor: ColorPropType,
|
||||
opacity: number,
|
||||
overflow: autoOrHiddenOrVisible,
|
||||
zIndex: number,
|
||||
/*
|
||||
* @platform web
|
||||
*/
|
||||
@@ -34,5 +35,5 @@ module.exports = {
|
||||
transition: string,
|
||||
userSelect: string,
|
||||
visibility: hiddenOrVisible,
|
||||
zIndex: number
|
||||
}
|
||||
WebkitOverflowScrolling: oneOf([ 'auto', 'touch' ])
|
||||
};
|
||||
|
||||
@@ -1,55 +1,55 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
import assert from 'assert'
|
||||
import includes from 'lodash/includes'
|
||||
import React from 'react'
|
||||
import View from '../'
|
||||
import { mount, shallow } from 'enzyme'
|
||||
import assert from 'assert';
|
||||
import includes from 'lodash/includes';
|
||||
import React from 'react';
|
||||
import View from '../';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
|
||||
suite('components/View', () => {
|
||||
suite('rendered element', () => {
|
||||
test('is a "div" by default', () => {
|
||||
const view = shallow(<View />)
|
||||
assert.equal(view.is('div'), true)
|
||||
})
|
||||
const view = shallow(<View />);
|
||||
assert.equal(view.is('div'), true);
|
||||
});
|
||||
|
||||
test('is a "span" when inside <View accessibilityRole="button" />', () => {
|
||||
const view = mount(<View accessibilityRole='button'><View /></View>)
|
||||
assert.equal(view.find('span').length, 1)
|
||||
})
|
||||
})
|
||||
const view = mount(<View accessibilityRole='button'><View /></View>);
|
||||
assert.equal(view.find('span').length, 1);
|
||||
});
|
||||
});
|
||||
|
||||
test('prop "children"', () => {
|
||||
const children = <View testID='1' />
|
||||
const view = shallow(<View>{children}</View>)
|
||||
assert.equal(view.prop('children'), children)
|
||||
})
|
||||
const children = <View testID='1' />;
|
||||
const view = shallow(<View>{children}</View>);
|
||||
assert.equal(view.prop('children'), children);
|
||||
});
|
||||
|
||||
test('prop "onLayout"', (done) => {
|
||||
mount(<View onLayout={onLayout} />)
|
||||
mount(<View onLayout={onLayout} />);
|
||||
function onLayout(e) {
|
||||
const { layout } = e.nativeEvent
|
||||
assert.deepEqual(layout, { x: 0, y: 0, width: 0, height: 0 })
|
||||
done()
|
||||
const { layout } = e.nativeEvent;
|
||||
assert.deepEqual(layout, { x: 0, y: 0, width: 0, height: 0 });
|
||||
done();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
test('prop "pointerEvents"', () => {
|
||||
const view = shallow(<View pointerEvents='box-only' />)
|
||||
assert.ok(includes(view.prop('className'), '__style_pebo') === true)
|
||||
})
|
||||
const view = shallow(<View pointerEvents='box-only' />);
|
||||
assert.ok(includes(view.prop('className'), '__style_pebo') === true);
|
||||
});
|
||||
|
||||
test('prop "style"', () => {
|
||||
const view = shallow(<View />)
|
||||
assert.equal(view.prop('style').flexShrink, 0)
|
||||
const view = shallow(<View />);
|
||||
assert.equal(view.prop('style').flexShrink, 0);
|
||||
|
||||
const flexView = shallow(<View style={{ flex: 1 }} />)
|
||||
assert.equal(flexView.prop('style').flexShrink, 1)
|
||||
const flexView = shallow(<View style={{ flex: 1 }} />);
|
||||
assert.equal(flexView.prop('style').flexShrink, 1);
|
||||
|
||||
const flexShrinkView = shallow(<View style={{ flexShrink: 1 }} />)
|
||||
assert.equal(flexShrinkView.prop('style').flexShrink, 1)
|
||||
const flexShrinkView = shallow(<View style={{ flexShrink: 1 }} />);
|
||||
assert.equal(flexShrinkView.prop('style').flexShrink, 1);
|
||||
|
||||
const flexAndShrinkView = shallow(<View style={{ flex: 1, flexShrink: 2 }} />)
|
||||
assert.equal(flexAndShrinkView.prop('style').flexShrink, 2)
|
||||
})
|
||||
})
|
||||
const flexAndShrinkView = shallow(<View style={{ flex: 1, flexShrink: 2 }} />);
|
||||
assert.equal(flexAndShrinkView.prop('style').flexShrink, 2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import applyLayout from '../../modules/applyLayout'
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods'
|
||||
import createReactDOMComponent from '../../modules/createReactDOMComponent'
|
||||
import EdgeInsetsPropType from '../../propTypes/EdgeInsetsPropType'
|
||||
import normalizeNativeEvent from '../../modules/normalizeNativeEvent'
|
||||
import { Component, PropTypes } from 'react'
|
||||
import StyleSheet from '../../apis/StyleSheet'
|
||||
import StyleSheetPropType from '../../propTypes/StyleSheetPropType'
|
||||
import ViewStylePropTypes from './ViewStylePropTypes'
|
||||
import applyLayout from '../../modules/applyLayout';
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods';
|
||||
import BaseComponentPropTypes from '../../propTypes/BaseComponentPropTypes';
|
||||
import createDOMElement from '../../modules/createDOMElement';
|
||||
import EdgeInsetsPropType from '../../propTypes/EdgeInsetsPropType';
|
||||
import normalizeNativeEvent from '../../modules/normalizeNativeEvent';
|
||||
import StyleSheet from '../../apis/StyleSheet';
|
||||
import StyleSheetPropType from '../../propTypes/StyleSheetPropType';
|
||||
import ViewStylePropTypes from './ViewStylePropTypes';
|
||||
import { Component, PropTypes } from 'react';
|
||||
|
||||
const eventHandlerNames = [
|
||||
'onClick',
|
||||
@@ -29,16 +30,13 @@ const eventHandlerNames = [
|
||||
'onTouchMoveCapture',
|
||||
'onTouchStart',
|
||||
'onTouchStartCapture'
|
||||
]
|
||||
];
|
||||
|
||||
class View extends Component {
|
||||
static displayName = 'View'
|
||||
static displayName = 'View';
|
||||
|
||||
static propTypes = {
|
||||
accessibilityLabel: createReactDOMComponent.propTypes.accessibilityLabel,
|
||||
accessibilityLiveRegion: createReactDOMComponent.propTypes.accessibilityLiveRegion,
|
||||
accessibilityRole: createReactDOMComponent.propTypes.accessibilityRole,
|
||||
accessible: createReactDOMComponent.propTypes.accessible,
|
||||
...BaseComponentPropTypes,
|
||||
children: PropTypes.any,
|
||||
collapsable: PropTypes.bool,
|
||||
hitSlop: EdgeInsetsPropType,
|
||||
@@ -63,9 +61,8 @@ class View extends Component {
|
||||
onTouchMoveCapture: PropTypes.func,
|
||||
onTouchStart: PropTypes.func,
|
||||
onTouchStartCapture: PropTypes.func,
|
||||
pointerEvents: PropTypes.oneOf(['auto', 'box-none', 'box-only', 'none']),
|
||||
style: StyleSheetPropType(ViewStylePropTypes),
|
||||
testID: createReactDOMComponent.propTypes.testID
|
||||
pointerEvents: PropTypes.oneOf([ 'auto', 'box-none', 'box-only', 'none' ]),
|
||||
style: StyleSheetPropType(ViewStylePropTypes)
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@@ -84,7 +81,7 @@ class View extends Component {
|
||||
getChildContext() {
|
||||
return {
|
||||
isInAButtonView: this.props.accessibilityRole === 'button'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -95,34 +92,34 @@ class View extends Component {
|
||||
pointerEvents,
|
||||
style,
|
||||
...other
|
||||
} = this.props
|
||||
} = this.props;
|
||||
|
||||
const flattenedStyle = StyleSheet.flatten(style)
|
||||
const pointerEventsStyle = pointerEvents && { pointerEvents }
|
||||
const flattenedStyle = StyleSheet.flatten(style);
|
||||
const pointerEventsStyle = pointerEvents && { pointerEvents };
|
||||
// 'View' needs to set 'flexShrink:0' only when there is no 'flex' or 'flexShrink' style provided
|
||||
const needsFlexReset = flattenedStyle.flex == null && flattenedStyle.flexShrink == null
|
||||
const needsFlexReset = flattenedStyle.flex == null && flattenedStyle.flexShrink == null;
|
||||
|
||||
const normalizedEventHandlers = eventHandlerNames.reduce((handlerProps, handlerName) => {
|
||||
const handler = this.props[handlerName]
|
||||
const handler = this.props[handlerName];
|
||||
if (typeof handler === 'function') {
|
||||
handlerProps[handlerName] = this._normalizeEventForHandler(handler, handlerName)
|
||||
handlerProps[handlerName] = this._normalizeEventForHandler(handler, handlerName);
|
||||
}
|
||||
return handlerProps
|
||||
}, {})
|
||||
return handlerProps;
|
||||
}, {});
|
||||
|
||||
const component = this.context.isInAButtonView ? 'span' : 'div';
|
||||
const props = {
|
||||
...other,
|
||||
...normalizedEventHandlers,
|
||||
component: this.context.isInAButtonView ? 'span' : 'div',
|
||||
style: [
|
||||
styles.initial,
|
||||
style,
|
||||
needsFlexReset && styles.flexReset,
|
||||
pointerEventsStyle
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
return createReactDOMComponent(props)
|
||||
return createDOMElement(component, props);
|
||||
}
|
||||
|
||||
_normalizeEventForHandler(handler, handlerName) {
|
||||
@@ -130,16 +127,16 @@ class View extends Component {
|
||||
// ResponderEvents and their handlers to fire twice for Touchables.
|
||||
// Auto-fix this issue by calling 'preventDefault' to cancel the mouse
|
||||
// events.
|
||||
const shouldCancelEvent = handlerName.indexOf('onResponder') === 0
|
||||
const shouldCancelEvent = handlerName.indexOf('onResponder') === 0;
|
||||
|
||||
return (e) => {
|
||||
e.nativeEvent = normalizeNativeEvent(e.nativeEvent)
|
||||
const returnValue = handler(e)
|
||||
e.nativeEvent = normalizeNativeEvent(e.nativeEvent);
|
||||
const returnValue = handler(e);
|
||||
if (shouldCancelEvent && e.cancelable) {
|
||||
e.preventDefault()
|
||||
e.preventDefault();
|
||||
}
|
||||
return returnValue
|
||||
}
|
||||
return returnValue;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,6 +168,6 @@ const styles = StyleSheet.create({
|
||||
flexReset: {
|
||||
flexShrink: 0
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
module.exports = applyLayout(applyNativeMethods(View))
|
||||
module.exports = applyLayout(applyNativeMethods(View));
|
||||
|
||||
30
src/core.js
30
src/core.js
@@ -1,14 +1,18 @@
|
||||
import './modules/injectResponderEventPlugin'
|
||||
import './modules/injectResponderEventPlugin';
|
||||
|
||||
import findNodeHandle from './modules/findNodeHandle'
|
||||
import ReactDOM from 'react-dom'
|
||||
import ReactDOMServer from 'react-dom/server'
|
||||
import I18nManager from './apis/I18nManager'
|
||||
import StyleSheet from './apis/StyleSheet'
|
||||
import Image from './components/Image'
|
||||
import Text from './components/Text'
|
||||
import TextInput from './components/TextInput'
|
||||
import View from './components/View'
|
||||
import findNodeHandle from './modules/findNodeHandle';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ReactDOMServer from 'react-dom/server';
|
||||
|
||||
// APIs
|
||||
import I18nManager from './apis/I18nManager';
|
||||
import StyleSheet from './apis/StyleSheet';
|
||||
|
||||
// components
|
||||
import Image from './components/Image';
|
||||
import Text from './components/Text';
|
||||
import TextInput from './components/TextInput';
|
||||
import View from './components/View';
|
||||
|
||||
const ReactNativeCore = {
|
||||
findNodeHandle,
|
||||
@@ -16,12 +20,14 @@ const ReactNativeCore = {
|
||||
renderToStaticMarkup: ReactDOMServer.renderToStaticMarkup,
|
||||
renderToString: ReactDOMServer.renderToString,
|
||||
unmountComponentAtNode: ReactDOM.unmountComponentAtNode,
|
||||
// APIs
|
||||
I18nManager,
|
||||
StyleSheet,
|
||||
// components
|
||||
Image,
|
||||
Text,
|
||||
TextInput,
|
||||
View
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = ReactNativeCore
|
||||
module.exports = ReactNativeCore;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user