create-expo-cljs-app
Version:
Create a react native application with Expo and Shadow-CLJS!
272 lines (253 loc) • 8.2 kB
Flow
/**
* Copyright (c) Nicolas Gallagher.
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import type { PlatformMethods } from '../../types';
import type { TextInputProps } from './types';
import * as React from 'react';
import createElement from '../createElement';
import css from '../StyleSheet/css';
import * as forwardedProps from '../../modules/forwardedProps';
import pick from '../../modules/pick';
import useElementLayout from '../../modules/useElementLayout';
import useLayoutEffect from '../../modules/useLayoutEffect';
import useMergeRefs from '../../modules/useMergeRefs';
import usePlatformMethods from '../../modules/usePlatformMethods';
import useResponderEvents from '../../modules/useResponderEvents';
import StyleSheet from '../StyleSheet';
import TextInputState from '../../modules/TextInputState';
/**
* Determines whether a 'selection' prop differs from a node's existing
* selection state.
*/
declare var isSelectionStale: (node: any, selection: any) => any;
/**
* Certain input types do no support 'selectSelectionRange' and will throw an
* error.
*/
declare var setSelection: (node: any, selection: any) => any;
const forwardPropsList = { ...forwardedProps.defaultProps,
...forwardedProps.accessibilityProps,
...forwardedProps.clickProps,
...forwardedProps.focusProps,
...forwardedProps.keyboardProps,
...forwardedProps.mouseProps,
...forwardedProps.touchProps,
...forwardedProps.styleProps,
autoCapitalize: true,
autoComplete: true,
autoCorrect: true,
autoFocus: true,
defaultValue: true,
disabled: true,
lang: true,
maxLength: true,
onChange: true,
onScroll: true,
placeholder: true,
pointerEvents: true,
readOnly: true,
rows: true,
spellCheck: true,
value: true,
type: true
};
declare var pickProps: (props: any) => any; // If an Input Method Editor is processing key input, the 'keyCode' is 229.
// https://www.w3.org/TR/uievents/#determine-keydown-keyup-keyCode
declare function isEventComposing(nativeEvent: any): any;
const TextInput: React.AbstractComponent<TextInputProps, HTMLElement & PlatformMethods> = React.forwardRef((props, forwardedRef) => {
const {
autoCapitalize = 'sentences',
autoComplete,
autoCompleteType,
autoCorrect = true,
blurOnSubmit,
clearTextOnFocus,
dir,
editable = true,
keyboardType = 'default',
multiline = false,
numberOfLines = 1,
onBlur,
onChange,
onChangeText,
onContentSizeChange,
onFocus,
onKeyPress,
onLayout,
onMoveShouldSetResponder,
onMoveShouldSetResponderCapture,
onResponderEnd,
onResponderGrant,
onResponderMove,
onResponderReject,
onResponderRelease,
onResponderStart,
onResponderTerminate,
onResponderTerminationRequest,
onScrollShouldSetResponder,
onScrollShouldSetResponderCapture,
onSelectionChange,
onSelectionChangeShouldSetResponder,
onSelectionChangeShouldSetResponderCapture,
onStartShouldSetResponder,
onStartShouldSetResponderCapture,
onSubmitEditing,
placeholderTextColor,
returnKeyType,
secureTextEntry = false,
selection,
selectTextOnFocus,
spellCheck
} = props;
let type;
let inputMode;
switch (keyboardType) {
case 'email-address':
type = 'email';
break;
case 'number-pad':
case 'numeric':
inputMode = 'numeric';
break;
case 'decimal-pad':
inputMode = 'decimal';
break;
case 'phone-pad':
type = 'tel';
break;
case 'search':
case 'web-search':
type = 'search';
break;
case 'url':
type = 'url';
break;
default:
type = 'text';
}
if (secureTextEntry) {
type = 'password';
}
const dimensions = React.useRef({
height: null,
width: null
});
const hostRef = React.useRef(null);
const handleContentSizeChange = React.useCallback(hostNode => {
if (multiline && onContentSizeChange && hostNode != null) {
const newHeight = hostNode.scrollHeight;
const newWidth = hostNode.scrollWidth;
if (newHeight !== dimensions.current.height || newWidth !== dimensions.current.width) {
dimensions.current.height = newHeight;
dimensions.current.width = newWidth;
onContentSizeChange({
nativeEvent: {
contentSize: {
height: dimensions.current.height,
width: dimensions.current.width
}
}
});
}
}
}, [multiline, onContentSizeChange]);
const imperativeRef = React.useMemo(() => 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;
};
handleContentSizeChange(hostNode);
}
}, [handleContentSizeChange]);
declare function handleBlur(e: any): any;
declare function handleChange(e: any): any;
declare function handleFocus(e: any): any;
declare function handleKeyDown(e: any): any;
declare function handleSelectionChange(e: any): any;
useLayoutEffect(() => {
const node = hostRef.current;
if (node != null && selection != null) {
setSelection(node, selection);
}
if (document.activeElement === node) {
TextInputState._currentlyFocusedNode = node;
}
}, [hostRef, selection]);
const component = multiline ? 'textarea' : 'input';
const classList = [classes.textinput];
const style = StyleSheet.compose(props.style, placeholderTextColor && {
placeholderTextColor
});
useElementLayout(hostRef, onLayout);
useResponderEvents(hostRef, {
onMoveShouldSetResponder,
onMoveShouldSetResponderCapture,
onResponderEnd,
onResponderGrant,
onResponderMove,
onResponderReject,
onResponderRelease,
onResponderStart,
onResponderTerminate,
onResponderTerminationRequest,
onScrollShouldSetResponder,
onScrollShouldSetResponderCapture,
onSelectionChangeShouldSetResponder,
onSelectionChangeShouldSetResponderCapture,
onStartShouldSetResponder,
onStartShouldSetResponderCapture
});
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.rows = multiline ? numberOfLines : undefined;
supportedProps.spellCheck = spellCheck != null ? spellCheck : autoCorrect;
supportedProps.style = style;
supportedProps.type = multiline ? undefined : type;
supportedProps.inputMode = inputMode;
const platformMethodsRef = usePlatformMethods(supportedProps);
const setRef = useMergeRefs(hostRef, platformMethodsRef, imperativeRef, forwardedRef);
supportedProps.ref = setRef;
return createElement(component, supportedProps);
});
TextInput.displayName = 'TextInput'; // $FlowFixMe
TextInput.State = TextInputState;
const classes = css.create({
textinput: {
MozAppearance: 'textfield',
WebkitAppearance: 'none',
backgroundColor: 'transparent',
border: '0 solid black',
borderRadius: 0,
boxSizing: 'border-box',
font: '14px System',
margin: 0,
padding: 0,
resize: 'none'
}
});
export default TextInput;