mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-04-23 04:00:04 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1963e9109a | ||
|
|
14072c7471 | ||
|
|
0af6dc00fc | ||
|
|
c9d401f09a | ||
|
|
8aeeed0ef7 |
@@ -218,7 +218,7 @@ const examples = [
|
|||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<Image
|
<Image
|
||||||
source={{uri: 'http://facebook.github.io/react/img/logo_og.png'}}
|
source={{ uri: 'http://facebook.github.io/react/img/logo_og.png', width: 1200, height: 630 }}
|
||||||
style={styles.base}
|
style={styles.base}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { storiesOf, action } from '@kadira/storybook';
|
import { storiesOf, action } from '@kadira/storybook';
|
||||||
import { ScrollView, StyleSheet, Text, View } from 'react-native'
|
import { ScrollView, StyleSheet, Text, TouchableHighlight, View } from 'react-native'
|
||||||
|
|
||||||
storiesOf('component: ScrollView', module)
|
storiesOf('component: ScrollView', module)
|
||||||
.add('vertical', () => (
|
.add('vertical', () => (
|
||||||
@@ -13,7 +13,7 @@ storiesOf('component: ScrollView', module)
|
|||||||
>
|
>
|
||||||
{Array.from({ length: 50 }).map((item, i) => (
|
{Array.from({ length: 50 }).map((item, i) => (
|
||||||
<View key={i} style={styles.box}>
|
<View key={i} style={styles.box}>
|
||||||
<Text>{i}</Text>
|
<TouchableHighlight onPress={() => {}}><Text>{i}</Text></TouchableHighlight>
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
@@ -39,7 +39,6 @@ storiesOf('component: ScrollView', module)
|
|||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
box: {
|
box: {
|
||||||
alignItems: 'center',
|
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
borderWidth: 1
|
borderWidth: 1
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "react-native-web",
|
"name": "react-native-web",
|
||||||
"version": "0.0.47",
|
"version": "0.0.48",
|
||||||
"description": "React Native for Web",
|
"description": "React Native for Web",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ suite('components/Image', () => {
|
|||||||
test('sets background image when value is an object', () => {
|
test('sets background image when value is an object', () => {
|
||||||
const defaultSource = { uri: 'https://google.com/favicon.ico' };
|
const defaultSource = { uri: 'https://google.com/favicon.ico' };
|
||||||
const image = shallow(<Image defaultSource={defaultSource} />);
|
const image = shallow(<Image defaultSource={defaultSource} />);
|
||||||
const backgroundImage = StyleSheet.flatten(image.prop('style')).backgroundImage;
|
const style = StyleSheet.flatten(image.prop('style'));
|
||||||
assert(backgroundImage.indexOf(defaultSource.uri) > -1);
|
assert(style.backgroundImage.indexOf(defaultSource.uri) > -1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('sets background image when value is a string', () => {
|
test('sets background image when value is a string', () => {
|
||||||
@@ -45,13 +45,30 @@ suite('components/Image', () => {
|
|||||||
const backgroundImage = StyleSheet.flatten(image.prop('style')).backgroundImage;
|
const backgroundImage = StyleSheet.flatten(image.prop('style')).backgroundImage;
|
||||||
assert(backgroundImage.indexOf(defaultSource) > -1);
|
assert(backgroundImage.indexOf(defaultSource) > -1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('sets "height" and "width" styles if missing', () => {
|
||||||
|
const defaultSource = { uri: 'https://google.com/favicon.ico', height: 10, width: 20 };
|
||||||
|
const image = mount(<Image defaultSource={defaultSource} />);
|
||||||
|
const html = image.html();
|
||||||
|
assert(html.indexOf('height: 10px') > -1);
|
||||||
|
assert(html.indexOf('width: 20px') > -1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not override "height" and "width" styles', () => {
|
||||||
|
const defaultSource = { uri: 'https://google.com/favicon.ico', height: 10, width: 20 };
|
||||||
|
const image = mount(<Image defaultSource={defaultSource} style={{ height: 20, width: 40 }} />);
|
||||||
|
const html = image.html();
|
||||||
|
assert(html.indexOf('height: 20px') > -1);
|
||||||
|
assert(html.indexOf('width: 40px') > -1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('prop "onError"', function (done) {
|
test('prop "onError"', function (done) {
|
||||||
this.timeout(5000);
|
this.timeout(5000);
|
||||||
mount(<Image onError={onError} source={{ uri: 'https://google.com/favicon.icox' }} />);
|
const image = mount(<Image onError={onError} source={{ uri: 'https://google.com/favicon.icox' }} />);
|
||||||
function onError(e) {
|
function onError(e) {
|
||||||
assert.equal(e.nativeEvent.type, 'error');
|
assert.ok(e.nativeEvent.error);
|
||||||
|
image.unmount();
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -63,6 +80,7 @@ suite('components/Image', () => {
|
|||||||
assert.equal(e.nativeEvent.type, 'load');
|
assert.equal(e.nativeEvent.type, 'load');
|
||||||
const hasBackgroundImage = (image.html()).indexOf('url("https://google.com/favicon.ico")') > -1;
|
const hasBackgroundImage = (image.html()).indexOf('url("https://google.com/favicon.ico")') > -1;
|
||||||
assert.equal(hasBackgroundImage, true);
|
assert.equal(hasBackgroundImage, true);
|
||||||
|
image.unmount();
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -74,6 +92,7 @@ suite('components/Image', () => {
|
|||||||
assert.ok(true);
|
assert.ok(true);
|
||||||
const hasBackgroundImage = (image.html()).indexOf('url("https://google.com/favicon.ico")') > -1;
|
const hasBackgroundImage = (image.html()).indexOf('url("https://google.com/favicon.ico")') > -1;
|
||||||
assert.equal(hasBackgroundImage, true);
|
assert.equal(hasBackgroundImage, true);
|
||||||
|
image.unmount();
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -121,10 +140,11 @@ suite('components/Image', () => {
|
|||||||
|
|
||||||
test('sets background image when value is an object', (done) => {
|
test('sets background image when value is an object', (done) => {
|
||||||
const source = { uri: 'https://google.com/favicon.ico' };
|
const source = { uri: 'https://google.com/favicon.ico' };
|
||||||
mount(<Image onLoad={onLoad} source={source} />);
|
const image = mount(<Image onLoad={onLoad} source={source} />);
|
||||||
function onLoad(e) {
|
function onLoad(e) {
|
||||||
const src = e.nativeEvent.target.src;
|
const src = e.nativeEvent.target.src;
|
||||||
assert.equal(src, source.uri);
|
assert.equal(src, source.uri);
|
||||||
|
image.unmount();
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -132,10 +152,11 @@ suite('components/Image', () => {
|
|||||||
test('sets background image when value is a string', (done) => {
|
test('sets background image when value is a string', (done) => {
|
||||||
// emulate require-ed asset
|
// emulate require-ed asset
|
||||||
const source = 'https://google.com/favicon.ico';
|
const source = 'https://google.com/favicon.ico';
|
||||||
mount(<Image onLoad={onLoad} source={source} />);
|
const image = mount(<Image onLoad={onLoad} source={source} />);
|
||||||
function onLoad(e) {
|
function onLoad(e) {
|
||||||
const src = e.nativeEvent.target.src;
|
const src = e.nativeEvent.target.src;
|
||||||
assert.equal(src, source);
|
assert.equal(src, source);
|
||||||
|
image.unmount();
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,11 +17,20 @@ const STATUS_IDLE = 'IDLE';
|
|||||||
|
|
||||||
const ImageSourcePropType = PropTypes.oneOfType([
|
const ImageSourcePropType = PropTypes.oneOfType([
|
||||||
PropTypes.shape({
|
PropTypes.shape({
|
||||||
uri: PropTypes.string.isRequired
|
height: PropTypes.number,
|
||||||
|
uri: PropTypes.string.isRequired,
|
||||||
|
width: PropTypes.number
|
||||||
}),
|
}),
|
||||||
PropTypes.string
|
PropTypes.string
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const resolveAssetDimensions = (source) => {
|
||||||
|
if (typeof source === 'object') {
|
||||||
|
const { height, width } = source;
|
||||||
|
return { height, width };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const resolveAssetSource = (source) => {
|
const resolveAssetSource = (source) => {
|
||||||
return ((typeof source === 'object') ? source.uri : source) || null;
|
return ((typeof source === 'object') ? source.uri : source) || null;
|
||||||
};
|
};
|
||||||
@@ -93,20 +102,26 @@ class Image extends Component {
|
|||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const displayImage = resolveAssetSource(!isLoaded ? defaultSource : source);
|
const displayImage = resolveAssetSource(!isLoaded ? defaultSource : source);
|
||||||
|
const imageSizeStyle = resolveAssetDimensions(!isLoaded ? defaultSource : source);
|
||||||
const backgroundImage = displayImage ? `url("${displayImage}")` : null;
|
const backgroundImage = displayImage ? `url("${displayImage}")` : null;
|
||||||
let style = StyleSheet.flatten(this.props.style);
|
const originalStyle = StyleSheet.flatten(this.props.style);
|
||||||
|
const resizeMode = this.props.resizeMode || originalStyle.resizeMode || ImageResizeMode.cover;
|
||||||
|
|
||||||
const resizeMode = this.props.resizeMode || style.resizeMode || ImageResizeMode.cover;
|
const style = StyleSheet.flatten([
|
||||||
// remove 'resizeMode' style, as it is not supported by View (N.B. styles are frozen in dev)
|
styles.initial,
|
||||||
style = process.env.NODE_ENV !== 'production' ? { ...style } : style;
|
imageSizeStyle,
|
||||||
|
originalStyle,
|
||||||
|
backgroundImage && { backgroundImage },
|
||||||
|
resizeModeStyles[resizeMode]
|
||||||
|
]);
|
||||||
|
// View doesn't support 'resizeMode' as a style
|
||||||
delete style.resizeMode;
|
delete style.resizeMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Image is a non-stretching View. The image is displayed as a background
|
* The image is displayed as a background image to support `resizeMode`.
|
||||||
* image to support `resizeMode`. The HTML image is hidden but used to
|
* The HTML image is hidden but used to provide the correct responsive
|
||||||
* provide the correct responsive image dimensions, and to support the
|
* image dimensions, and to support the image context menu. Child content
|
||||||
* image context menu. Child content is rendered into an element absolutely
|
* is rendered into an element absolutely positioned over the image.
|
||||||
* positioned over the image.
|
|
||||||
*/
|
*/
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
@@ -114,12 +129,7 @@ class Image extends Component {
|
|||||||
accessibilityRole='img'
|
accessibilityRole='img'
|
||||||
accessible={accessible}
|
accessible={accessible}
|
||||||
onLayout={onLayout}
|
onLayout={onLayout}
|
||||||
style={[
|
style={style}
|
||||||
styles.initial,
|
|
||||||
style,
|
|
||||||
backgroundImage && { backgroundImage },
|
|
||||||
resizeModeStyles[resizeMode]
|
|
||||||
]}
|
|
||||||
testID={testID}
|
testID={testID}
|
||||||
>
|
>
|
||||||
{createDOMElement('img', { src: displayImage, style: styles.img })}
|
{createDOMElement('img', { src: displayImage, style: styles.img })}
|
||||||
@@ -149,14 +159,18 @@ class Image extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onError = (e) => {
|
_onError = () => {
|
||||||
const { onError } = this.props;
|
const { onError, source } = this.props;
|
||||||
const event = { nativeEvent: e };
|
|
||||||
|
|
||||||
this._destroyImageLoader();
|
this._destroyImageLoader();
|
||||||
this._updateImageState(STATUS_ERRORED);
|
|
||||||
this._onLoadEnd();
|
this._onLoadEnd();
|
||||||
if (onError) { onError(event); }
|
this._updateImageState(STATUS_ERRORED);
|
||||||
|
if (onError) {
|
||||||
|
onError({
|
||||||
|
nativeEvent: {
|
||||||
|
error: `Failed to load resource ${resolveAssetSource(source)} (404)`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onLoad = (e) => {
|
_onLoad = (e) => {
|
||||||
@@ -191,7 +205,6 @@ class Image extends Component {
|
|||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
initial: {
|
initial: {
|
||||||
alignSelf: 'flex-start',
|
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
backgroundPosition: 'center',
|
backgroundPosition: 'center',
|
||||||
backgroundRepeat: 'no-repeat',
|
backgroundRepeat: 'no-repeat',
|
||||||
|
|||||||
@@ -124,10 +124,10 @@ class View extends Component {
|
|||||||
|
|
||||||
_normalizeEventForHandler(handler, handlerName) {
|
_normalizeEventForHandler(handler, handlerName) {
|
||||||
// Browsers fire mouse events after touch events. This causes the
|
// Browsers fire mouse events after touch events. This causes the
|
||||||
// ResponderEvents and their handlers to fire twice for Touchables.
|
// 'onResponderRelease' handler to be called twice for Touchables.
|
||||||
// Auto-fix this issue by calling 'preventDefault' to cancel the mouse
|
// Auto-fix this issue by calling 'preventDefault' to cancel the mouse
|
||||||
// events.
|
// events.
|
||||||
const shouldCancelEvent = handlerName.indexOf('onResponder') === 0;
|
const shouldCancelEvent = handlerName === 'onResponderRelease';
|
||||||
|
|
||||||
return (e) => {
|
return (e) => {
|
||||||
e.nativeEvent = normalizeNativeEvent(e.nativeEvent);
|
e.nativeEvent = normalizeNativeEvent(e.nativeEvent);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const LayoutPropTypes = {
|
|||||||
alignItems: oneOf([ 'baseline', 'center', 'flex-end', 'flex-start', 'stretch' ]),
|
alignItems: oneOf([ 'baseline', 'center', 'flex-end', 'flex-start', 'stretch' ]),
|
||||||
alignSelf: oneOf([ 'auto', 'baseline', 'center', 'flex-end', 'flex-start', 'stretch' ]),
|
alignSelf: oneOf([ 'auto', 'baseline', 'center', 'flex-end', 'flex-start', 'stretch' ]),
|
||||||
flex: number,
|
flex: number,
|
||||||
flexBasis: string,
|
flexBasis: numberOrString,
|
||||||
flexDirection: oneOf([ 'column', 'column-reverse', 'row', 'row-reverse' ]),
|
flexDirection: oneOf([ 'column', 'column-reverse', 'row', 'row-reverse' ]),
|
||||||
flexGrow: number,
|
flexGrow: number,
|
||||||
flexShrink: number,
|
flexShrink: number,
|
||||||
|
|||||||
Reference in New Issue
Block a user