UNPKG

hypertoxin

Version:

A themeable ReactNative component library for developing native apps

1,280 lines (1,235 loc) 93.8 kB
/** * Copyright 2016-present Tuan Le. * * Licensed under the MIT License. * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://opensource.org/licenses/mit-license.html * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *------------------------------------------------------------------------ * * @module TextField * @description - Text field input component. * * @author Tuan Le (tuan.t.lei@gmail.com) * * @flow */ 'use strict'; // eslint-disable-line import React from 'react'; import ReactNative from 'react-native'; // eslint-disable-line import PropTypes from 'prop-types'; import { Text as AnimatedText, View as AnimatedView } from 'react-native-animatable'; import dismissKeyboard from 'react-native/Libraries/Utilities/dismissKeyboard'; import { DefaultTheme, DefaultThemeContext } from '../../themes/default-theme'; const { Dimensions, FlatList, Text, TextInput, View } = ReactNative; const DEVICE_WIDTH = Dimensions.get(`window`).width; const DEVICE_HEIGHT = Dimensions.get(`window`).height; const DEFAULT_ANIMATION_DURATION_MS = 300; const DEFAULT_TEXT_FIELD_STYLE = { small: { container: { flexShrink: 1, flexDirection: `column`, alignSelf: `stretch`, alignItems: `stretch`, justifyContent: `center`, width: `100%`, maxWidth: DEVICE_WIDTH, padding: 3, backgroundColor: `transparent` }, box: { focused: { flexDirection: `column`, alignItems: `flex-end`, justifyContent: `flex-end`, height: DefaultTheme.field.size.text.input.small * 1.65, paddingVertical: 3, paddingLeft: 6, overflow: `hidden` }, blurred: { flexDirection: `column`, alignItems: `flex-end`, justifyContent: `flex-end`, height: DefaultTheme.field.size.text.input.small * 1.65, paddingVertical: 3, paddingLeft: 6, overflow: `hidden` } }, contentLeftRoom: { flexDirection: `row`, alignItems: `center`, justifyContent: `center`, maxHeight: DefaultTheme.field.size.text.input.small - 6, backgroundColor: `transparent` }, contentRightRoom: { flexDirection: `row`, alignItems: `center`, justifyContent: `center`, maxHeight: DefaultTheme.field.size.text.input.small - 6, backgroundColor: `transparent` }, input: { ...DefaultTheme.field.font.text.input.small, flexGrow: 1, textAlign: `left`, alignSelf: `stretch`, alignItems: `stretch`, maxWidth: DEVICE_WIDTH, height: DefaultTheme.field.size.text.input.small, paddingLeft: 6, backgroundColor: `transparent` }, helper: { ...DefaultTheme.field.font.text.helper, textAlign: `right`, paddingRight: 9 }, status: { ...DefaultTheme.field.font.text.status, textAlign: `left`, paddingLeft: 6, color: DefaultTheme.field.color.text.status }, label: { focused: { ...DefaultTheme.field.font.text.label.focused.small, position: `absolute`, textAlign: `left`, paddingLeft: 6 }, blurred: { ...DefaultTheme.field.font.text.label.blurred.small, position: `absolute`, textAlign: `left`, paddingLeft: 6 } }, underline: { focused: { justifyContent: `flex-end`, height: DefaultTheme.field.size.text.underline.focused.small, transform: [{ scaleX: 0 }] }, blurred: { justifyContent: `flex-end`, height: DefaultTheme.field.size.text.underline.blurred.small } }, selection: { position: `absolute`, flexDirection: `column`, alignItems: `stretch`, justifyContent: `center`, maxWidth: DEVICE_WIDTH, maxHeight: DEVICE_HEIGHT / 4, zIndex: 10, opacity: 0, transform: [{ translateX: -DEVICE_WIDTH }] } }, normal: { container: { flexShrink: 1, flexDirection: `column`, alignSelf: `stretch`, alignItems: `stretch`, justifyContent: `center`, width: `100%`, maxWidth: DEVICE_WIDTH, padding: 3, backgroundColor: `transparent` }, box: { focused: { flexDirection: `column`, alignItems: `flex-end`, justifyContent: `flex-end`, height: DefaultTheme.field.size.text.input.normal * 1.65, paddingVertical: 3, paddingLeft: 6, overflow: `hidden` }, blurred: { flexDirection: `column`, alignItems: `flex-end`, justifyContent: `flex-end`, height: DefaultTheme.field.size.text.input.normal * 1.65, paddingVertical: 3, paddingLeft: 6, overflow: `hidden` } }, contentLeftRoom: { flexDirection: `row`, alignItems: `center`, justifyContent: `center`, maxHeight: DefaultTheme.field.size.text.input.normal - 6, backgroundColor: `transparent` }, contentRightRoom: { flexDirection: `row`, alignItems: `center`, justifyContent: `center`, maxHeight: DefaultTheme.field.size.text.input.normal - 6, backgroundColor: `transparent` }, input: { ...DefaultTheme.field.font.text.input.normal, flexGrow: 1, textAlign: `left`, alignSelf: `stretch`, alignItems: `stretch`, maxWidth: DEVICE_WIDTH, height: DefaultTheme.field.size.text.input.normal, paddingLeft: 6, backgroundColor: `transparent` }, helper: { ...DefaultTheme.field.font.text.helper, textAlign: `right`, paddingRight: 9 }, status: { ...DefaultTheme.field.font.text.status, textAlign: `left`, paddingLeft: 6, color: DefaultTheme.field.color.text.status }, label: { focused: { ...DefaultTheme.field.font.text.label.focused.normal, position: `absolute`, textAlign: `left`, paddingLeft: 6 }, blurred: { ...DefaultTheme.field.font.text.label.blurred.normal, position: `absolute`, textAlign: `left`, paddingLeft: 6 } }, underline: { focused: { justifyContent: `flex-end`, height: DefaultTheme.field.size.text.underline.focused.normal, transform: [{ scaleX: 0 }] }, blurred: { justifyContent: `flex-end`, height: DefaultTheme.field.size.text.underline.blurred.normal } }, selection: { position: `absolute`, flexDirection: `column`, alignItems: `stretch`, justifyContent: `center`, maxWidth: DEVICE_WIDTH, maxHeight: DEVICE_HEIGHT / 4, zIndex: 10, opacity: 0, transform: [{ translateX: -DEVICE_WIDTH }] } }, large: { container: { flexShrink: 1, flexDirection: `column`, alignSelf: `stretch`, alignItems: `stretch`, justifyContent: `center`, width: `100%`, maxWidth: DEVICE_WIDTH, padding: 3, backgroundColor: `transparent` }, box: { focused: { flexDirection: `column`, alignItems: `flex-end`, justifyContent: `flex-end`, height: DefaultTheme.field.size.text.input.large * 1.65, paddingVertical: 3, paddingLeft: 6, overflow: `hidden` }, blurred: { flexDirection: `column`, alignItems: `flex-end`, justifyContent: `flex-end`, height: DefaultTheme.field.size.text.input.large * 1.65, paddingVertical: 3, paddingLeft: 6, overflow: `hidden` } }, contentLeftRoom: { flexDirection: `row`, alignItems: `center`, justifyContent: `center`, maxHeight: DefaultTheme.field.size.text.input.large - 6, backgroundColor: `transparent` }, contentRightRoom: { flexDirection: `row`, alignItems: `center`, justifyContent: `center`, maxHeight: DefaultTheme.field.size.text.input.large - 6, backgroundColor: `transparent` }, input: { ...DefaultTheme.field.font.text.input.large, flexGrow: 1, textAlign: `left`, alignSelf: `stretch`, alignItems: `stretch`, maxWidth: DEVICE_WIDTH, height: DefaultTheme.field.size.text.input.large, paddingLeft: 6, backgroundColor: `transparent` }, helper: { ...DefaultTheme.field.font.text.helper, textAlign: `right`, paddingRight: 9 }, status: { ...DefaultTheme.field.font.text.status, textAlign: `left`, paddingLeft: 6, color: DefaultTheme.field.color.text.status }, label: { focused: { ...DefaultTheme.field.font.text.label.focused.large, position: `absolute`, textAlign: `left`, paddingLeft: 6 }, blurred: { ...DefaultTheme.field.font.text.label.blurred.large, position: `absolute`, textAlign: `left`, paddingLeft: 6 } }, underline: { focused: { justifyContent: `flex-end`, height: DefaultTheme.field.size.text.underline.focused.large, transform: [{ scaleX: 0 }] }, blurred: { justifyContent: `flex-end`, height: DefaultTheme.field.size.text.underline.blurred.large } }, selection: { position: `absolute`, flexDirection: `column`, alignItems: `stretch`, justifyContent: `center`, maxWidth: DEVICE_WIDTH, maxHeight: DEVICE_HEIGHT / 4, zIndex: 10, opacity: 0, transform: [{ translateX: -DEVICE_WIDTH }] } } }; const isEmptyInputValue = (value) => { if (typeof value === `object`) { return Object.getOwnPropertyNames(value).length === 0; } else if (Array.isArray(value) || typeof value === `string`) { return value.length === 0; } return true; }; const readjustStyle = (newStyle = { shade: `themed`, overlay: `themed`, corner: `themed`, margin: `themed`, disabled: false }, prevAdjustedStyle = DEFAULT_TEXT_FIELD_STYLE, Theme = DefaultTheme) => { const { shade, overlay, corner, margin, disabled, style } = newStyle; const themedShade = shade === `themed` ? Theme.field.text.shade : shade; const themedOverlay = overlay === `themed` ? Theme.field.text.overlay : overlay; const themedSelectionColor = Theme.field.color.text.box[themedShade]; const nullMargin = { margin: null, marginTop: null, marginBottom: null, marginLeft: null, marginRight: null, marginHorizontal: null, marginVertical: null }; const nullBorderRadius = { borderTopLeftRadius: null, borderTopRightRadius: null, borderBottomLeftRadius: null, borderBottomRightRadius: null }; let themedBoxColor; let themedBoxBorderFocusColor; let themedBoxBorderBlurColor; let themedBorderRadius; let themedBorderWidth = 0; let themedInputColor; let themedHelperColor; let themedFocusColor; let themedBlurColor; let themedCorner; let themedMargin; if (disabled) { themedFocusColor = Theme.field.color.text.disabled[themedShade]; themedBlurColor = themedFocusColor; themedInputColor = themedFocusColor; } else { themedFocusColor = Theme.field.color.text.focused[themedShade]; themedBlurColor = Theme.field.color.text.blurred[themedShade]; themedInputColor = Theme.field.color.text.input[themedShade]; } themedHelperColor = Theme.field.color.text.helper[themedShade]; switch (themedOverlay) { // eslint-disable-line case `opaque`: themedBoxColor = Theme.field.color.text.box[themedShade]; themedBoxBorderFocusColor = `transparent`; themedBoxBorderBlurColor = `transparent`; break; case `translucent`: themedBoxColor = `${Theme.field.color.text.box[themedShade]}${Theme.field.color.text.opacity}`; themedBoxBorderFocusColor = `transparent`; themedBoxBorderBlurColor = `transparent`; break; case `translucent-outline`: themedBoxColor = `${Theme.field.color.text.box[themedShade]}${Theme.field.color.text.opacity}`; themedBoxBorderFocusColor = themedFocusColor; themedBoxBorderBlurColor = themedBlurColor; themedBorderWidth = 1; break; case `transparent`: themedBoxColor = `transparent`; themedBoxBorderFocusColor = `transparent`; themedBoxBorderBlurColor = `transparent`; break; case `transparent-outline`: themedBoxColor = `transparent`; themedBoxBorderFocusColor = themedFocusColor; themedBoxBorderBlurColor = themedBlurColor; themedBorderWidth = 1; break; } if (typeof corner === `string`) { if (corner === `themed`) { if (typeof Theme.field.text.corner === `string` && Theme.field.corner.text.hasOwnProperty(Theme.field.text.corner)) { themedCorner = Theme.field.corner.text[Theme.field.text.corner]; } else { themedCorner = Theme.field.text.corner; } } else if (Theme.field.corner.text.hasOwnProperty(corner)) { themedCorner = Theme.field.corner.text[corner]; } else { themedCorner = 0; } } else { themedCorner = corner; } if (typeof themedCorner === `number`) { themedBorderRadius = { small: { borderRadius: themedCorner }, normal: { borderRadius: themedCorner }, large: { borderRadius: themedCorner } }; } else if (typeof themedCorner === `object`) { if (themedCorner.hasOwnProperty(`small`) && typeof themedCorner.small === `object` && themedCorner.hasOwnProperty(`normal`) && typeof themedCorner.normal === `object` && themedCorner.hasOwnProperty(`large`) && typeof themedCorner.large === `object`) { themedBorderRadius = { small: Object.entries(themedCorner.small).reduce((_themedBorderRadius, [ key, value ]) => { let _borderRadius = nullBorderRadius; _borderRadius[`border${key.charAt(0).toUpperCase()}${key.slice(1)}Radius`] = value; return { ..._themedBorderRadius, ..._borderRadius }; }, nullBorderRadius), normal: Object.entries(themedCorner.normal).reduce((_themedBorderRadius, [ key, value ]) => { let _borderRadius = nullBorderRadius; _borderRadius[`border${key.charAt(0).toUpperCase()}${key.slice(1)}Radius`] = value; return { ..._themedBorderRadius, ..._borderRadius }; }, nullBorderRadius), large: Object.entries(themedCorner.large).reduce((_themedBorderRadius, [ key, value ]) => { let _borderRadius = nullBorderRadius; _borderRadius[`border${key.charAt(0).toUpperCase()}${key.slice(1)}Radius`] = value; return { ..._themedBorderRadius, ..._borderRadius }; }, nullBorderRadius) }; } else if (themedCorner.hasOwnProperty(`small`) && typeof themedCorner.small === `number` && themedCorner.hasOwnProperty(`normal`) && typeof themedCorner.normal === `number` && themedCorner.hasOwnProperty(`large`) && typeof themedCorner.large === `number`) { themedBorderRadius = { small: { borderRadius: themedCorner.small }, normal: { borderRadius: themedCorner.normal }, large: { borderRadius: themedCorner.large } }; } else { themedBorderRadius = { small: { ...nullBorderRadius, ...themedCorner }, normal: { ...nullBorderRadius, ...themedCorner }, large: { ...nullBorderRadius, ...themedCorner } }; } } if (typeof margin === `string`) { if (margin === `themed`) { if (typeof Theme.field.text.margin === `string` && Theme.field.margin.text.hasOwnProperty(Theme.field.text.margin)) { themedMargin = Theme.field.margin.text[Theme.field.text.margin]; } else { themedMargin = Theme.field.text.margin; } } else if (Theme.field.margin.text.hasOwnProperty(margin)) { themedMargin = Theme.field.margin.text[margin]; } else { themedMargin = 0; } } else { themedMargin = margin; } if (typeof themedMargin === `number`) { themedMargin = { small: { ...nullMargin, margin: themedMargin }, normal: { ...nullMargin, margin: themedMargin }, large: { ...nullMargin, margin: themedMargin } }; } else if (typeof themedMargin === `object`) { themedMargin = Object.entries(themedMargin).reduce((_themedMargin, [ key, value ]) => { let _margin = nullMargin; _margin[`margin${key.charAt(0).toUpperCase()}${key.slice(1)}`] = value; _themedMargin = { small: { ..._themedMargin.small, ..._margin }, normal: { ..._themedMargin.normal, ..._margin }, large: { ..._themedMargin.large, ..._margin } }; return _themedMargin; }, { small: nullMargin, normal: nullMargin, large: nullMargin }); } return { small: { container: { ...prevAdjustedStyle.small.container, ...themedMargin.small, ...(typeof style === `object` && style.hasOwnProperty(`container`) && typeof style.container === `object` ? style.container : {}) }, box: { focused: { ...prevAdjustedStyle.small.box.focused, ...themedBorderRadius.small, height: Theme.field.size.text.input.small * 1.65, borderColor: themedBoxBorderFocusColor, borderWidth: themedBorderWidth, backgroundColor: themedBoxColor, ...(typeof style === `object` && style.hasOwnProperty(`box`) && typeof style.box === `object` && style.box.hasOwnProperty(`focused`) && typeof style.box.focused === `object` ? style.box.focused : {}) }, blurred: { ...prevAdjustedStyle.small.box.blurred, ...themedBorderRadius.small, height: Theme.field.size.text.input.small * 1.65, borderColor: themedBoxBorderBlurColor, borderWidth: themedBorderWidth, backgroundColor: themedBoxColor, ...(typeof style === `object` && style.hasOwnProperty(`box`) && typeof style.box === `object` && style.box.hasOwnProperty(`blurred`) && typeof style.box.blurred === `object` ? style.box.blurred : {}) } }, contentLeftRoom: { ...prevAdjustedStyle.small.contentLeftRoom, maxHeight: Theme.field.size.text.input.small - 6, ...(typeof style === `object` && style.hasOwnProperty(`contentLeftRoom`) && typeof style.contentLeftRoom === `object` ? style.contentLeftRoom : {}) }, contentRightRoom: { ...prevAdjustedStyle.small.contentRightRoom, maxHeight: Theme.field.size.text.input.small - 6, ...(typeof style === `object` && style.hasOwnProperty(`contentRightRoom`) && typeof style.contentRightRoom === `object` ? style.contentRightRoom : {}) }, input: { ...prevAdjustedStyle.small.input, ...Theme.field.font.text.input.small, height: Theme.field.size.text.input.small, color: themedInputColor, ...(typeof style === `object` && style.hasOwnProperty(`input`) && typeof style.input === `object` ? style.input : {}) }, helper: { ...prevAdjustedStyle.small.helper, ...Theme.field.font.text.helper, color: themedHelperColor, ...(typeof style === `object` && style.hasOwnProperty(`helper`) && typeof style.helper === `object` ? style.helper : {}) }, status: { ...prevAdjustedStyle.small.status, ...Theme.field.font.text.status, color: Theme.field.color.text.status, ...(typeof style === `object` && style.hasOwnProperty(`status`) && typeof style.status === `object` ? style.status : {}) }, label: { focused: { ...prevAdjustedStyle.small.label.focused, ...Theme.field.font.text.label.focused.small, color: themedFocusColor, ...(typeof style === `object` && style.hasOwnProperty(`label`) && typeof style.label === `object` && style.label.hasOwnProperty(`focused`) && typeof style.label.focused === `object` ? style.label.focused : {}) }, blurred: { ...prevAdjustedStyle.small.label.blurred, ...Theme.field.font.text.label.blurred.small, color: themedFocusColor, ...(typeof style === `object` && style.hasOwnProperty(`label`) && typeof style.label === `object` && style.label.hasOwnProperty(`blurred`) && typeof style.label.blurred === `object` ? style.label.blurred : {}) } }, underline: { focused: { ...prevAdjustedStyle.small.underline.focused, height: Theme.field.size.text.underline.focused.small, backgroundColor: themedFocusColor, ...(typeof style === `object` && style.hasOwnProperty(`underline`) && typeof style.underline === `object` && style.underline.hasOwnProperty(`focused`) && typeof style.underline.focused === `object` ? style.underline.focused : {}) }, blurred: { ...prevAdjustedStyle.small.underline.blurred, height: Theme.field.size.text.underline.blurred.small, alignItems: overlay === `transparent` ? `flex-start` : `center`, backgroundColor: themedBlurColor, ...(typeof style === `object` && style.hasOwnProperty(`underline`) && typeof style.underline === `object` && style.underline.hasOwnProperty(`blurred`) && typeof style.underline.blurred === `object` ? style.underline.blurred : {}) } }, selection: { ...prevAdjustedStyle.small.selection, // ...dropShadow, ...themedBorderRadius.small, borderColor: themedBoxBorderBlurColor, borderWidth: themedBorderWidth, backgroundColor: themedSelectionColor, ...(typeof style === `object` && style.hasOwnProperty(`selection`) && typeof style.selection === `object` ? style.selection : {}) } }, normal: { container: { ...prevAdjustedStyle.normal.container, ...themedMargin.normal, ...(typeof style === `object` && style.hasOwnProperty(`container`) && typeof style.container === `object` ? style.container : {}) }, box: { focused: { ...prevAdjustedStyle.normal.box.focused, ...themedBorderRadius.normal, height: Theme.field.size.text.input.normal * 1.65, borderColor: themedBoxBorderFocusColor, borderWidth: themedBorderWidth, backgroundColor: themedBoxColor, ...(typeof style === `object` && style.hasOwnProperty(`box`) && typeof style.box === `object` && style.box.hasOwnProperty(`focused`) && typeof style.box.focused === `object` ? style.box.focused : {}) }, blurred: { ...prevAdjustedStyle.normal.box.blurred, ...themedBorderRadius.normal, height: Theme.field.size.text.input.normal * 1.65, borderColor: themedBoxBorderBlurColor, borderWidth: themedBorderWidth, backgroundColor: themedBoxColor, ...(typeof style === `object` && style.hasOwnProperty(`box`) && typeof style.box === `object` && style.box.hasOwnProperty(`blurred`) && typeof style.box.blurred === `object` ? style.box.blurred : {}) } }, contentLeftRoom: { ...prevAdjustedStyle.normal.contentLeftRoom, maxHeight: Theme.field.size.text.input.normal - 6, ...(typeof style === `object` && style.hasOwnProperty(`contentLeftRoom`) && typeof style.contentLeftRoom === `object` ? style.contentLeftRoom : {}) }, contentRightRoom: { ...prevAdjustedStyle.normal.contentRightRoom, maxHeight: Theme.field.size.text.input.normal - 6, ...(typeof style === `object` && style.hasOwnProperty(`contentRightRoom`) && typeof style.contentRightRoom === `object` ? style.contentRightRoom : {}) }, input: { ...prevAdjustedStyle.normal.input, ...Theme.field.font.text.input.normal, height: Theme.field.size.text.input.normal, color: themedInputColor, ...(typeof style === `object` && style.hasOwnProperty(`input`) && typeof style.input === `object` ? style.input : {}) }, helper: { ...prevAdjustedStyle.normal.helper, ...Theme.field.font.text.helper, color: themedHelperColor, ...(typeof style === `object` && style.hasOwnProperty(`helper`) && typeof style.helper === `object` ? style.helper : {}) }, status: { ...prevAdjustedStyle.normal.status, ...Theme.field.font.text.status, color: Theme.field.color.text.status, ...(typeof style === `object` && style.hasOwnProperty(`status`) && typeof style.status === `object` ? style.status : {}) }, label: { focused: { ...prevAdjustedStyle.normal.label.focused, ...Theme.field.font.text.label.focused.normal, color: themedFocusColor, ...(typeof style === `object` && style.hasOwnProperty(`label`) && typeof style.label === `object` && style.label.hasOwnProperty(`focused`) && typeof style.label.focused === `object` ? style.label.focused : {}) }, blurred: { ...prevAdjustedStyle.normal.label.blurred, ...Theme.field.font.text.label.blurred.normal, color: themedFocusColor, ...(typeof style === `object` && style.hasOwnProperty(`label`) && typeof style.label === `object` && style.label.hasOwnProperty(`blurred`) && typeof style.label.blurred === `object` ? style.label.blurred : {}) } }, underline: { focused: { ...prevAdjustedStyle.normal.underline.focused, height: Theme.field.size.text.underline.focused.normal, backgroundColor: themedFocusColor, ...(typeof style === `object` && style.hasOwnProperty(`underline`) && typeof style.underline === `object` && style.underline.hasOwnProperty(`focused`) && typeof style.underline.focused === `object` ? style.underline.focused : {}) }, blurred: { ...prevAdjustedStyle.normal.underline.blurred, height: Theme.field.size.text.underline.blurred.normal, alignItems: overlay === `transparent` ? `flex-start` : `center`, backgroundColor: themedBlurColor, ...(typeof style === `object` && style.hasOwnProperty(`underline`) && typeof style.underline === `object` && style.underline.hasOwnProperty(`blurred`) && typeof style.underline.blurred === `object` ? style.underline.blurred : {}) } }, selection: { ...prevAdjustedStyle.normal.selection, // ...dropShadow, ...themedBorderRadius.normal, borderColor: themedBoxBorderBlurColor, borderWidth: themedBorderWidth, backgroundColor: themedSelectionColor, ...(typeof style === `object` && style.hasOwnProperty(`selection`) && typeof style.selection === `object` ? style.selection : {}) } }, large: { container: { ...prevAdjustedStyle.large.container, ...themedMargin.large, ...(typeof style === `object` && style.hasOwnProperty(`container`) && typeof style.container === `object` ? style.container : {}) }, box: { focused: { ...prevAdjustedStyle.large.box.focused, ...themedBorderRadius.large, height: Theme.field.size.text.input.large * 1.65, borderColor: themedBoxBorderFocusColor, borderWidth: themedBorderWidth, backgroundColor: themedBoxColor, ...(typeof style === `object` && style.hasOwnProperty(`box`) && typeof style.box === `object` && style.box.hasOwnProperty(`focused`) && typeof style.box.focused === `object` ? style.box.focused : {}) }, blurred: { ...prevAdjustedStyle.large.box.blurred, ...themedBorderRadius.large, height: Theme.field.size.text.input.large * 1.65, borderColor: themedBoxBorderBlurColor, borderWidth: themedBorderWidth, backgroundColor: themedBoxColor, ...(typeof style === `object` && style.hasOwnProperty(`box`) && typeof style.box === `object` && style.box.hasOwnProperty(`blurred`) && typeof style.box.blurred === `object` ? style.box.blurred : {}) } }, contentLeftRoom: { ...prevAdjustedStyle.large.contentLeftRoom, maxHeight: Theme.field.size.text.input.large - 6, ...(typeof style === `object` && style.hasOwnProperty(`contentLeftRoom`) && typeof style.contentLeftRoom === `object` ? style.contentLeftRoom : {}) }, contentRightRoom: { ...prevAdjustedStyle.large.contentRightRoom, maxHeight: Theme.field.size.text.input.large - 6, ...(typeof style === `object` && style.hasOwnProperty(`contentRightRoom`) && typeof style.contentRightRoom === `object` ? style.contentRightRoom : {}) }, input: { ...prevAdjustedStyle.large.input, ...Theme.field.font.text.input.large, height: Theme.field.size.text.input.large, color: themedInputColor, ...(typeof style === `object` && style.hasOwnProperty(`input`) && typeof style.input === `object` ? style.input : {}) }, helper: { ...prevAdjustedStyle.large.helper, ...Theme.field.font.text.helper, color: themedHelperColor, ...(typeof style === `object` && style.hasOwnProperty(`helper`) && typeof style.helper === `object` ? style.helper : {}) }, status: { ...prevAdjustedStyle.large.status, ...Theme.field.font.text.status, color: Theme.field.color.text.status, ...(typeof style === `object` && style.hasOwnProperty(`status`) && typeof style.status === `object` ? style.status : {}) }, label: { focused: { ...prevAdjustedStyle.large.label.focused, ...Theme.field.font.text.label.focused.large, color: themedFocusColor, ...(typeof style === `object` && style.hasOwnProperty(`label`) && typeof style.label === `object` && style.label.hasOwnProperty(`focused`) && typeof style.label.focused === `object` ? style.label.focused : {}) }, blurred: { ...prevAdjustedStyle.large.label.blurred, ...Theme.field.font.text.label.blurred.large, color: themedFocusColor, ...(typeof style === `object` && style.hasOwnProperty(`label`) && typeof style.label === `object` && style.label.hasOwnProperty(`blurred`) && typeof style.label.blurred === `object` ? style.label.blurred : {}) } }, underline: { focused: { ...prevAdjustedStyle.large.underline.focused, height: Theme.field.size.text.underline.focused.large, backgroundColor: themedFocusColor, ...(typeof style === `object` && style.hasOwnProperty(`underline`) && typeof style.underline === `object` && style.underline.hasOwnProperty(`focused`) && typeof style.underline.focused === `object` ? style.underline.focused : {}) }, blurred: { ...prevAdjustedStyle.large.underline.blurred, height: Theme.field.size.text.underline.blurred.large, alignItems: overlay === `transparent` ? `flex-start` : `center`, backgroundColor: themedBlurColor, ...(typeof style === `object` && style.hasOwnProperty(`underline`) && typeof style.underline === `object` && style.underline.hasOwnProperty(`blurred`) && typeof style.underline.blurred === `object` ? style.underline.blurred : {}) } }, selection: { ...prevAdjustedStyle.large.selection, // ...dropShadow, ...themedBorderRadius.large, borderColor: themedBoxBorderBlurColor, borderWidth: themedBorderWidth, backgroundColor: themedSelectionColor, ...(typeof style === `object` && style.hasOwnProperty(`selection`) && typeof style.selection === `object` ? style.selection : {}) } } }; }; export default class TextField extends React.Component { static contextType = DefaultThemeContext static propTypes = { exclusions: PropTypes.arrayOf(PropTypes.string), room: PropTypes.oneOf([ `none`, `content-left`, `content-middle`, `content-right`, `content-bottom`, `content-top`, `media` ]), shade: PropTypes.oneOf([ `themed`, `light`, `dark` ]), overlay: PropTypes.oneOf([ `themed`, `opaque`, `translucent`, `translucent-outline`, `transparent`, `transparent-outline` ]), corner: PropTypes.oneOfType([ PropTypes.string, PropTypes.number, PropTypes.shape({ topLeft: PropTypes.number, topRight: PropTypes.number, bottomLeft: PropTypes.number, bottomRight: PropTypes.number }) ]), size: PropTypes.oneOf([ `themed`, `small`, `normal`, `large` ]), margin: PropTypes.oneOfType([ PropTypes.string, PropTypes.number, PropTypes.shape({ top: PropTypes.number, bottom: PropTypes.number, left: PropTypes.number, right: PropTypes.number, horizontal: PropTypes.number, vertical: PropTypes.number }) ]), autoFocus: PropTypes.bool, autoCorrect: PropTypes.bool, secured: PropTypes.bool, underlined: PropTypes.oneOfType([ PropTypes.bool, PropTypes.string ]), disabled: PropTypes.bool, initialValue: PropTypes.oneOfType([ PropTypes.number, PropTypes.string ]), selectableValues: PropTypes.arrayOf(PropTypes.oneOfType([ PropTypes.number, PropTypes.string, PropTypes.object ])), label: PropTypes.string, hint: PropTypes.string, charLimit: PropTypes.number, lineLimit: PropTypes.number, inputType: PropTypes.oneOf([ `default`, `numeric`, `monetary`, `phone-pad`, `email-address`, `credit-card-visa`, `credit-card-master`, `credit-card-discover`, `credit-card-american-express` ]), disableValidation: PropTypes.bool, disableFormatting: PropTypes.bool, initialAnimation: PropTypes.oneOfType([ PropTypes.string, PropTypes.shape({ refName: PropTypes.string, transitions: PropTypes.arrayOf(PropTypes.shape({ to: PropTypes.oneOfType([ PropTypes.func, PropTypes.object ]), from: PropTypes.oneOfType([ PropTypes.func, PropTypes.object ]), option: PropTypes.shape({ duration: PropTypes.number, delay: PropTypes.number, easing: PropTypes.string }) })), onTransitionBegin: PropTypes.func, onTransitionEnd: PropTypes.func, onAnimationBegin: PropTypes.func, onAnimationEnd: PropTypes.func }) ]), onValidate: PropTypes.func, onReformat: PropTypes.func, onEditing: PropTypes.func, onDoneEdit: PropTypes.func, onSelect: PropTypes.func, onFocus: PropTypes.func, onBlur: PropTypes.func, onHideSelection: PropTypes.func, onShowSelection: PropTypes.func, onClear: PropTypes.func, renderSelectableItem: PropTypes.func } static defaultProps = { room: `none`, shade: `themed`, overlay: `themed`, corner: `themed`, size: `themed`, margin: `themed`, autoFocus: false, autoCorrect: false, secured: false, underlined: `themed`, disabled: false, initialValue: ``, selectableValues: [], label: ``, hint: ``, charLimit: -1, lineLimit: 1, inputType: `default`, disableValidation: false, disableFormatting: false, initialAnimation: `themed`, onValidate: (value, inputType) => { let regex; let validated = true; let status = ``; switch(inputType) { case `email-address`: regex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/; validated = regex.test(value); status = validated ? `` : `Email is invalid`; break; case `phone-pad`: regex = /^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$/; validated = regex.test(value); status = validated ? `` : `Phone number is invalid`; break; case `credit-card-visa`: regex = /^4[0-9]{12}(?:[0-9]{3})?$/; validated = regex.test(value); status = validated ? `` : `Visa credit card number is invalid`; break; case `credit-card-master`: regex = /^(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}$/; validated = regex.test(value); status = validated ? `` : `Master credit card number is invalid`; break; case `credit-card-discover`: regex = /^6(?:011|5[0-9]{2})[0-9]{12}$/; validated = regex.test(value); status = validated ? `` : `Discover credit card number is invalid`; break; case `credit-card-american-express`: regex = /^3[47][0-9]{13}$/; validated = regex.test(value); status = validated ? `` : `American Express credit card number is invalid`; break; case `default`: break; default: break; } return { validated, status }; }, onReformat: (value, inputType) => { let reformattedValue = value; switch(inputType) { //eslint-disable-line case `monetary`: reformattedValue = `${parseFloat(value).toFixed(2)}`; break; case `numeric`: reformattedValue = `${parseFloat(value)}`; break; case `phone-pad` || `credit-card-visa` || `credit-card-master` || `credit-card-discover` || `credit-card-american-express`: reformattedValue = `${parseInt(value, 10)}`; break; default: reformattedValue = value; break; } return reformattedValue; }, onEditing: () => null, onDoneEdit: () => null, onSelect: () => null, onFocus: () => null, onBlur: () => null, onHideSelection: () => null, onShowSelection: () => null, onClear: () => null, renderSelectableItem: () => null // eslint-disable-line } static getDerivedStateFromProps (props, state) { const { shade, overlay, corner, margin, disabled, inputType, disableValidation, onValidate, style } = props; const { Theme } = state.context; if (disableValidation || !isEmptyInputValue(state.value)) { const { validated, status } = onValidate(state.value, inputType); return { adjustedStyle: readjustStyle({ shade, overlay, corner, margin, disabled, style }, state.adjustedStyle, Theme), validation: { validated, status } }; } return { adjustedStyle: readjustStyle({ shade, overlay, corner, margin, disabled, style }, state.adjustedStyle, Theme) }; } constructor (props) { super(props); const component = this; const { size } = props; const themedSize = size === `themed` ? DefaultTheme.field.text.size : size; component.refCache = {}; component.state = { context: { Theme: DefaultTheme }, visibility: { selection: false }, adjustedStyle: DEFAULT_TEXT_FIELD_STYLE, container: { width: 0, height: DefaultTheme.field.size.text.input[themedSize] * 1.65, top: 0, left: 0 }, input: { focused: false, width: 0, height: DefaultTheme.field.size.text.input[themedSize], top: 0, left: 0, value: props.initialValue, lineCount: 1 }, validation: { validated: true, status: `` } }; } isValidated = () => { const component = this; const { validation } = component.state; return validation.validated; } isSelectionVisible = () => { const component = this; const { visibility } = component.state; return visibility.selection; } isFocused = () => { const component = this; const textInput = component.refCache[`text-input`]; if (textInput !== undefined) { return textInput.isFocused(); } return false; } showSelection = (animation = { transitions: null, onTransitionBegin: () => null, onTransitionEnd: () => null, onAnimationBegin: () => null, onAnimationEnd: () => null }) => { const component = this; const { disabled, onShowSelection } = component.props; const { visibility } = component.state; if (typeof animation === `object`) { const { transitions, onTransitionBegin, onTransitionEnd, onAnimationBegin, onAnimationEnd } = animation; let animationPromises = []; if (!disabled && !visibility.selection) { animationPromises = component.animate({ refName: `animated-selection-view`, transitions: Array.isArray(transitions) ? transitions : [{ to: { translateX: 0 },