diff --git a/packages/docs/src/components/View/View.stories.mdx b/packages/docs/src/components/View/View.stories.mdx index 015225a2..6d1224e9 100644 --- a/packages/docs/src/components/View/View.stories.mdx +++ b/packages/docs/src/components/View/View.stories.mdx @@ -23,7 +23,6 @@ Please also refer to the in-depth "Accessibility" and "Style" guides. | accessibilityValue | ?Object | | | accessible | ?boolean | | | children | ?any | | -| forwardedRef | ?Ref | | | hitSlop | ?Object | | | importantForAccessibility | ?string | | | nativeID | ?string | | diff --git a/packages/react-native-web/src/exports/ActivityIndicator/index.js b/packages/react-native-web/src/exports/ActivityIndicator/index.js index 74485145..0bb8e523 100644 --- a/packages/react-native-web/src/exports/ActivityIndicator/index.js +++ b/packages/react-native-web/src/exports/ActivityIndicator/index.js @@ -28,7 +28,7 @@ type ActivityIndicatorProps = { size?: 'small' | 'large' | number }; -const ActivityIndicator = forwardRef((props, ref) => { +const ActivityIndicator = forwardRef((props, forwardedRef) => { const { animating = true, color = '#1976D2', @@ -57,7 +57,7 @@ const ActivityIndicator = forwardRef((props, ref) => {...other} accessibilityRole="progressbar" accessibilityValue={accessibilityValue} - ref={ref} + ref={forwardedRef} style={[styles.container, style]} > void, onValueChange?: ?(e: any) => void, value?: boolean }; -const CheckBox = forwardRef((props, ref) => { +const CheckBox = forwardRef((props, forwardedRef) => { const { color, disabled, onChange, onValueChange, style, value, ...other } = props; - const checkboxRef = useRef(null); - - useImperativeHandle( - ref, - () => { - return { - blur() { - UIManager.blur(checkboxRef.current); - }, - focus() { - UIManager.focus(checkboxRef.current); - } - }; - }, - [checkboxRef] - ); - function handleChange(event: Object) { const value = event.nativeEvent.target.checked; event.nativeEvent.value = value; @@ -69,13 +53,13 @@ const CheckBox = forwardRef((props, ref) => { checked: value, disabled: disabled, onChange: handleChange, - ref: checkboxRef, + ref: forwardedRef, style: [styles.nativeControl, styles.cursorInherit], type: 'checkbox' }); return ( - + {fakeControl} {nativeControl} diff --git a/packages/react-native-web/src/exports/ImageBackground/index.js b/packages/react-native-web/src/exports/ImageBackground/index.js index 9b4f8e1d..6c976114 100644 --- a/packages/react-native-web/src/exports/ImageBackground/index.js +++ b/packages/react-native-web/src/exports/ImageBackground/index.js @@ -10,11 +10,11 @@ import type { ImageProps } from '../Image'; import type { ViewProps } from '../View'; -import setAndForwardRef from '../../modules/setAndForwardRef'; +import * as React from 'react'; +import { forwardRef } from 'react'; import Image from '../Image'; import StyleSheet from '../StyleSheet'; import View from '../View'; -import React, { forwardRef, useImperativeHandle, useRef } from 'react'; type ImageBackgroundProps = { ...ImageProps, @@ -28,33 +28,12 @@ const emptyObject = {}; /** * Very simple drop-in replacement for which supports nesting views. */ -const ImageBackground = forwardRef((props, ref) => { +const ImageBackground = forwardRef((props, forwardedRef) => { const { children, style = emptyObject, imageStyle, imageRef, ...rest } = props; const { height, width } = StyleSheet.flatten(style); - const containerRef = useRef(null); - - useImperativeHandle( - ref, - () => ({ - setNativeProps(props) { - if (containerRef.current != null) { - containerRef.current.setNativeProps(props); - } - } - }), - [containerRef] - ); - - const setRef = setAndForwardRef({ - getForwardedRef: () => ref, - setLocalRef: c => { - containerRef.current = c; - } - }); - return ( - + ((props, ref) => { +const Picker = forwardRef((props, forwardedRef) => { const { children, enabled, @@ -48,12 +48,13 @@ const Picker = forwardRef((props, ref) => { const hostRef = useRef(null); const setRef = setAndForwardRef({ - getForwardedRef: () => ref, - setLocalRef: c => { - hostRef.current = c; + getForwardedRef: () => forwardedRef, + setLocalRef: hostNode => { + hostRef.current = hostNode; } }); - usePlatformMethods(hostRef, ref, [], style); + + usePlatformMethods(hostRef, [], style); function handleChange(e: Object) { const { selectedIndex, value } = e.target; diff --git a/packages/react-native-web/src/exports/Pressable/index.js b/packages/react-native-web/src/exports/Pressable/index.js index 02008353..19d5ab62 100644 --- a/packages/react-native-web/src/exports/Pressable/index.js +++ b/packages/react-native-web/src/exports/Pressable/index.js @@ -14,7 +14,8 @@ import type { PressResponderConfig } from '../../modules/PressResponder'; import type { ViewProps } from '../View'; import * as React from 'react'; -import { useMemo, useState, useRef, useImperativeHandle } from 'react'; +import { forwardRef, memo, useMemo, useState, useRef } from 'react'; +import setAndForwardRef from '../../modules/setAndForwardRef'; import usePressEvents from '../../modules/PressResponder/usePressEvents'; import View from '../View'; @@ -94,42 +95,46 @@ function Pressable(props: Props, forwardedRef): React.Node { ...rest } = props; - const hostRef = useRef(null); - const viewRef = useRef | null>(null); const [focused, setFocused] = useForceableState(false); const [pressed, setPressed] = useForceableState(testOnly_pressed === true); - useImperativeHandle(forwardedRef, () => viewRef.current); - const pressEventHandlers = usePressEvents( - hostRef, - useMemo( - () => ({ - delayLongPress, - delayPressStart: delayPressIn, - delayPressEnd: delayPressOut, - disabled, - onLongPress, - onPress, - onPressChange: setPressed, - onPressStart: onPressIn, - onPressMove, - onPressEnd: onPressOut - }), - [ - delayLongPress, - delayPressIn, - delayPressOut, - disabled, - onLongPress, - onPress, - onPressIn, - onPressMove, - onPressOut, - setPressed - ] - ) + const hostRef = useRef(null); + const setRef = setAndForwardRef({ + getForwardedRef: () => forwardedRef, + setLocalRef: hostNode => { + hostRef.current = hostNode; + } + }); + + const pressConfig = useMemo( + () => ({ + delayLongPress, + delayPressStart: delayPressIn, + delayPressEnd: delayPressOut, + disabled, + onLongPress, + onPress, + onPressChange: setPressed, + onPressStart: onPressIn, + onPressMove, + onPressEnd: onPressOut + }), + [ + delayLongPress, + delayPressIn, + delayPressOut, + disabled, + onLongPress, + onPress, + onPressIn, + onPressMove, + onPressOut, + setPressed + ] ); + const pressEventHandlers = usePressEvents(hostRef, pressConfig); + const accessibilityState = { disabled, ...props.accessibilityState }; const interactionState = { focused, pressed }; @@ -152,10 +157,9 @@ function Pressable(props: Props, forwardedRef): React.Node { accessibilityState={accessibilityState} accessible={accessible !== false} focusable={focusable !== false} - forwardedRef={hostRef} onBlur={createFocusHandler(onBlur, false)} onFocus={createFocusHandler(onFocus, true)} - ref={viewRef} + ref={setRef} style={typeof style === 'function' ? style(interactionState) : style} > {typeof children === 'function' ? children(interactionState) : children} @@ -168,7 +172,7 @@ function useForceableState(forced: boolean): [boolean, (boolean) => void] { return [pressed || forced, setPressed]; } -const MemoedPressable = React.memo(React.forwardRef(Pressable)); +const MemoedPressable = memo(forwardRef(Pressable)); MemoedPressable.displayName = 'Pressable'; export default (MemoedPressable: React.AbstractComponent>); diff --git a/packages/react-native-web/src/exports/ScrollView/ScrollViewBase.js b/packages/react-native-web/src/exports/ScrollView/ScrollViewBase.js index a8843b3f..b75df0dc 100644 --- a/packages/react-native-web/src/exports/ScrollView/ScrollViewBase.js +++ b/packages/react-native-web/src/exports/ScrollView/ScrollViewBase.js @@ -9,11 +9,11 @@ import type { ViewProps } from '../View'; +import * as React from 'react'; +import { forwardRef, useRef } from 'react'; import debounce from 'debounce'; -import setAndForwardRef from '../../modules/setAndForwardRef'; import StyleSheet from '../StyleSheet'; import View from '../View'; -import React, { forwardRef, useImperativeHandle, useRef } from 'react'; type Props = { ...ViewProps, @@ -70,14 +70,13 @@ function shouldEmitScrollEvent(lastTick: number, eventThrottle: number) { /** * Encapsulates the Web-specific scroll throttling and disabling logic */ -const ScrollViewBase = forwardRef((props, ref) => { +const ScrollViewBase = forwardRef((props, forwardedRef) => { const { accessibilityLabel, accessibilityRelationship, accessibilityRole, accessibilityState, children, - forwardedRef, importantForAccessibility, nativeID, onLayout, @@ -94,28 +93,6 @@ const ScrollViewBase = forwardRef((props, ref) => { } = props; const scrollState = useRef({ isScrolling: false, scrollLastTick: 0 }); - const viewRef = useRef(null); - - useImperativeHandle( - ref, - () => { - return { - setNativeProps(props: Object) { - if (viewRef.current != null) { - viewRef.current.setNativeProps(props); - } - } - }; - }, - [] - ); - - const setRef = setAndForwardRef({ - getForwardedRef: () => ref, - setLocalRef: c => { - viewRef.current = c; - } - }); function createPreventableScrollHandler(handler: Function) { return (e: Object) => { @@ -173,7 +150,6 @@ const ScrollViewBase = forwardRef((props, ref) => { accessibilityRole={accessibilityRole} accessibilityState={accessibilityState} children={children} - forwardedRef={forwardedRef} importantForAccessibility={importantForAccessibility} nativeID={nativeID} onLayout={onLayout} @@ -181,7 +157,7 @@ const ScrollViewBase = forwardRef((props, ref) => { onTouchMove={createPreventableScrollHandler(onTouchMove)} onWheel={createPreventableScrollHandler(onWheel)} pointerEvents={pointerEvents} - ref={setRef} + ref={forwardedRef} style={[ style, !scrollEnabled && styles.scrollDisabled, diff --git a/packages/react-native-web/src/exports/ScrollView/index.js b/packages/react-native-web/src/exports/ScrollView/index.js index d3771503..57ce6212 100644 --- a/packages/react-native-web/src/exports/ScrollView/index.js +++ b/packages/react-native-web/src/exports/ScrollView/index.js @@ -48,8 +48,8 @@ const ScrollView = ((createReactClass({ }, setNativeProps(props: Object) { - if (this._scrollViewRef) { - this._scrollViewRef.setNativeProps(props); + if (this._scrollNodeRef) { + this._scrollNodeRef.setNativeProps(props); } }, @@ -189,7 +189,7 @@ const ScrollView = ((createReactClass({ {...contentSizeChangeProps} children={children} collapsable={false} - forwardedRef={this._setInnerViewRef} + ref={this._setInnerViewRef} style={StyleSheet.compose( horizontal && styles.contentContainerHorizontal, contentContainerStyle @@ -231,23 +231,14 @@ const ScrollView = ((createReactClass({ return React.cloneElement( refreshControl, { style: props.style }, - + {contentContainer} ); } return ( - + {contentContainer} ); @@ -282,10 +273,6 @@ const ScrollView = ((createReactClass({ this._innerViewRef = component; }, - _setScrollViewRef(component) { - this._scrollViewRef = component; - }, - _setScrollNodeRef(component) { this._scrollNodeRef = component; } diff --git a/packages/react-native-web/src/exports/Switch/index.js b/packages/react-native-web/src/exports/Switch/index.js index b82ed901..b34b23c2 100644 --- a/packages/react-native-web/src/exports/Switch/index.js +++ b/packages/react-native-web/src/exports/Switch/index.js @@ -10,12 +10,12 @@ import type { ColorValue } from '../../types'; import type { ViewProps } from '../View'; +import * as React from 'react'; +import { forwardRef, useRef } from 'react'; import createElement from '../createElement'; import multiplyStyleLengthValue from '../../modules/multiplyStyleLengthValue'; import StyleSheet from '../StyleSheet'; -import UIManager from '../UIManager'; import View from '../View'; -import React, { forwardRef, useImperativeHandle, useRef } from 'react'; type SwitchProps = { ...ViewProps, @@ -32,7 +32,7 @@ const emptyObject = {}; const thumbDefaultBoxShadow = '0px 1px 3px rgba(0,0,0,0.5)'; const thumbFocusedBoxShadow = `${thumbDefaultBoxShadow}, 0 0 0 10px rgba(0,0,0,0.1)`; -const Switch = forwardRef((props, ref) => { +const Switch = forwardRef((props, forwardedRef) => { const { accessibilityLabel, activeThumbColor = '#009688', @@ -46,24 +46,8 @@ const Switch = forwardRef((props, ref) => { ...other } = props; - const checkboxRef = useRef(null); const thumbRef = useRef(null); - useImperativeHandle( - ref, - () => { - return { - blur() { - UIManager.blur(checkboxRef.current); - }, - focus() { - UIManager.focus(checkboxRef.current); - } - }; - }, - [checkboxRef] - ); - function handleChange(event: Object) { if (onValueChange != null) { onValueChange(event.nativeEvent.target.checked); @@ -74,7 +58,7 @@ const Switch = forwardRef((props, ref) => { const isFocused = event.nativeEvent.type === 'focus'; const boxShadow = isFocused ? thumbFocusedBoxShadow : thumbDefaultBoxShadow; if (thumbRef.current != null) { - thumbRef.current.setNativeProps({ style: { boxShadow } }); + thumbRef.current.style.boxShadow = boxShadow; } } @@ -130,13 +114,13 @@ const Switch = forwardRef((props, ref) => { onBlur: handleFocusState, onChange: handleChange, onFocus: handleFocusState, - ref: checkboxRef, + ref: forwardedRef, style: [styles.nativeControl, styles.cursorInherit], type: 'checkbox' }); return ( - + {nativeControl} diff --git a/packages/react-native-web/src/exports/Text/index.js b/packages/react-native-web/src/exports/Text/index.js index 6ac3467c..a10b78b4 100644 --- a/packages/react-native-web/src/exports/Text/index.js +++ b/packages/react-native-web/src/exports/Text/index.js @@ -10,34 +10,80 @@ import type { TextProps } from './types'; +import * as React from 'react'; +import { forwardRef, useContext, useRef } from 'react'; import createElement from '../createElement'; import css from '../StyleSheet/css'; +import pick from '../../modules/pick'; import setAndForwardRef from '../../modules/setAndForwardRef'; import useElementLayout from '../../hooks/useElementLayout'; import usePlatformMethods from '../../hooks/usePlatformMethods'; import useResponderEvents from '../../hooks/useResponderEvents'; -import React, { forwardRef, useContext, useRef } from 'react'; import StyleSheet from '../StyleSheet'; import TextAncestorContext from './TextAncestorContext'; -const Text = forwardRef((props, ref) => { +const forwardPropsList = { + accessibilityLabel: true, + accessibilityLiveRegion: true, + accessibilityRelationship: true, + accessibilityRole: true, + accessibilityState: true, + accessibilityValue: true, + accessible: true, + children: true, + classList: true, + dir: true, + importantForAccessibility: true, + lang: true, + nativeID: true, + onBlur: true, + onClick: true, + onClickCapture: true, + onContextMenu: true, + onFocus: true, + onKeyDown: true, + onKeyUp: true, + onTouchCancel: true, + onTouchCancelCapture: true, + onTouchEnd: true, + onTouchEndCapture: true, + onTouchMove: true, + onTouchMoveCapture: true, + onTouchStart: true, + onTouchStartCapture: true, + pointerEvents: true, + ref: true, + style: true, + testID: true, + // unstable + onMouseDown: true, + onMouseEnter: true, + onMouseLeave: true, + onMouseMove: true, + onMouseOver: true, + onMouseOut: true, + onMouseUp: true, + onScroll: true, + onWheel: true, + href: true, + itemID: true, + itemRef: true, + itemProp: true, + itemScope: true, + itemType: true, + rel: true, + target: true, + unstable_ariaSet: true, + unstable_dataSet: true +}; + +const pickProps = props => pick(props, forwardPropsList); + +const Text = forwardRef((props, forwardedRef) => { const { - accessibilityLabel, - accessibilityLiveRegion, - accessibilityRelationship, - accessibilityRole, - accessibilityState, - children, dir, - forwardedRef, - importantForAccessibility, - lang, - nativeID, numberOfLines, - onBlur, onClick, - onContextMenu, - onFocus, onLayout, onPress, onMoveShouldSetResponder, @@ -56,42 +102,15 @@ const Text = forwardRef((props, ref) => { onSelectionChangeShouldSetResponderCapture, onStartShouldSetResponder, onStartShouldSetResponderCapture, - selectable, - testID, - // unstable - onMouseDown, - onMouseEnter, - onMouseLeave, - onMouseMove, - onMouseOver, - onMouseOut, - onMouseUp, - onTouchCancel, - onTouchCancelCapture, - onTouchEnd, - onTouchEndCapture, - onTouchMove, - onTouchMoveCapture, - onTouchStart, - onTouchStartCapture, - href, - itemID, - itemRef, - itemProp, - itemScope, - itemType, - rel, - target, - unstable_ariaSet, - unstable_dataSet + selectable } = props; const hasTextAncestor = useContext(TextAncestorContext); const hostRef = useRef(null); const setRef = setAndForwardRef({ getForwardedRef: () => forwardedRef, - setLocalRef: c => { - hostRef.current = c; + setLocalRef: hostNode => { + hostRef.current = hostNode; } }); @@ -109,7 +128,7 @@ const Text = forwardRef((props, ref) => { ]; useElementLayout(hostRef, onLayout); - usePlatformMethods(hostRef, ref, classList, style); + usePlatformMethods(hostRef, classList, style); useResponderEvents(hostRef, { onMoveShouldSetResponder, onMoveShouldSetResponderCapture, @@ -140,54 +159,15 @@ const Text = forwardRef((props, ref) => { } const component = hasTextAncestor ? 'span' : 'div'; - const element = createElement(component, { - accessibilityLabel, - accessibilityLiveRegion, - accessibilityRelationship, - accessibilityRole, - accessibilityState, - accessible: onPress != null ? true : null, - children, - classList, - // allow browsers to automatically infer the language writing direction - dir: dir !== undefined ? dir : 'auto', - importantForAccessibility, - lang, - nativeID, - onBlur, - onClick: handleClick, - onContextMenu, - onFocus, - ref: setRef, - style, - testID, - // unstable - onMouseDown, - onMouseEnter, - onMouseLeave, - onMouseMove, - onMouseOver, - onMouseOut, - onMouseUp, - onTouchCancel, - onTouchCancelCapture, - onTouchEnd, - onTouchEndCapture, - onTouchMove, - onTouchMoveCapture, - onTouchStart, - onTouchStartCapture, - href, - itemID, - itemRef, - itemProp, - itemScope, - itemType, - rel, - target, - unstable_ariaSet, - unstable_dataSet - }); + const supportedProps = pickProps(props); + supportedProps.classList = classList; + // 'auto' by default allows browsers to infer writing direction + supportedProps.dir = dir !== undefined ? dir : 'auto'; + supportedProps.onClick = handleClick; + supportedProps.ref = setRef; + supportedProps.style = style; + + const element = createElement(component, supportedProps); return hasTextAncestor ? ( element diff --git a/packages/react-native-web/src/exports/Text/types.js b/packages/react-native-web/src/exports/Text/types.js index ed3e2d56..30d7f4e5 100644 --- a/packages/react-native-web/src/exports/Text/types.js +++ b/packages/react-native-web/src/exports/Text/types.js @@ -8,8 +8,8 @@ * @flow */ -import type { ColorValue, GenericStyleProp, LayoutEvent } from '../../types'; -import type { ViewStyle } from '../View/types'; +import type { ColorValue, GenericStyleProp } from '../../types'; +import type { ViewProps, ViewStyle } from '../View/types'; type FontWeightValue = | 'normal' @@ -61,9 +61,10 @@ export type TextStyle = { WebkitFontSmoothing?: ?string }; +type AllowedViewProps = $Diff; + export type TextProps = { - accessibilityLabel?: ?string, - accessibilityLiveRegion?: 'none' | 'polite' | 'assertive', + ...AllowedViewProps, accessibilityRelationship?: { activedescendant?: ?string, controls?: ?string, @@ -95,62 +96,10 @@ export type TextProps = { required?: ?boolean, selected?: ?boolean }, - accessible?: ?boolean, - children?: ?any, dir?: 'auto' | 'ltr' | 'rtl', - forwardedRef?: any, - importantForAccessibility?: 'auto' | 'yes' | 'no' | 'no-hide-descendants', - nativeID?: ?string, numberOfLines?: ?number, - onBlur?: (e: any) => void, - onClick?: (e: any) => void, - onContextMenu?: (e: any) => void, - onFocus?: (e: any) => void, - onLayout?: (e: LayoutEvent) => void, onPress?: (e: any) => void, - onMoveShouldSetResponder?: (e: any) => boolean, - onMoveShouldSetResponderCapture?: (e: any) => boolean, - onResponderEnd?: (e: any) => void, - onResponderGrant?: (e: any) => void, - onResponderMove?: (e: any) => void, - onResponderReject?: (e: any) => void, - onResponderRelease?: (e: any) => void, - onResponderStart?: (e: any) => void, - onResponderTerminate?: (e: any) => void, - onResponderTerminationRequest?: (e: any) => boolean, - onScrollShouldSetResponder?: (e: any) => boolean, - onScrollShouldSetResponderCapture?: (e: any) => boolean, - onSelectionChangeShouldSetResponder?: (e: any) => boolean, - onSelectionChangeShouldSetResponderCapture?: (e: any) => boolean, - onStartShouldSetResponder?: (e: any) => boolean, - onStartShouldSetResponderCapture?: (e: any) => boolean, selectable?: boolean, style?: GenericStyleProp, - testID?: ?string, - // unstable - onMouseDown?: (e: any) => void, - onMouseEnter?: (e: any) => void, - onMouseLeave?: (e: any) => void, - onMouseMove?: (e: any) => void, - onMouseOver?: (e: any) => void, - onMouseOut?: (e: any) => void, - onMouseUp?: (e: any) => void, - onTouchCancel?: (e: any) => void, - onTouchCancelCapture?: (e: any) => void, - onTouchEnd?: (e: any) => void, - onTouchEndCapture?: (e: any) => void, - onTouchMove?: (e: any) => void, - onTouchMoveCapture?: (e: any) => void, - onTouchStart?: (e: any) => void, - onTouchStartCapture?: (e: any) => void, - href?: ?string, - itemID?: ?string, - itemRef?: ?string, - itemProp?: ?string, - itemScope?: ?string, - itemType?: ?string, - rel?: ?string, - target?: ?string, - unstable_ariaSet?: Object, - unstable_dataSet?: Object + testID?: ?string }; diff --git a/packages/react-native-web/src/exports/TextInput/index.js b/packages/react-native-web/src/exports/TextInput/index.js index 16e78bee..5cfd039f 100644 --- a/packages/react-native-web/src/exports/TextInput/index.js +++ b/packages/react-native-web/src/exports/TextInput/index.js @@ -10,14 +10,15 @@ import type { TextInputProps } from './types'; +import { forwardRef, useRef } from 'react'; import createElement from '../createElement'; import css from '../StyleSheet/css'; +import pick from '../../modules/pick'; import setAndForwardRef from '../../modules/setAndForwardRef'; import useElementLayout from '../../hooks/useElementLayout'; import useLayoutEffect from '../../hooks/useLayoutEffect'; -import { usePlatformInputMethods } from '../../hooks/usePlatformMethods'; +import usePlatformMethods from '../../hooks/usePlatformMethods'; import useResponderEvents from '../../hooks/useResponderEvents'; -import { forwardRef, useRef } from 'react'; import StyleSheet from '../StyleSheet'; import TextInputState from '../../modules/TextInputState'; @@ -49,33 +50,87 @@ const setSelection = (node, selection) => { } }; -const TextInput = forwardRef((props, ref) => { +const forwardPropsList = { + accessibilityLabel: true, + accessibilityLiveRegion: true, + accessibilityRelationship: true, + accessibilityRole: true, + accessibilityState: true, + accessibilityValue: true, + accessible: true, + autoCapitalize: true, + autoComplete: true, + autoCorrect: true, + autoFocus: true, + children: true, + classList: true, + defaultValue: true, + dir: true, + disabled: true, + importantForAccessibility: true, + maxLength: true, + nativeID: true, + onBlur: true, + onChange: true, + onClick: true, + onClickCapture: true, + onContextMenu: true, + onFocus: true, + onScroll: true, + onTouchCancel: true, + onTouchCancelCapture: true, + onTouchEnd: true, + onTouchEndCapture: true, + onTouchMove: true, + onTouchMoveCapture: true, + onTouchStart: true, + onTouchStartCapture: true, + placeholder: true, + pointerEvents: true, + readOnly: true, + ref: true, + rows: true, + spellCheck: true, + style: true, + value: true, + testID: true, + type: true, + // unstable + onMouseDown: true, + onMouseEnter: true, + onMouseLeave: true, + onMouseMove: true, + onMouseOver: true, + onMouseOut: true, + onMouseUp: true, + itemID: true, + itemRef: true, + itemProp: true, + itemScope: true, + itemType: true, + unstable_ariaSet: true, + unstable_dataSet: true +}; + +const pickProps = props => pick(props, forwardPropsList); + +const TextInput = forwardRef((props, forwardedRef) => { const { - accessibilityLabel, - accessibilityRelationship, - accessibilityState, autoCapitalize = 'sentences', autoComplete, autoCompleteType, autoCorrect = true, - autoFocus, blurOnSubmit, clearTextOnFocus, - defaultValue, - disabled, + dir, editable = true, - forwardedRef, - importantForAccessibility, keyboardType = 'default', - maxLength, multiline = false, - nativeID, numberOfLines = 1, onBlur, onChange, onChangeText, onContentSizeChange, - onContextMenu, onFocus, onKeyPress, onLayout, @@ -89,7 +144,6 @@ const TextInput = forwardRef((props, ref) => { onResponderStart, onResponderTerminate, onResponderTerminationRequest, - onScroll, onScrollShouldSetResponder, onScrollShouldSetResponderCapture, onSelectionChange, @@ -98,24 +152,12 @@ const TextInput = forwardRef((props, ref) => { onStartShouldSetResponder, onStartShouldSetResponderCapture, onSubmitEditing, - placeholder, placeholderTextColor, - pointerEvents, returnKeyType, secureTextEntry = false, selection = emptyObject, selectTextOnFocus, - spellCheck, - testID, - value, - // unstable - itemID, - itemRef, - itemProp, - itemScope, - itemType, - unstable_ariaSet, - unstable_dataSet + spellCheck } = props; let type; @@ -150,21 +192,27 @@ const TextInput = forwardRef((props, ref) => { const dimensions = useRef({ height: null, width: null }); const setRef = setAndForwardRef({ getForwardedRef: () => forwardedRef, - setLocalRef: c => { - hostRef.current = c; + setLocalRef: hostNode => { + // TextInput needs to add more methods to the hostNode in addition to those + // added by `usePlatformMethods`. This is temporarily until an API like + // `TextInput.clear(hostRef)` is added to React Native. + if (hostNode != null) { + hostNode.clear = function() { + if (hostNode != null) { + hostNode.value = ''; + } + }; + hostNode.isFocused = function() { + return hostNode != null && TextInputState.currentlyFocusedField() === hostNode; + }; + } + hostRef.current = hostNode; if (hostRef.current != null) { handleContentSizeChange(); } } }); - const component = multiline ? 'textarea' : 'input'; - const classList = [classes.textinput]; - const style = StyleSheet.compose( - props.style, - placeholderTextColor && { placeholderTextColor } - ); - function handleBlur(e) { TextInputState._currentlyFocusedNode = null; if (onBlur) { @@ -285,8 +333,15 @@ const TextInput = forwardRef((props, ref) => { } }, [hostRef, selection]); + const component = multiline ? 'textarea' : 'input'; + const classList = [classes.textinput]; + const style = StyleSheet.compose( + props.style, + placeholderTextColor && { placeholderTextColor } + ); + useElementLayout(hostRef, onLayout); - usePlatformInputMethods(hostRef, ref, classList, style); + usePlatformMethods(hostRef, classList, style); useResponderEvents(hostRef, { onMoveShouldSetResponder, onMoveShouldSetResponderCapture, @@ -306,48 +361,27 @@ const TextInput = forwardRef((props, ref) => { onStartShouldSetResponderCapture }); - return createElement(component, { - accessibilityLabel, - accessibilityRelationship, - accessibilityState, - autoCapitalize, - autoComplete: autoComplete || autoCompleteType || 'on', - autoCorrect: autoCorrect ? 'on' : 'off', - autoFocus, - classList, - defaultValue, - dir: 'auto', - disabled, - enterkeyhint: returnKeyType, - importantForAccessibility, - maxLength, - nativeID, - onBlur: handleBlur, - onChange: handleChange, - onContextMenu, - onFocus: handleFocus, - onKeyDown: handleKeyDown, - onScroll, - onSelect: handleSelectionChange, - placeholder, - pointerEvents, - testID, - readOnly: !editable, - ref: setRef, - rows: multiline ? numberOfLines : undefined, - spellCheck: spellCheck != null ? spellCheck : autoCorrect, - style, - type: multiline ? undefined : type, - value, - // unstable - itemID, - itemRef, - itemProp, - itemScope, - itemType, - unstable_ariaSet, - unstable_dataSet - }); + const supportedProps = pickProps(props); + supportedProps.autoCapitalize = autoCapitalize; + supportedProps.autoComplete = autoComplete || autoCompleteType || 'on'; + supportedProps.autoCorrect = autoCorrect ? 'on' : 'off'; + supportedProps.classList = classList; + // 'auto' by default allows browsers to infer writing direction + supportedProps.dir = dir !== undefined ? dir : 'auto'; + supportedProps.enterkeyhint = returnKeyType; + supportedProps.onBlur = handleBlur; + supportedProps.onChange = handleChange; + supportedProps.onFocus = handleFocus; + supportedProps.onKeyDown = handleKeyDown; + supportedProps.onSelect = handleSelectionChange; + supportedProps.readOnly = !editable; + supportedProps.ref = setRef; + supportedProps.rows = multiline ? numberOfLines : undefined; + supportedProps.spellCheck = spellCheck != null ? spellCheck : autoCorrect; + supportedProps.style = style; + supportedProps.type = multiline ? undefined : type; + + return createElement(component, supportedProps); }); TextInput.displayName = 'TextInput'; diff --git a/packages/react-native-web/src/exports/TextInput/types.js b/packages/react-native-web/src/exports/TextInput/types.js index 24947cc2..21ca91cc 100644 --- a/packages/react-native-web/src/exports/TextInput/types.js +++ b/packages/react-native-web/src/exports/TextInput/types.js @@ -27,6 +27,7 @@ export type TextInputProps = { blurOnSubmit?: ?boolean, clearTextOnFocus?: ?boolean, defaultValue?: ?string, + dir?: ?('auto' | 'ltr' | 'rtl'), disabled?: ?boolean, editable?: ?boolean, inputAccessoryViewID?: ?string, @@ -43,12 +44,10 @@ export type TextInputProps = { maxLength?: ?number, multiline?: ?boolean, numberOfLines?: ?number, - onBlur?: (e: any) => void, onChange?: (e: any) => void, onChangeText?: (e: string) => void, onContentSizeChange?: (e: any) => void, onEndEditing?: (e: any) => void, - onFocus?: (e: any) => void, onKeyPress?: (e: any) => void, onSelectionChange?: (e: any) => void, onScroll?: (e: any) => void, diff --git a/packages/react-native-web/src/exports/TouchableHighlight/index.js b/packages/react-native-web/src/exports/TouchableHighlight/index.js index aa7c4a9e..eae948a8 100644 --- a/packages/react-native-web/src/exports/TouchableHighlight/index.js +++ b/packages/react-native-web/src/exports/TouchableHighlight/index.js @@ -15,8 +15,9 @@ import type { Props as TouchableWithoutFeedbackProps } from '../TouchableWithout import type { ViewProps } from '../View'; import * as React from 'react'; -import { useCallback, useMemo, useState, useRef, useImperativeHandle } from 'react'; +import { useCallback, useMemo, useState, useRef } from 'react'; import usePressEvents from '../../modules/PressResponder/usePressEvents'; +import setAndForwardRef from '../../modules/setAndForwardRef'; import StyleSheet from '../StyleSheet'; import View from '../View'; @@ -25,7 +26,6 @@ type ViewStyle = $PropertyType; type Props = $ReadOnly<{| ...TouchableWithoutFeedbackProps, activeOpacity?: ?number, - hostRef: React.Ref, onHideUnderlay?: ?() => void, onShowUnderlay?: ?() => void, style?: ViewStyle, @@ -93,8 +93,12 @@ function TouchableHighlight(props: Props, forwardedRef): React.Node { } = props; const hostRef = useRef(null); - const viewRef = useRef | null>(null); - useImperativeHandle(forwardedRef, () => viewRef.current); + const setRef = setAndForwardRef({ + getForwardedRef: () => forwardedRef, + setLocalRef: hostNode => { + hostRef.current = hostNode; + } + }); const [extraStyles, setExtraStyles] = useState( testOnly_pressed === true ? createExtraStyles(activeOpacity, underlayColor) : null @@ -122,46 +126,45 @@ function TouchableHighlight(props: Props, forwardedRef): React.Node { } }, [onHideUnderlay, props, testOnly_pressed]); - const pressEventHandlers = usePressEvents( - hostRef, - useMemo( - () => ({ - cancelable: !rejectResponderTermination, - disabled, - delayLongPress, - delayPressStart: delayPressIn, - delayPressEnd: delayPressOut, - onLongPress, - onPress, - onPressStart(event) { - showUnderlay(); - if (onPressIn != null) { - onPressIn(event); - } - }, - onPressEnd(event) { - hideUnderlay(); - if (onPressOut != null) { - onPressOut(event); - } + const pressConfig = useMemo( + () => ({ + cancelable: !rejectResponderTermination, + disabled, + delayLongPress, + delayPressStart: delayPressIn, + delayPressEnd: delayPressOut, + onLongPress, + onPress, + onPressStart(event) { + showUnderlay(); + if (onPressIn != null) { + onPressIn(event); } - }), - [ - delayLongPress, - delayPressIn, - delayPressOut, - disabled, - onLongPress, - onPress, - onPressIn, - onPressOut, - rejectResponderTermination, - showUnderlay, - hideUnderlay - ] - ) + }, + onPressEnd(event) { + hideUnderlay(); + if (onPressOut != null) { + onPressOut(event); + } + } + }), + [ + delayLongPress, + delayPressIn, + delayPressOut, + disabled, + onLongPress, + onPress, + onPressIn, + onPressOut, + rejectResponderTermination, + showUnderlay, + hideUnderlay + ] ); + const pressEventHandlers = usePressEvents(hostRef, pressConfig); + const child = React.Children.only(children); return ( @@ -174,8 +177,7 @@ function TouchableHighlight(props: Props, forwardedRef): React.Node { }} accessible={accessible !== false} focusable={focusable !== false && onPress !== undefined} - forwardedRef={hostRef} - ref={viewRef} + ref={setRef} style={[ styles.root, style, diff --git a/packages/react-native-web/src/exports/TouchableOpacity/index.js b/packages/react-native-web/src/exports/TouchableOpacity/index.js index 60e4dc82..355545b2 100644 --- a/packages/react-native-web/src/exports/TouchableOpacity/index.js +++ b/packages/react-native-web/src/exports/TouchableOpacity/index.js @@ -14,8 +14,9 @@ import type { Props as TouchableWithoutFeedbackProps } from '../TouchableWithout import type { ViewProps } from '../View'; import * as React from 'react'; -import { useCallback, useMemo, useState, useRef, useImperativeHandle } from 'react'; +import { useCallback, useMemo, useState, useRef } from 'react'; import usePressEvents from '../../modules/PressResponder/usePressEvents'; +import setAndForwardRef from '../../modules/setAndForwardRef'; import StyleSheet from '../StyleSheet'; import View from '../View'; @@ -56,8 +57,12 @@ function TouchableOpacity(props: Props, forwardedRef): React.Node { } = props; const hostRef = useRef(null); - const viewRef = useRef | null>(null); - useImperativeHandle(forwardedRef, () => viewRef.current); + const setRef = setAndForwardRef({ + getForwardedRef: () => forwardedRef, + setLocalRef: hostNode => { + hostRef.current = hostNode; + } + }); const styleOpacity = getStyleOpacityWithDefault(style); const [duration, setDuration] = useState('0s'); @@ -85,46 +90,45 @@ function TouchableOpacity(props: Props, forwardedRef): React.Node { [setOpacityTo, styleOpacity] ); - const pressEventHandlers = usePressEvents( - hostRef, - useMemo( - () => ({ - cancelable: !rejectResponderTermination, - disabled, - delayLongPress, - delayPressStart: delayPressIn, - delayPressEnd: delayPressOut, - onLongPress, - onPress, - onPressStart(event) { - opacityActive(event.dispatchConfig.registrationName === 'onResponderGrant' ? 0 : 150); - if (onPressIn != null) { - onPressIn(event); - } - }, - onPressEnd(event) { - opacityInactive(250); - if (onPressOut != null) { - onPressOut(event); - } + const pressConfig = useMemo( + () => ({ + cancelable: !rejectResponderTermination, + disabled, + delayLongPress, + delayPressStart: delayPressIn, + delayPressEnd: delayPressOut, + onLongPress, + onPress, + onPressStart(event) { + opacityActive(event.dispatchConfig.registrationName === 'onResponderGrant' ? 0 : 150); + if (onPressIn != null) { + onPressIn(event); } - }), - [ - delayLongPress, - delayPressIn, - delayPressOut, - disabled, - onLongPress, - onPress, - onPressIn, - onPressOut, - opacityActive, - opacityInactive, - rejectResponderTermination - ] - ) + }, + onPressEnd(event) { + opacityInactive(250); + if (onPressOut != null) { + onPressOut(event); + } + } + }), + [ + delayLongPress, + delayPressIn, + delayPressOut, + disabled, + onLongPress, + onPress, + onPressIn, + onPressOut, + opacityActive, + opacityInactive, + rejectResponderTermination + ] ); + const pressEventHandlers = usePressEvents(hostRef, pressConfig); + return ( |}>; -const PASSTHROUGH_PROPS = [ - 'accessibilityLabel', - 'accessibilityLiveRegion', - 'accessibilityRole', - 'accessibilityState', - 'accessibilityValue', - 'hitSlop', - 'importantForAccessibility', - 'nativeID', - 'onBlur', - 'onFocus', - 'onLayout', - 'testID' -]; +const forwardPropsList = { + accessibilityLabel: true, + accessibilityLiveRegion: true, + accessibilityRole: true, + accessibilityState: true, + accessibilityValue: true, + accessible: true, + children: true, + disabled: true, + focusable: true, + hitSlop: true, + importantForAccessibility: true, + nativeID: true, + onBlur: true, + onFocus: true, + onLayout: true, + testID: true +}; + +const pickProps = props => pick(props, forwardPropsList); function TouchableWithoutFeedback(props: Props, forwardedRef): React.Node { const { @@ -75,56 +83,49 @@ function TouchableWithoutFeedback(props: Props, forwardedRef): React.Node { } = props; const hostRef = useRef(null); - const viewRef = useRef(null); - useImperativeHandle(forwardedRef, () => viewRef.current); + const setRef = setAndForwardRef({ + getForwardedRef: () => forwardedRef, + setLocalRef: hostNode => { + hostRef.current = hostNode; + } + }); - const pressEventHandlers = usePressEvents( - hostRef, - useMemo( - () => ({ - cancelable: !rejectResponderTermination, - disabled, - delayLongPress, - delayPressStart: delayPressIn, - delayPressEnd: delayPressOut, - onLongPress, - onPress, - onPressStart: onPressIn, - onPressEnd: onPressOut - }), - [ - disabled, - delayPressIn, - delayPressOut, - delayLongPress, - onLongPress, - onPress, - onPressIn, - onPressOut, - rejectResponderTermination - ] - ) + const pressConfig = useMemo( + () => ({ + cancelable: !rejectResponderTermination, + disabled, + delayLongPress, + delayPressStart: delayPressIn, + delayPressEnd: delayPressOut, + onLongPress, + onPress, + onPressStart: onPressIn, + onPressEnd: onPressOut + }), + [ + disabled, + delayPressIn, + delayPressOut, + delayLongPress, + onLongPress, + onPress, + onPressIn, + onPressOut, + rejectResponderTermination + ] ); + const pressEventHandlers = usePressEvents(hostRef, pressConfig); + const element = React.Children.only(props.children); const children = [element.props.children]; - const elementProps: { [string]: mixed, ... } = { - ...pressEventHandlers, - accessible: accessible !== false, - accessibilityState: { - disabled, - ...props.accessibilityState - }, - focusable: focusable !== false && onPress !== undefined, - forwardedRef: hostRef, - ref: viewRef - }; + const supportedProps = pickProps(props); + supportedProps.accessible = accessible !== false; + supportedProps.accessibilityState = { disabled, ...props.accessibilityState }; + supportedProps.focusable = focusable !== false && onPress !== undefined; + supportedProps.ref = setRef; - for (const prop of PASSTHROUGH_PROPS) { - if (props[prop] !== undefined) { - elementProps[prop] = props[prop]; - } - } + const elementProps = Object.assign(supportedProps, pressEventHandlers); return React.cloneElement(element, elementProps, ...children); } diff --git a/packages/react-native-web/src/exports/View/index.js b/packages/react-native-web/src/exports/View/index.js index 510d167b..5e8a81bc 100644 --- a/packages/react-native-web/src/exports/View/index.js +++ b/packages/react-native-web/src/exports/View/index.js @@ -10,17 +10,73 @@ import type { ViewProps } from './types'; +import * as React from 'react'; +import { forwardRef, useContext, useRef } from 'react'; import createElement from '../createElement'; import css from '../StyleSheet/css'; +import pick from '../../modules/pick'; import setAndForwardRef from '../../modules/setAndForwardRef'; import useElementLayout from '../../hooks/useElementLayout'; import usePlatformMethods from '../../hooks/usePlatformMethods'; import useResponderEvents from '../../hooks/useResponderEvents'; import StyleSheet from '../StyleSheet'; import TextAncestorContext from '../Text/TextAncestorContext'; -import React, { forwardRef, useContext, useRef } from 'react'; -export type { ViewProps }; +const forwardPropsList = { + accessibilityLabel: true, + accessibilityLiveRegion: true, + accessibilityRelationship: true, + accessibilityRole: true, + accessibilityState: true, + accessibilityValue: true, + accessible: true, + children: true, + classList: true, + disabled: true, + importantForAccessibility: true, + nativeID: true, + onBlur: true, + onClick: true, + onClickCapture: true, + onContextMenu: true, + onFocus: true, + onKeyDown: true, + onKeyUp: true, + onTouchCancel: true, + onTouchCancelCapture: true, + onTouchEnd: true, + onTouchEndCapture: true, + onTouchMove: true, + onTouchMoveCapture: true, + onTouchStart: true, + onTouchStartCapture: true, + pointerEvents: true, + ref: true, + style: true, + testID: true, + // unstable + onMouseDown: true, + onMouseEnter: true, + onMouseLeave: true, + onMouseMove: true, + onMouseOver: true, + onMouseOut: true, + onMouseUp: true, + onScroll: true, + onWheel: true, + href: true, + itemID: true, + itemRef: true, + itemProp: true, + itemScope: true, + itemType: true, + rel: true, + target: true, + unstable_ariaSet: true, + unstable_dataSet: true +}; + +const pickProps = props => pick(props, forwardPropsList); function createHitSlopElement(hitSlop) { const hitSlopStyle = {}; @@ -36,23 +92,9 @@ function createHitSlopElement(hitSlop) { }); } -const View = forwardRef((props, ref) => { +const View = forwardRef((props, forwardedRef) => { const { - accessibilityLabel, - accessibilityLiveRegion, - accessibilityRelationship, - accessibilityRole, - accessibilityState, - accessibilityValue, - accessible, - forwardedRef, hitSlop, - importantForAccessibility, - nativeID, - onBlur, - onClick, - onContextMenu, - onFocus, onLayout, onMoveShouldSetResponder, onMoveShouldSetResponderCapture, @@ -69,39 +111,7 @@ const View = forwardRef((props, ref) => { onSelectionChangeShouldSetResponder, onSelectionChangeShouldSetResponderCapture, onStartShouldSetResponder, - onStartShouldSetResponderCapture, - pointerEvents, - testID, - // unstable - onKeyDown, - onKeyUp, - onMouseDown, - onMouseEnter, - onMouseLeave, - onMouseMove, - onMouseOver, - onMouseOut, - onMouseUp, - onScroll, - onTouchCancel, - onTouchCancelCapture, - onTouchEnd, - onTouchEndCapture, - onTouchMove, - onTouchMoveCapture, - onTouchStart, - onTouchStartCapture, - onWheel, - href, - itemID, - itemRef, - itemProp, - itemScope, - itemType, - rel, - target, - unstable_ariaSet, - unstable_dataSet + onStartShouldSetResponderCapture } = props; if (process.env.NODE_ENV !== 'production') { @@ -116,8 +126,8 @@ const View = forwardRef((props, ref) => { const hostRef = useRef(null); const setRef = setAndForwardRef({ getForwardedRef: () => forwardedRef, - setLocalRef: c => { - hostRef.current = c; + setLocalRef: hostNode => { + hostRef.current = hostNode; } }); @@ -131,7 +141,7 @@ const View = forwardRef((props, ref) => { ); useElementLayout(hostRef, onLayout); - usePlatformMethods(hostRef, ref, classList, style); + usePlatformMethods(hostRef, classList, style); useResponderEvents(hostRef, { onMoveShouldSetResponder, onMoveShouldSetResponderCapture, @@ -151,57 +161,13 @@ const View = forwardRef((props, ref) => { onStartShouldSetResponderCapture }); - return createElement('div', { - accessibilityLabel, - accessibilityLiveRegion, - accessibilityRelationship, - accessibilityRole, - accessibilityState, - accessibilityValue, - accessible, - children, - classList, - importantForAccessibility, - nativeID, - onBlur, - onClick, - onContextMenu, - onFocus, - pointerEvents, - ref: setRef, - style, - testID, - // unstable - onKeyDown, - onKeyUp, - onMouseDown, - onMouseEnter, - onMouseLeave, - onMouseMove, - onMouseOver, - onMouseOut, - onMouseUp, - onScroll, - onTouchCancel, - onTouchCancelCapture, - onTouchEnd, - onTouchEndCapture, - onTouchMove, - onTouchMoveCapture, - onTouchStart, - onTouchStartCapture, - onWheel, - href, - itemID, - itemRef, - itemProp, - itemScope, - itemType, - rel, - target, - unstable_ariaSet, - unstable_dataSet - }); + const supportedProps = pickProps(props); + supportedProps.children = children; + supportedProps.classList = classList; + supportedProps.ref = setRef; + supportedProps.style = style; + + return createElement('div', supportedProps); }); View.displayName = 'View'; @@ -240,4 +206,6 @@ const styles = StyleSheet.create({ } }); +export type { ViewProps }; + export default View; diff --git a/packages/react-native-web/src/exports/View/types.js b/packages/react-native-web/src/exports/View/types.js index be1cec3c..1c354131 100644 --- a/packages/react-native-web/src/exports/View/types.js +++ b/packages/react-native-web/src/exports/View/types.js @@ -93,14 +93,16 @@ export type ViewProps = { }, accessible?: boolean, children?: ?any, - forwardedRef?: any, hitSlop?: ?EdgeInsetsValue, importantForAccessibility?: 'auto' | 'yes' | 'no' | 'no-hide-descendants', nativeID?: ?string, onBlur?: (e: any) => void, onClick?: (e: any) => void, + onClickCapture?: (e: any) => void, onContextMenu?: (e: any) => void, onFocus?: (e: any) => void, + onKeyDown?: (e: any) => void, + onKeyUp?: (e: any) => void, onLayout?: (e: LayoutEvent) => void, onMoveShouldSetResponder?: (e: any) => boolean, onMoveShouldSetResponderCapture?: (e: any) => boolean, @@ -122,8 +124,6 @@ export type ViewProps = { style?: GenericStyleProp, testID?: ?string, // unstable - onKeyDown?: (e: any) => void, - onKeyUp?: (e: any) => void, onMouseDown?: (e: any) => void, onMouseEnter?: (e: any) => void, onMouseLeave?: (e: any) => void, diff --git a/packages/react-native-web/src/hooks/usePlatformMethods.js b/packages/react-native-web/src/hooks/usePlatformMethods.js index f5564bc4..4d975329 100644 --- a/packages/react-native-web/src/hooks/usePlatformMethods.js +++ b/packages/react-native-web/src/hooks/usePlatformMethods.js @@ -13,9 +13,8 @@ import type { ElementRef } from 'react'; import UIManager from '../exports/UIManager'; import createDOMProps from '../modules/createDOMProps'; import { useImperativeHandle, useRef } from 'react'; -import TextInputState from '../modules/TextInputState'; -function setNativeProps(node, nativeProps, classList, style, previousStyle) { +function setNativeProps(node, nativeProps, classList, style, previousStyleRef) { if (node != null && nativeProps) { const domProps = createDOMProps(null, { ...nativeProps, @@ -25,102 +24,45 @@ function setNativeProps(node, nativeProps, classList, style, previousStyle) { const nextDomStyle = domProps.style; - if (previousStyle.current != null) { + if (previousStyleRef.current != null) { if (domProps.style == null) { domProps.style = {}; } - for (const styleName in previousStyle.current) { + for (const styleName in previousStyleRef.current) { if (domProps.style[styleName] == null) { domProps.style[styleName] = ''; } } } - previousStyle.current = nextDomStyle; + previousStyleRef.current = nextDomStyle; UIManager.updateView(node, domProps); } } +/** + * Adds non-standard methods to the hode element. This is temporarily until an + * API like `ReactNative.measure(hostRef, callback)` is added to React Native. + */ export default function usePlatformMethods( hostRef: ElementRef, - ref: ElementRef, classList: Array, - style: GenericStyleProp, - extras: any + style: GenericStyleProp ) { - const previousStyle = useRef(null); + const previousStyleRef = useRef(null); useImperativeHandle( - ref, + hostRef, () => { const hostNode = hostRef.current; - return { - blur() { - UIManager.blur(hostNode); - }, - focus() { - UIManager.focus(hostNode); - }, - measure(callback) { - UIManager.measure(hostNode, callback); - }, - measureLayout(relativeToNativeNode, onFail, onSuccess) { - UIManager.measureLayout(hostNode, relativeToNativeNode, onFail, onSuccess); - }, - measureInWindow(callback) { - UIManager.measureInWindow(hostNode, callback); - }, - setNativeProps(nativeProps) { - setNativeProps(hostNode, nativeProps, classList, style, previousStyle); - } - }; - }, - [classList, hostRef, style] - ); -} - -export function usePlatformInputMethods( - hostRef: ElementRef, - ref: ElementRef, - classList: Array, - style: GenericStyleProp, - extras: any -) { - const previousStyle = useRef(null); - - useImperativeHandle( - ref, - () => { - const hostNode = hostRef.current; - return { - blur() { - UIManager.blur(hostNode); - }, - clear() { - if (hostNode != null) { - hostNode.value = ''; - } - }, - focus() { - UIManager.focus(hostNode); - }, - isFocused() { - return hostNode != null && TextInputState.currentlyFocusedField() === hostNode; - }, - measure(callback) { - UIManager.measure(hostNode, callback); - }, - measureLayout(relativeToNativeNode, onFail, onSuccess) { - UIManager.measureLayout(hostNode, relativeToNativeNode, onFail, onSuccess); - }, - measureInWindow(callback) { - UIManager.measureInWindow(hostNode, callback); - }, - setNativeProps(nativeProps) { - setNativeProps(hostNode, nativeProps, classList, style, previousStyle); - } - }; + hostNode.measure = callback => UIManager.measure(hostNode, callback); + hostNode.measureLayout = (relativeToNode, success, failure) => + UIManager.measureLayout(hostNode, relativeToNode, failure, success); + hostNode.measureInWindow = callback => UIManager.measureInWindow(hostNode, callback); + hostNode.setNativeProps = nativeProps => + setNativeProps(hostNode, nativeProps, classList, style, previousStyleRef); + return hostNode; }, [classList, hostRef, style] ); diff --git a/packages/react-native-web/src/modules/pick/index.js b/packages/react-native-web/src/modules/pick/index.js new file mode 100644 index 00000000..a53ad436 --- /dev/null +++ b/packages/react-native-web/src/modules/pick/index.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) Nicolas Gallagher. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +export default function pick(obj: Object, list: { [string]: boolean }): Object { + const nextObj = {}; + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + if (list[key] === true) { + nextObj[key] = obj[key]; + } + } + } + return nextObj; +}