mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-05-25 15:42:24 +00:00
[add] initial 'onLayout' support
Add initial support for 'onLayout' when components mount and update. Ref #60
This commit is contained in:
@@ -33,7 +33,7 @@ const UIManager = {
|
||||
_measureLayout(node, relativeTo, onSuccess)
|
||||
},
|
||||
|
||||
updateView(node, props, component /* only needed to surpress React errors in __DEV__ */) {
|
||||
updateView(node, props, component /* only needed to surpress React errors in development */) {
|
||||
for (const prop in props) {
|
||||
const value = props[prop]
|
||||
|
||||
|
||||
@@ -23,12 +23,15 @@ const ImageSourcePropType = PropTypes.oneOfType([
|
||||
])
|
||||
|
||||
class Image extends Component {
|
||||
static displayName = 'Image'
|
||||
|
||||
static propTypes = {
|
||||
accessibilityLabel: createReactDOMComponent.propTypes.accessibilityLabel,
|
||||
accessible: createReactDOMComponent.propTypes.accessible,
|
||||
children: PropTypes.any,
|
||||
defaultSource: ImageSourcePropType,
|
||||
onError: PropTypes.func,
|
||||
onLayout: PropTypes.func,
|
||||
onLoad: PropTypes.func,
|
||||
onLoadEnd: PropTypes.func,
|
||||
onLoadStart: PropTypes.func,
|
||||
@@ -82,6 +85,7 @@ class Image extends Component {
|
||||
accessible,
|
||||
children,
|
||||
defaultSource,
|
||||
onLayout,
|
||||
source,
|
||||
testID
|
||||
} = this.props
|
||||
@@ -107,6 +111,7 @@ class Image extends Component {
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
accessibilityRole='img'
|
||||
accessible={accessible}
|
||||
onLayout={onLayout}
|
||||
ref='root'
|
||||
style={[
|
||||
styles.initial,
|
||||
|
||||
@@ -14,6 +14,15 @@ suite('components/Text', () => {
|
||||
|
||||
test('prop "numberOfLines"')
|
||||
|
||||
test('prop "onLayout"', (done) => {
|
||||
mount(<Text onLayout={onLayout} />)
|
||||
function onLayout(e) {
|
||||
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')
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import applyLayout from '../../modules/applyLayout'
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods'
|
||||
import createReactDOMComponent from '../../modules/createReactDOMComponent'
|
||||
import { Component, PropTypes } from 'react'
|
||||
@@ -6,12 +7,15 @@ import StyleSheetPropType from '../../propTypes/StyleSheetPropType'
|
||||
import TextStylePropTypes from './TextStylePropTypes'
|
||||
|
||||
class Text extends Component {
|
||||
static displayName = 'Text'
|
||||
|
||||
static propTypes = {
|
||||
accessibilityLabel: createReactDOMComponent.propTypes.accessibilityLabel,
|
||||
accessibilityRole: createReactDOMComponent.propTypes.accessibilityRole,
|
||||
accessible: createReactDOMComponent.propTypes.accessible,
|
||||
children: PropTypes.any,
|
||||
numberOfLines: PropTypes.number,
|
||||
onLayout: PropTypes.func,
|
||||
onPress: PropTypes.func,
|
||||
style: StyleSheetPropType(TextStylePropTypes),
|
||||
testID: createReactDOMComponent.propTypes.testID
|
||||
@@ -21,16 +25,11 @@ class Text extends Component {
|
||||
accessible: true
|
||||
};
|
||||
|
||||
_onPress = (e) => {
|
||||
if (this.props.onPress) this.props.onPress(e)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
numberOfLines,
|
||||
/* eslint-disable no-unused-vars */
|
||||
onPress,
|
||||
/* eslint-enable no-unused-vars */
|
||||
onLayout, // eslint-disable-line
|
||||
onPress, // eslint-disable-line
|
||||
style,
|
||||
...other
|
||||
} = this.props
|
||||
@@ -46,9 +45,13 @@ class Text extends Component {
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
_onPress = (e) => {
|
||||
if (this.props.onPress) this.props.onPress(e)
|
||||
}
|
||||
}
|
||||
|
||||
applyNativeMethods(Text)
|
||||
applyLayout(applyNativeMethods(Text))
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
initial: {
|
||||
|
||||
@@ -73,9 +73,7 @@ class TextInput extends Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
/* eslint-disable react/prop-types */
|
||||
accessibilityLabel,
|
||||
/* eslint-enable react/prop-types */
|
||||
accessibilityLabel, // eslint-disable-line
|
||||
autoComplete,
|
||||
autoFocus,
|
||||
defaultValue,
|
||||
@@ -85,6 +83,7 @@ class TextInput extends Component {
|
||||
maxNumberOfLines,
|
||||
multiline,
|
||||
numberOfLines,
|
||||
onLayout,
|
||||
onSelectionChange,
|
||||
placeholder,
|
||||
placeholderTextColor,
|
||||
@@ -171,6 +170,7 @@ class TextInput extends Component {
|
||||
<View
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
onClick={this._handleClick}
|
||||
onLayout={onLayout}
|
||||
style={[ styles.initial, rootStyles ]}
|
||||
testID={testID}
|
||||
>
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
import assert from 'assert'
|
||||
import includes from 'lodash/includes'
|
||||
import React from 'react'
|
||||
import { shallow } from 'enzyme'
|
||||
import View from '../'
|
||||
import { mount, shallow } from 'enzyme'
|
||||
|
||||
suite('components/View', () => {
|
||||
test('prop "children"', () => {
|
||||
@@ -13,6 +13,15 @@ suite('components/View', () => {
|
||||
assert.equal(view.prop('children'), children)
|
||||
})
|
||||
|
||||
test('prop "onLayout"', (done) => {
|
||||
mount(<View onLayout={onLayout} />)
|
||||
function onLayout(e) {
|
||||
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)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import applyLayout from '../../modules/applyLayout'
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods'
|
||||
import createReactDOMComponent from '../../modules/createReactDOMComponent'
|
||||
import EdgeInsetsPropType from '../../propTypes/EdgeInsetsPropType'
|
||||
@@ -8,6 +9,8 @@ import StyleSheetPropType from '../../propTypes/StyleSheetPropType'
|
||||
import ViewStylePropTypes from './ViewStylePropTypes'
|
||||
|
||||
class View extends Component {
|
||||
static displayName = 'View'
|
||||
|
||||
static propTypes = {
|
||||
accessibilityLabel: createReactDOMComponent.propTypes.accessibilityLabel,
|
||||
accessibilityLiveRegion: createReactDOMComponent.propTypes.accessibilityLiveRegion,
|
||||
@@ -105,7 +108,7 @@ class View extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
applyNativeMethods(View)
|
||||
applyLayout(applyNativeMethods(View))
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
// https://github.com/facebook/css-layout#default-values
|
||||
|
||||
@@ -113,11 +113,11 @@ const NativeMethodsMixin = {
|
||||
* In the future, we should cleanup callbacks by cancelling them instead of
|
||||
* using this.
|
||||
*/
|
||||
const mountSafeCallback = (context: Component, callback: ?Function) => () => {
|
||||
if (!callback || (context.isMounted && !context.isMounted())) {
|
||||
return
|
||||
const mountSafeCallback = (context: Component, callback: ?Function) => (...args) => {
|
||||
if (!callback) {
|
||||
return undefined
|
||||
}
|
||||
return callback.apply(context, arguments)
|
||||
return callback.apply(context, args)
|
||||
}
|
||||
|
||||
module.exports = NativeMethodsMixin
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) 2016-present, Nicolas Gallagher.
|
||||
* All rights reserved.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import emptyFunction from 'fbjs/lib/emptyFunction'
|
||||
|
||||
const applyLayout = (Component) => {
|
||||
const componentDidMount = Component.prototype.componentDidMount || emptyFunction
|
||||
const componentDidUpdate = Component.prototype.componentDidUpdate || emptyFunction
|
||||
|
||||
Component.prototype.componentDidMount = function () {
|
||||
componentDidMount()
|
||||
this._layoutState = {}
|
||||
this._handleLayout()
|
||||
}
|
||||
|
||||
Component.prototype.componentDidUpdate = function () {
|
||||
componentDidUpdate()
|
||||
this._handleLayout()
|
||||
}
|
||||
|
||||
Component.prototype._handleLayout = function () {
|
||||
const layout = this._layoutState
|
||||
const { onLayout } = this.props
|
||||
|
||||
if (onLayout) {
|
||||
this.measure((x, y, width, height) => {
|
||||
if (layout.x !== x || layout.y !== y || layout.width !== width || layout.height !== height) {
|
||||
const nextLayout = { x, y, width, height }
|
||||
const nativeEvent = { layout: nextLayout }
|
||||
onLayout({ nativeEvent })
|
||||
this._layoutState = nextLayout
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
return Component
|
||||
}
|
||||
|
||||
module.exports = applyLayout
|
||||
Reference in New Issue
Block a user