mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-03-27 22:55:56 +08:00
Add new components
- ActivityIndicator - Portal - StaticContainer - StaticRenderer
This commit is contained in:
69
docs/components/ActivityIndicator.md
Normal file
69
docs/components/ActivityIndicator.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# ActivityIndicator
|
||||
|
||||
## Props
|
||||
|
||||
[...View props](./View.md)
|
||||
|
||||
**animating**: bool = true
|
||||
|
||||
Whether to show the indicator (true, the default) or hide it (false).
|
||||
|
||||
**color**: string = #999999
|
||||
|
||||
The foreground color of the spinner (default is gray).
|
||||
|
||||
**hidesWhenStopped**: bool = true
|
||||
|
||||
Whether the indicator should hide when not animating (true by default).
|
||||
|
||||
**size**: oneOf('small, 'large')
|
||||
|
||||
Size of the indicator. Small has a height of `20`, large has a height of `36`.
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
import React, { ActivityIndicator, Component, StyleSheet, View } from 'react-native'
|
||||
|
||||
class ToggleAnimatingActivityIndicator extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = { animating: true }
|
||||
}
|
||||
|
||||
componentDidMount: function() {
|
||||
this.setToggleTimeout();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
animating={this.state.animating}
|
||||
size="large"
|
||||
style={[styles.centering, { height: 80 }]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_setToggleTimeout() {
|
||||
setTimeout(() => {
|
||||
this.setState({ animating: !this.state.animating })
|
||||
this._setToggleTimeout()
|
||||
}, 1200)
|
||||
}
|
||||
})
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
centering: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
gray: {
|
||||
backgroundColor: '#cccccc'
|
||||
},
|
||||
horizontal: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-around'
|
||||
}
|
||||
})
|
||||
```
|
||||
@@ -76,7 +76,7 @@ Used to locate a view in end-to-end tests.
|
||||
|
||||
```js
|
||||
import placeholderAvatar from './placeholderAvatar.png'
|
||||
import React, { Component, Image, PropTypes, StyleSheet } from 'react-native-web'
|
||||
import React, { Component, Image, PropTypes, StyleSheet } from 'react-native'
|
||||
|
||||
export default class ImageExample extends Component {
|
||||
constructor(props, context) {
|
||||
|
||||
@@ -15,7 +15,7 @@ Content to display over the image.
|
||||
## Examples
|
||||
|
||||
```js
|
||||
import React, { Component, ListView, PropTypes } from 'react-native-web'
|
||||
import React, { Component, ListView, PropTypes } from 'react-native'
|
||||
|
||||
export default class ListViewExample extends Component {
|
||||
static propTypes = {}
|
||||
|
||||
67
docs/components/Portal.md
Normal file
67
docs/components/Portal.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Portal
|
||||
|
||||
`Portal` is used to render modal content on top of everything else in the
|
||||
application. It passes modal views all the way up to the root element created
|
||||
by `AppRegistry.runApplication`.
|
||||
|
||||
There can only be one `Portal` instance rendered in an application, and this
|
||||
instance is controlled by React Native for Web.
|
||||
|
||||
## Methods
|
||||
|
||||
static **allocateTag**()
|
||||
|
||||
Creates a new unique tag for the modal that your component is rendering. A
|
||||
good place to allocate a tag is in `componentWillMount`. Returns a string. See
|
||||
`showModal` and `closeModal`.
|
||||
|
||||
static **closeModal**(tag: string)
|
||||
|
||||
Remove a modal from the collection of modals to be rendered. The `tag` must
|
||||
exactly match the tag previous passed to `showModal` to identify the React
|
||||
component.
|
||||
|
||||
static **getOpenModals**()
|
||||
|
||||
Get an array of all the open modals, as identified by their tag string.
|
||||
|
||||
static **showModal**(tag: string, component: any)
|
||||
|
||||
Render a new modal. The `tag` must be unique as it is used to identify the
|
||||
React component to render. This same tag can later be used in `closeModal`.
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
import React, { Portal, Text, Touchable } from 'react-native'
|
||||
|
||||
export default class PortalExample extends Component {
|
||||
componentWillMount() {
|
||||
this._portalTag = Portal.allocateTag()
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Touchable onPress={this._handlePortalOpen.bind(this)}>
|
||||
<Text>Open portal</Text>
|
||||
</Touchable>
|
||||
)
|
||||
}
|
||||
|
||||
_handlePortalClose(e) {
|
||||
Portal.closeModal(this._portalTag)
|
||||
}
|
||||
|
||||
_handlePortalOpen(e) {
|
||||
Portal.showModal(this._portalTag, this._renderPortalContent())
|
||||
}
|
||||
|
||||
_renderPortalContent() {
|
||||
return (
|
||||
<Touchable onPress={this._handlePortalClose.bind(this)}>
|
||||
<Text>Close portal</Text>
|
||||
</Touchable>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -43,7 +43,7 @@ time the view is scrolled.
|
||||
## Examples
|
||||
|
||||
```js
|
||||
import React, { Component, ScrollView, StyleSheet } from 'react-native-web'
|
||||
import React, { Component, ScrollView, StyleSheet } from 'react-native'
|
||||
import Item from './Item'
|
||||
|
||||
export default class ScrollViewExample extends Component {
|
||||
|
||||
@@ -74,7 +74,7 @@ Used to locate this view in end-to-end tests.
|
||||
## Examples
|
||||
|
||||
```js
|
||||
import React, { Component, PropTypes, StyleSheet, Text } from 'react-native-web'
|
||||
import React, { Component, PropTypes, StyleSheet, Text } from 'react-native'
|
||||
|
||||
export default class PrettyText extends Component {
|
||||
static propTypes = {
|
||||
|
||||
@@ -150,7 +150,7 @@ user edits to the value set `editable={false}`.
|
||||
## Examples
|
||||
|
||||
```js
|
||||
import React, { Component, StyleSheet, TextInput } from 'react-native-web'
|
||||
import React, { Component, StyleSheet, TextInput } from 'react-native'
|
||||
|
||||
export default class TextInputExample extends Component {
|
||||
constructor(props, context) {
|
||||
|
||||
@@ -84,7 +84,7 @@ Delay in ms, from the release of the touch, before `onPressOut` is called.
|
||||
## Examples
|
||||
|
||||
```js
|
||||
import React, { Component, PropTypes, Touchable } from 'react-native-web'
|
||||
import React, { Component, PropTypes, Touchable } from 'react-native'
|
||||
|
||||
export default class Example extends Component {
|
||||
static propTypes = {}
|
||||
|
||||
@@ -168,7 +168,7 @@ Used to locate this view in end-to-end tests.
|
||||
## Examples
|
||||
|
||||
```js
|
||||
import React, { Component, PropTypes, StyleSheet, View } from 'react-native-web'
|
||||
import React, { Component, PropTypes, StyleSheet, View } from 'react-native'
|
||||
|
||||
export default class ViewExample extends Component {
|
||||
render() {
|
||||
|
||||
@@ -20,34 +20,4 @@ suite('ReactNativeWeb', () => {
|
||||
assert.ok(React.renderToStaticMarkup)
|
||||
})
|
||||
})
|
||||
|
||||
suite('render methods', () => {
|
||||
const id = 'test'
|
||||
let div
|
||||
|
||||
setup(() => {
|
||||
div = document.createElement('div')
|
||||
div.id = id
|
||||
document.body.appendChild(div)
|
||||
})
|
||||
|
||||
teardown(() => {
|
||||
document.body.removeChild(div)
|
||||
})
|
||||
|
||||
test('"render" creates style sheet', () => {
|
||||
React.render(<div />, div)
|
||||
assert.ok(document.getElementById('react-stylesheet'))
|
||||
})
|
||||
|
||||
test('"renderToString" creates style sheet', () => {
|
||||
const result = React.renderToString(<div />)
|
||||
assert.ok(result.indexOf('react-stylesheet') > -1)
|
||||
})
|
||||
|
||||
test('"renderToStaticMarkup" creates style sheet', () => {
|
||||
const result = React.renderToStaticMarkup(<div />)
|
||||
assert.ok(result.indexOf('react-stylesheet') > -1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
5
src/components/ActivityIndicator/__tests__/index-test.js
Normal file
5
src/components/ActivityIndicator/__tests__/index-test.js
Normal file
@@ -0,0 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
suite('components/ActivityIndicator', () => {
|
||||
test.skip('NO TEST COVERAGE', () => {})
|
||||
})
|
||||
108
src/components/ActivityIndicator/index.js
Normal file
108
src/components/ActivityIndicator/index.js
Normal file
@@ -0,0 +1,108 @@
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import StyleSheet from '../../apis/StyleSheet'
|
||||
import View from '../View'
|
||||
|
||||
const GRAY = '#999999'
|
||||
|
||||
const animationEffectTimingProperties = {
|
||||
direction: 'alternate',
|
||||
duration: 700,
|
||||
easing: 'ease-in-out',
|
||||
fill: 'forwards',
|
||||
iterations: Infinity
|
||||
}
|
||||
|
||||
const keyframeEffects = [
|
||||
{ transform: 'scale(1)', opacity: 1.0 },
|
||||
{ transform: 'scale(0.95)', opacity: 0.5 }
|
||||
]
|
||||
|
||||
export default class ActivityIndicator extends Component {
|
||||
static propTypes = {
|
||||
animating: PropTypes.bool,
|
||||
color: PropTypes.string,
|
||||
hidesWhenStopped: PropTypes.bool,
|
||||
size: PropTypes.oneOf(['small', 'large']),
|
||||
style: PropTypes.object
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
animating: true,
|
||||
color: GRAY,
|
||||
hidesWhenStopped: true,
|
||||
size: 'small',
|
||||
style: {}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
if (document.documentElement.animate) {
|
||||
this._player = ReactDOM.findDOMNode(this._indicatorRef).animate(keyframeEffects, animationEffectTimingProperties)
|
||||
}
|
||||
if (this.props.animating) {
|
||||
this._player.play()
|
||||
} else {
|
||||
this._player.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.props.animating) {
|
||||
this._player.play()
|
||||
} else {
|
||||
this._player.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
animating,
|
||||
color,
|
||||
hidesWhenStopped,
|
||||
size,
|
||||
style,
|
||||
...other
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<View {...other} style={{ ...styles.container, ...style }}>
|
||||
<View
|
||||
ref={(c) => { this._indicatorRef = c }}
|
||||
style={{
|
||||
...styles.indicator[size],
|
||||
...(hidesWhenStopped && !animating && styles.hidesWhenStopped),
|
||||
borderColor: color
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const indicatorStyle = StyleSheet.create({
|
||||
borderRadius: 100,
|
||||
borderWidth: 3
|
||||
})
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
hidesWhenStopped: {
|
||||
visibility: 'hidden'
|
||||
},
|
||||
indicator: {
|
||||
small: {
|
||||
...indicatorStyle,
|
||||
width: 20,
|
||||
height: 20
|
||||
},
|
||||
large: {
|
||||
...indicatorStyle,
|
||||
borderWidth: 4,
|
||||
width: 36,
|
||||
height: 36
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import StylePropTypes from '../../apis/StyleSheet/StylePropTypes'
|
||||
import StyleSheet from '../../apis/StyleSheet'
|
||||
|
||||
@@ -18,7 +18,7 @@ const roleComponents = {
|
||||
region: 'section'
|
||||
}
|
||||
|
||||
class CoreComponent extends React.Component {
|
||||
export default class CoreComponent extends Component {
|
||||
static propTypes = {
|
||||
accessibilityLabel: PropTypes.string,
|
||||
accessibilityLiveRegion: PropTypes.oneOf([ 'assertive', 'off', 'polite' ]),
|
||||
@@ -66,5 +66,3 @@ class CoreComponent extends React.Component {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default CoreComponent
|
||||
|
||||
@@ -3,7 +3,7 @@ import { pickProps } from '../../modules/filterObjectProps'
|
||||
import StyleSheet from '../../apis/StyleSheet'
|
||||
import CoreComponent from '../CoreComponent'
|
||||
import ImageStylePropTypes from './ImageStylePropTypes'
|
||||
import React, { PropTypes } from 'react'
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import View from '../View'
|
||||
|
||||
const STATUS_ERRORED = 'ERRORED'
|
||||
@@ -52,7 +52,7 @@ const styles = StyleSheet.create({
|
||||
}
|
||||
})
|
||||
|
||||
class Image extends React.Component {
|
||||
export default class Image extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
const { uri } = props.source
|
||||
@@ -209,5 +209,3 @@ class Image extends React.Component {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Image
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import ScrollView from '../ScrollView'
|
||||
|
||||
class ListView extends React.Component {
|
||||
export default class ListView extends Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.any,
|
||||
style: PropTypes.style
|
||||
@@ -17,5 +17,3 @@ class ListView extends React.Component {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default ListView
|
||||
|
||||
156
src/components/Portal/index.js
Normal file
156
src/components/Portal/index.js
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Copyright 2015-present, Nicolas Gallagher
|
||||
* Copyright 2004-present, Facebook Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import invariant from 'invariant'
|
||||
import Platform from '../../apis/Platform'
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import StyleSheet from '../../apis/StyleSheet'
|
||||
import View from '../View'
|
||||
|
||||
let _portalRef: any
|
||||
// unique identifiers for modals
|
||||
let lastUsedTag = 0
|
||||
|
||||
/**
|
||||
* A container that renders all the modals on top of everything else in the application.
|
||||
*/
|
||||
export default class Portal extends Component {
|
||||
static propTypes = {
|
||||
onModalVisibilityChanged: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new unique tag.
|
||||
*/
|
||||
static allocateTag(): string {
|
||||
return `__modal_${++lastUsedTag}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a new modal.
|
||||
*/
|
||||
static showModal(tag: string, component: any) {
|
||||
if (!_portalRef) {
|
||||
console.error('Calling showModal but no "Portal" has been rendered.')
|
||||
return
|
||||
}
|
||||
_portalRef._showModal(tag, component)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a modal from the collection of modals to be rendered.
|
||||
*/
|
||||
static closeModal(tag: string) {
|
||||
if (!_portalRef) {
|
||||
console.error('Calling closeModal but no "Portal" has been rendered.')
|
||||
return
|
||||
}
|
||||
_portalRef._closeModal(tag)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of all the open modals, as identified by their tag string.
|
||||
*/
|
||||
static getOpenModals(): Array<string> {
|
||||
if (!_portalRef) {
|
||||
console.error('Calling getOpenModals but no "Portal" has been rendered.')
|
||||
return []
|
||||
}
|
||||
return _portalRef._getOpenModals()
|
||||
}
|
||||
|
||||
static notifyAccessibilityService() {
|
||||
if (!_portalRef) {
|
||||
console.error('Calling closeModal but no "Portal" has been rendered.')
|
||||
return
|
||||
}
|
||||
_portalRef._notifyAccessibilityService()
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = { modals: {} }
|
||||
this._closeModal = this._closeModal.bind(this)
|
||||
this._getOpenModals = this._getOpenModals.bind(this)
|
||||
this._showModal = this._showModal.bind(this)
|
||||
}
|
||||
|
||||
render() {
|
||||
invariant(
|
||||
_portalRef === this || _portalRef === undefined,
|
||||
'More than one Portal instance detected. Never use <Portal> in your code.'
|
||||
)
|
||||
_portalRef = this
|
||||
if (!this.state.modals) { return null }
|
||||
const modals = []
|
||||
for (const tag in this.state.modals) {
|
||||
modals.push(this.state.modals[tag])
|
||||
}
|
||||
if (modals.length === 0) { return null }
|
||||
|
||||
return (
|
||||
<View style={styles.root}>
|
||||
{modals}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
_closeModal(tag: string) {
|
||||
if (!this.state.modals.hasOwnProperty(tag)) {
|
||||
return
|
||||
}
|
||||
// We are about to close last modal, so Portal will disappear.
|
||||
// Let's enable accessibility for application view.
|
||||
if (this._getOpenModals().length === 1) {
|
||||
this.props.onModalVisibilityChanged(false)
|
||||
}
|
||||
// This way state is chained through multiple calls to
|
||||
// _showModal, _closeModal correctly.
|
||||
this.setState((state) => {
|
||||
const modals = state.modals
|
||||
delete modals[tag]
|
||||
return { modals }
|
||||
})
|
||||
}
|
||||
|
||||
_getOpenModals(): Array<string> {
|
||||
return Object.keys(this.state.modals)
|
||||
}
|
||||
|
||||
_notifyAccessibilityService() {
|
||||
if (Platform.OS === 'web') {
|
||||
// We need to send accessibility event in a new batch, as otherwise
|
||||
// TextViews have no text set at the moment of populating event.
|
||||
}
|
||||
}
|
||||
|
||||
_showModal(tag: string, component: any) {
|
||||
// We are about to open first modal, so Portal will appear.
|
||||
// Let's disable accessibility for background view on Android.
|
||||
if (this._getOpenModals().length === 0) {
|
||||
this.props.onModalVisibilityChanged(true)
|
||||
}
|
||||
// This way state is chained through multiple calls to
|
||||
// _showModal, _closeModal correctly.
|
||||
this.setState((state) => {
|
||||
const modals = state.modals
|
||||
modals[tag] = component
|
||||
return { modals }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0
|
||||
}
|
||||
})
|
||||
@@ -1,6 +1,6 @@
|
||||
import { pickProps } from '../../modules/filterObjectProps'
|
||||
import debounce from 'lodash.debounce'
|
||||
import React, { PropTypes } from 'react'
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import ScrollViewStylePropTypes from './ScrollViewStylePropTypes'
|
||||
import StyleSheet from '../../apis/StyleSheet'
|
||||
import View from '../View'
|
||||
@@ -22,7 +22,7 @@ const styles = StyleSheet.create({
|
||||
}
|
||||
})
|
||||
|
||||
class ScrollView extends React.Component {
|
||||
export default class ScrollView extends Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.any,
|
||||
contentContainerStyle: PropTypes.shape(ScrollViewStylePropTypes),
|
||||
@@ -136,5 +136,3 @@ class ScrollView extends React.Component {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default ScrollView
|
||||
|
||||
5
src/components/StaticContainer/__tests__/index-test.js
Normal file
5
src/components/StaticContainer/__tests__/index-test.js
Normal file
@@ -0,0 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
suite('components/StaticContainer', () => {
|
||||
test.skip('NO TEST COVERAGE', () => {})
|
||||
})
|
||||
40
src/components/StaticContainer/index.js
Normal file
40
src/components/StaticContainer/index.js
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Nicolas Gallagher.
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
|
||||
/**
|
||||
* Renders static content efficiently by allowing React to short-circuit the
|
||||
* reconciliation process. This component should be used when you know that a
|
||||
* subtree of components will never need to be updated.
|
||||
*
|
||||
* const someValue = ...; // We know for certain this value will never change.
|
||||
* return (
|
||||
* <StaticContainer>
|
||||
* <MyComponent value={someValue} />
|
||||
* </StaticContainer>
|
||||
* );
|
||||
*
|
||||
* Typically, you will not need to use this component and should opt for normal
|
||||
* React reconciliation.
|
||||
*/
|
||||
export default class StaticContainer extends Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.any.isRequired,
|
||||
shouldUpdate: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps: { shouldUpdate: boolean }): boolean {
|
||||
return nextProps.shouldUpdate
|
||||
}
|
||||
|
||||
render() {
|
||||
const child = this.props.children
|
||||
return (child === null || child === false) ? null : React.Children.only(child)
|
||||
}
|
||||
}
|
||||
5
src/components/StaticRenderer/__tests__/index-test.js
Normal file
5
src/components/StaticRenderer/__tests__/index-test.js
Normal file
@@ -0,0 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
|
||||
suite('components/StaticRenderer', () => {
|
||||
test.skip('NO TEST COVERAGE', () => {})
|
||||
})
|
||||
38
src/components/StaticRenderer/index.js
Normal file
38
src/components/StaticRenderer/index.js
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Nicolas Gallagher.
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import { Component, PropTypes } from 'react'
|
||||
|
||||
/**
|
||||
* Renders static content efficiently by allowing React to short-circuit the
|
||||
* reconciliation process. This component should be used when you know that a
|
||||
* subtree of components will never need to be updated.
|
||||
*
|
||||
* const someValue = ...; // We know for certain this value will never change.
|
||||
* return (
|
||||
* <StaticRenderer render={() => <MyComponent value={someValue} />} />
|
||||
* );
|
||||
*
|
||||
* Typically, you will not need to use this component and should opt for normal
|
||||
* React reconciliation.
|
||||
*/
|
||||
|
||||
export default class StaticRenderer extends Component {
|
||||
static propTypes = {
|
||||
render: PropTypes.func.isRequired,
|
||||
shouldUpdate: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps: { shouldUpdate: boolean }): boolean {
|
||||
return nextProps.shouldUpdate
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.props.render()
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { pickProps } from '../../modules/filterObjectProps'
|
||||
import CoreComponent from '../CoreComponent'
|
||||
import React, { PropTypes } from 'react'
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import StyleSheet from '../../apis/StyleSheet'
|
||||
import TextStylePropTypes from './TextStylePropTypes'
|
||||
|
||||
@@ -24,7 +24,7 @@ const styles = StyleSheet.create({
|
||||
}
|
||||
})
|
||||
|
||||
class Text extends React.Component {
|
||||
export default class Text extends Component {
|
||||
static propTypes = {
|
||||
_className: PropTypes.string, // escape-hatch for code migrations
|
||||
accessibilityLabel: CoreComponent.propTypes.accessibilityLabel,
|
||||
@@ -76,5 +76,3 @@ class Text extends React.Component {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Text
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { pickProps } from '../../modules/filterObjectProps'
|
||||
import CoreComponent from '../CoreComponent'
|
||||
import React, { PropTypes } from 'react'
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import StyleSheet from '../../apis/StyleSheet'
|
||||
import Text from '../Text'
|
||||
@@ -39,7 +39,7 @@ const styles = StyleSheet.create({
|
||||
}
|
||||
})
|
||||
|
||||
class TextInput extends React.Component {
|
||||
export default class TextInput extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
this.state = { showPlaceholder: !props.value && !props.defaultValue }
|
||||
@@ -221,5 +221,3 @@ class TextInput extends React.Component {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default TextInput
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import Tappable from 'react-tappable'
|
||||
import View from '../View'
|
||||
import StyleSheet from '../../apis/StyleSheet'
|
||||
@@ -11,7 +11,7 @@ const styles = StyleSheet.create({
|
||||
}
|
||||
})
|
||||
|
||||
class Touchable extends React.Component {
|
||||
export default class Touchable extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
this.state = {
|
||||
@@ -130,5 +130,3 @@ class Touchable extends React.Component {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Touchable
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { pickProps } from '../../modules/filterObjectProps'
|
||||
import CoreComponent from '../CoreComponent'
|
||||
import React, { PropTypes } from 'react'
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import StyleSheet from '../../apis/StyleSheet'
|
||||
import ViewStylePropTypes from './ViewStylePropTypes'
|
||||
|
||||
@@ -30,7 +30,7 @@ const styles = StyleSheet.create({
|
||||
}
|
||||
})
|
||||
|
||||
class View extends React.Component {
|
||||
export default class View extends Component {
|
||||
static propTypes = {
|
||||
_className: PropTypes.string, // escape-hatch for code migrations
|
||||
accessibilityLabel: CoreComponent.propTypes.accessibilityLabel,
|
||||
@@ -76,5 +76,3 @@ class View extends React.Component {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default View
|
||||
|
||||
56
src/index.js
56
src/index.js
@@ -2,63 +2,55 @@ import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import ReactDOMServer from 'react-dom/server'
|
||||
|
||||
// api
|
||||
import StyleSheet from './modules/StyleSheet'
|
||||
// apis
|
||||
import AppRegistry from './apis/AppRegistry'
|
||||
import AppState from './apis/AppState'
|
||||
import AsyncStorage from './apis/AsyncStorage'
|
||||
import Dimensions from './apis/Dimensions'
|
||||
import NetInfo from './apis/NetInfo'
|
||||
import PixelRatio from './apis/PixelRatio'
|
||||
import Platform from './apis/Platform'
|
||||
import StyleSheet from './apis/StyleSheet'
|
||||
|
||||
// components
|
||||
import ActivityIndicator from './components/ActivityIndicator'
|
||||
import Image from './components/Image'
|
||||
import ListView from './components/ListView'
|
||||
import Portal from './components/Portal'
|
||||
import ScrollView from './components/ScrollView'
|
||||
import Text from './components/Text'
|
||||
import TextInput from './components/TextInput'
|
||||
import Touchable from './components/Touchable'
|
||||
import View from './components/View'
|
||||
|
||||
const renderStyle = () => {
|
||||
return `<style id='react-stylesheet'>${StyleSheet._renderToString()}</style>`
|
||||
}
|
||||
|
||||
const render = (element, container, callback) => {
|
||||
const styleElement = document.getElementById('react-stylesheet')
|
||||
if (!styleElement) {
|
||||
const style = renderStyle()
|
||||
container.insertAdjacentHTML('beforebegin', style)
|
||||
}
|
||||
return ReactDOM.render(element, container, callback)
|
||||
}
|
||||
|
||||
const renderToString = (element) => {
|
||||
const style = renderStyle()
|
||||
const html = ReactDOMServer.renderToString(element)
|
||||
return `${style}\n${html}`
|
||||
}
|
||||
|
||||
const renderToStaticMarkup = (element) => {
|
||||
const style = renderStyle()
|
||||
const html = ReactDOMServer.renderToStaticMarkup(element)
|
||||
return `${style}\n${html}`
|
||||
}
|
||||
|
||||
const ReactNative = {
|
||||
// apis
|
||||
AppRegistry,
|
||||
AppState,
|
||||
AsyncStorage,
|
||||
Dimensions,
|
||||
NetInfo,
|
||||
PixelRatio,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
|
||||
// components
|
||||
ActivityIndicator,
|
||||
Image,
|
||||
ListView,
|
||||
Portal,
|
||||
ScrollView,
|
||||
Text,
|
||||
TextInput,
|
||||
Touchable,
|
||||
TouchableHighlight: Touchable,
|
||||
TouchableOpacity: Touchable,
|
||||
TouchableWithoutFeedback: Touchable,
|
||||
View,
|
||||
|
||||
// React
|
||||
...React,
|
||||
...ReactDOM,
|
||||
...ReactDOMServer,
|
||||
render,
|
||||
renderToString,
|
||||
renderToStaticMarkup
|
||||
...ReactDOMServer
|
||||
}
|
||||
|
||||
module.exports = ReactNative
|
||||
|
||||
Reference in New Issue
Block a user