hypertoxin
Version:
A themeable ReactNative component library for developing native apps
1,280 lines (1,235 loc) • 93.8 kB
JavaScript
/**
* 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
},