@ysmood/material-ui
Version:
Material Design UI components built with React
353 lines (299 loc) • 10.1 kB
JSX
var React = require('react');
var ColorManipulator = require('./utils/color-manipulator');
var Colors = require('./styles/colors');
var StylePropable = require('./mixins/style-propable');
var Transitions = require('./styles/transitions');
var UniqueId = require('./utils/unique-id');
var EnhancedTextarea = require('./enhanced-textarea');
var TextField = React.createClass({
mixins: [StylePropable],
contextTypes: {
muiTheme: React.PropTypes.object
},
propTypes: {
errorText: React.PropTypes.string,
floatingLabelText: React.PropTypes.string,
hintText: React.PropTypes.string,
id: React.PropTypes.string,
multiLine: React.PropTypes.bool,
onBlur: React.PropTypes.func,
onChange: React.PropTypes.func,
onFocus: React.PropTypes.func,
onKeyDown: React.PropTypes.func,
onEnterKeyDown: React.PropTypes.func,
type: React.PropTypes.string,
rows: React.PropTypes.number
},
getDefaultProps: function() {
return {
type: 'text',
rows: 1
};
},
getInitialState: function() {
return {
errorText: this.props.errorText,
hasValue: this.props.value || this.props.defaultValue ||
(this.props.valueLink && this.props.valueLink.value)
};
},
componentWillReceiveProps: function(nextProps) {
var hasErrorProp = nextProps.hasOwnProperty('errorText');
var hasValueLinkProp = nextProps.hasOwnProperty('valueLink');
var hasValueProp = nextProps.hasOwnProperty('value');
var hasNewDefaultValue = nextProps.defaultValue !== this.props.defaultValue;
var newState = {};
if (hasValueProp) {
newState.hasValue = nextProps.value;
} else if (hasValueLinkProp) {
newState.hasValue = nextProps.valueLink.value;
} else if (hasNewDefaultValue) {
newState.hasValue = nextProps.defaultValue;
}
if (hasErrorProp) newState.errorText = nextProps.errorText;
if (newState) this.setState(newState);
},
errorColor: Colors.red500,
_getDisabledTextColor: function() {
return this.getTheme().disabledColor;
},
getTheme: function() {
return this.context.muiTheme.palette;
},
getStyles: function() {
var styles = {
root: {
fontSize: '16px',
lineHeight: '24px',
width: (64 * 4),
height: (this.props.rows - 1) * 24 + (this.props.floatingLabelText ? 72 : 48),
display: 'inline-block',
position: 'relative',
fontFamily: this.context.muiTheme.contentFontFamily,
transition: Transitions.easeOut('200ms', 'height')
},
error: {
position: 'absolute',
bottom: -10,
fontSize: '12px',
lineHeight: '12px',
color: this.errorColor,
transition: Transitions.easeOut(),
},
hint: {
position: 'absolute',
lineHeight: '48px',
opacity: 1,
color: this._getDisabledTextColor(),
transition: Transitions.easeOut()
},
input: {
WebkitTapHighlightColor: 'rgba(0,0,0,0)',
position: 'relative',
width: '100%',
height: '100%',
border: 'none',
outline: 'none',
backgroundColor: 'transparent',
color: this.getTheme().textColor,
font: 'inherit'
},
underline: {
border: 'none',
borderBottom: 'solid 1px ' + this.getTheme().borderColor,
position: 'absolute',
width: '100%',
bottom: 8,
margin: 0,
MozBoxSizing: 'content-box',
boxSizing: 'content-box',
height: 0
},
underlineAfter: {
position: 'absolute',
userSelect: 'none',
cursor: 'default',
bottom: 0,
color: this._getDisabledTextColor()
}
};
styles.floatingLabel = this.mergeStyles(styles.hint, {
top: 24,
opacity: 1,
transform: 'scale(1) translate3d(0, 0, 0)',
transformOrigin: 'left top'
});
styles.textarea = this.mergeStyles(styles.input, {
paddingTop: this.props.floatingLabelText ? 36 : 12,
boxSizing: 'border-box',
font: 'inherit'
});
styles.focusUnderline= this.mergeStyles(styles.underline, {
borderBottom: 'solid 2px ' + this.getTheme().primary1Color,
transform: 'scaleX(0)',
transition: Transitions.easeOut(),
});
if (this.props.disabled) {
styles.hint.color = this._getDisabledTextColor();
styles.input.color = this._getDisabledTextColor();
}
if (this.state.isFocused) {
styles.floatingLabel.color = this.getTheme().primary1Color;
styles.floatingLabel.transform = 'perspective(1px) scale(0.75) translate3d(0, -18px, 0)';
styles.focusUnderline.transform = 'scaleX(1)';
}
if (this.state.hasValue) {
styles.floatingLabel.color = ColorManipulator.fade(this.getTheme().textColor, 0.5);
styles.floatingLabel.transform = 'perspective(1px) scale(0.75) translate3d(0, -18px, 0)';
styles.hint.opacity = 0;
}
if (this.props.floatingLabelText) {
styles.hint.top = 24;
styles.hint.opacity = 0;
styles.input.boxSizing = 'border-box';
if (this.state.isFocused && !this.state.hasValue) styles.hint.opacity = 1;
}
if (this.props.errorText && this.state.isFocused) styles.floatingLabel.color = this.errorColor;
if (this.props.floatingLabelText && !this.props.multiLine) styles.input.paddingTop = 26;
if (this.props.errorText) {
styles.focusUnderline.borderColor = this.errorColor;
styles.focusUnderline.transform = 'scaleX(1)';
}
return styles;
},
render: function() {
var {
className,
errorText,
floatingLabelText,
hintText,
id,
multiLine,
onBlur,
onChange,
onFocus,
type,
rows,
...other
} = this.props;
var styles = this.getStyles();
var inputId = this.props.id || UniqueId.generate();
var errorTextElement = this.state.errorText ? (
<div style={this.mergeAndPrefix(styles.error)}>{this.state.errorText}</div>
) : null;
var hintTextElement = this.props.hintText ? (
<div style={this.mergeAndPrefix(styles.hint)}>{this.props.hintText}</div>
) : null;
var floatingLabelTextElement = this.props.floatingLabelText ? (
<label
style={this.mergeAndPrefix(styles.floatingLabel)}
htmlFor={inputId}>
{this.props.floatingLabelText}
</label>
) : null;
var inputProps;
var inputElement;
inputProps = {
id: inputId,
ref: this._getRef(),
style: this.mergeAndPrefix(styles.input),
onBlur: this._handleInputBlur,
onFocus: this._handleInputFocus,
onKeyDown: this._handleInputKeyDown
};
if (!this.props.hasOwnProperty('valueLink')) {
inputProps.onChange = this._handleInputChange;
}
inputElement = this.props.multiLine ? (
<EnhancedTextarea
{...other}
{...inputProps}
rows={this.props.rows}
onHeightChange={this._handleTextAreaHeightChange}
textareaStyle={this.mergeAndPrefix(styles.textarea)} />
) : (
<input
{...other}
{...inputProps}
type={this.props.type} />
);
var underlineElement = this.props.disabled ? (
<div style={this.mergeAndPrefix(styles.underlineAfter)}>
.............................................................
</div>
) : (
<hr style={this.mergeAndPrefix(styles.underline)}/>
);
var focusUnderlineElement = <hr style={this.mergeAndPrefix(styles.focusUnderline)} />;
return (
<div className={this.props.className} style={this.mergeAndPrefix(styles.root, this.props.style)}>
{floatingLabelTextElement}
{hintTextElement}
{inputElement}
{underlineElement}
{focusUnderlineElement}
{errorTextElement}
</div>
);
},
blur: function() {
if (this.isMounted()) this._getInputNode().blur();
},
clearValue: function() {
this.setValue('');
},
focus: function() {
if (this.isMounted()) this._getInputNode().focus();
},
getValue: function() {
return this.isMounted() ? this._getInputNode().value : undefined;
},
setErrorText: function(newErrorText) {
if (process.env.NODE_ENV !== 'production' && this.props.hasOwnProperty('errorText')) {
console.error('Cannot call TextField.setErrorText when errorText is defined as a property.');
} else if (this.isMounted()) {
this.setState({errorText: newErrorText});
}
},
setValue: function(newValue) {
if (process.env.NODE_ENV !== 'production' && this._isControlled()) {
console.error('Cannot call TextField.setValue when value or valueLink is defined as a property.');
} else if (this.isMounted()) {
this._getInputNode().value = newValue;
this.setState({hasValue: newValue});
}
},
_getRef: function() {
return this.props.ref ? this.props.ref : 'input';
},
_getInputNode: function() {
return this.props.multiLine ?
this.refs[this._getRef()].getInputNode() : React.findDOMNode(this.refs[this._getRef()]);
},
_handleInputBlur: function(e) {
this.setState({isFocused: false});
if (this.props.onBlur) this.props.onBlur(e);
},
_handleInputChange: function(e) {
this.setState({hasValue: e.target.value});
if (this.props.onChange) this.props.onChange(e);
},
_handleInputFocus: function(e) {
this.setState({isFocused: true});
if (this.props.onFocus) this.props.onFocus(e);
},
_handleInputKeyDown: function(e) {
if (e.keyCode === 13 && this.props.onEnterKeyDown) this.props.onEnterKeyDown(e);
if (this.props.onKeyDown) this.props.onKeyDown(e);
},
_handleTextAreaHeightChange: function(e, height) {
var newHeight = height + 24;
if (this.props.floatingLabelText) newHeight += 24;
React.findDOMNode(this).style.height = newHeight + 'px';
},
_isControlled: function() {
return this.props.hasOwnProperty('value') ||
this.props.hasOwnProperty('valueLink');
}
});
module.exports = TextField;