material-ui
Version:
React Components that Implement Google's Material Design.
591 lines (514 loc) • 19.4 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends2 = require('babel-runtime/helpers/extends');
var _extends3 = _interopRequireDefault(_extends2);
var _objectWithoutProperties2 = require('babel-runtime/helpers/objectWithoutProperties');
var _objectWithoutProperties3 = _interopRequireDefault(_objectWithoutProperties2);
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _simpleAssign = require('simple-assign');
var _simpleAssign2 = _interopRequireDefault(_simpleAssign);
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
var _reactDom = require('react-dom');
var _reactDom2 = _interopRequireDefault(_reactDom);
var _shallowEqual = require('recompose/shallowEqual');
var _shallowEqual2 = _interopRequireDefault(_shallowEqual);
var _transitions = require('../styles/transitions');
var _transitions2 = _interopRequireDefault(_transitions);
var _EnhancedTextarea = require('./EnhancedTextarea');
var _EnhancedTextarea2 = _interopRequireDefault(_EnhancedTextarea);
var _TextFieldHint = require('./TextFieldHint');
var _TextFieldHint2 = _interopRequireDefault(_TextFieldHint);
var _TextFieldLabel = require('./TextFieldLabel');
var _TextFieldLabel2 = _interopRequireDefault(_TextFieldLabel);
var _TextFieldUnderline = require('./TextFieldUnderline');
var _TextFieldUnderline2 = _interopRequireDefault(_TextFieldUnderline);
var _warning = require('warning');
var _warning2 = _interopRequireDefault(_warning);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var getStyles = function getStyles(props, context, state) {
var _context$muiTheme = context.muiTheme,
baseTheme = _context$muiTheme.baseTheme,
_context$muiTheme$tex = _context$muiTheme.textField,
floatingLabelColor = _context$muiTheme$tex.floatingLabelColor,
focusColor = _context$muiTheme$tex.focusColor,
textColor = _context$muiTheme$tex.textColor,
disabledTextColor = _context$muiTheme$tex.disabledTextColor,
backgroundColor = _context$muiTheme$tex.backgroundColor,
errorColor = _context$muiTheme$tex.errorColor;
var styles = {
root: {
fontSize: 16,
lineHeight: '24px',
width: props.fullWidth ? '100%' : 256,
height: (props.rows - 1) * 24 + (props.floatingLabelText ? 72 : 48),
display: 'inline-block',
position: 'relative',
backgroundColor: backgroundColor,
fontFamily: baseTheme.fontFamily,
transition: _transitions2.default.easeOut('200ms', 'height'),
cursor: props.disabled ? 'not-allowed' : 'auto'
},
error: {
position: 'relative',
bottom: 2,
fontSize: 12,
lineHeight: '12px',
color: errorColor,
transition: _transitions2.default.easeOut()
},
floatingLabel: {
color: props.disabled ? disabledTextColor : floatingLabelColor,
pointerEvents: 'none'
},
input: {
padding: 0,
position: 'relative',
width: '100%',
border: 'none',
outline: 'none',
backgroundColor: 'rgba(0,0,0,0)',
color: props.disabled ? disabledTextColor : textColor,
cursor: 'inherit',
font: 'inherit',
WebkitOpacity: 1,
WebkitTapHighlightColor: 'rgba(0,0,0,0)' // Remove mobile color flashing (deprecated style).
},
inputNative: {
appearance: 'textfield' // Improve type search style.
}
};
styles.textarea = (0, _simpleAssign2.default)({}, styles.input, {
marginTop: props.floatingLabelText ? 36 : 12,
marginBottom: props.floatingLabelText ? -36 : -12,
boxSizing: 'border-box',
font: 'inherit'
});
// Do not assign a height to the textarea as he handles it on his own.
styles.input.height = '100%';
if (state.isFocused) {
styles.floatingLabel.color = focusColor;
}
if (props.floatingLabelText) {
styles.input.boxSizing = 'border-box';
if (!props.multiLine) {
styles.input.marginTop = 14;
}
if (state.errorText) {
styles.error.bottom = !props.multiLine ? styles.error.fontSize + 3 : 3;
}
}
if (state.errorText) {
if (state.isFocused) {
styles.floatingLabel.color = styles.error.color;
}
}
return styles;
};
/**
* Check if a value is valid to be displayed inside an input.
*
* @param The value to check.
* @returns True if the string provided is valid, false otherwise.
*/
function isValid(value) {
return value !== '' && value !== undefined && value !== null && !(Array.isArray(value) && value.length === 0);
}
var TextField = function (_Component) {
(0, _inherits3.default)(TextField, _Component);
function TextField() {
var _ref;
var _temp, _this, _ret;
(0, _classCallCheck3.default)(this, TextField);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = (0, _possibleConstructorReturn3.default)(this, (_ref = TextField.__proto__ || (0, _getPrototypeOf2.default)(TextField)).call.apply(_ref, [this].concat(args))), _this), _this.state = {
isFocused: false,
errorText: undefined,
hasValue: false
}, _this.handleInputBlur = function (event) {
_this.setState({ isFocused: false });
if (_this.props.onBlur) {
_this.props.onBlur(event);
}
}, _this.handleInputChange = function (event) {
if (!_this.props.hasOwnProperty('value')) {
_this.setState({ hasValue: isValid(event.target.value) });
}
if (_this.props.onChange) {
_this.props.onChange(event, event.target.value);
}
}, _this.handleInputFocus = function (event) {
if (_this.props.disabled) {
return;
}
_this.setState({ isFocused: true });
if (_this.props.onFocus) {
_this.props.onFocus(event);
}
}, _this.handleHeightChange = function (event, height) {
var newHeight = height + 24;
if (_this.props.floatingLabelText) {
newHeight += 24;
}
_reactDom2.default.findDOMNode(_this).style.height = newHeight + 'px';
}, _temp), (0, _possibleConstructorReturn3.default)(_this, _ret);
}
(0, _createClass3.default)(TextField, [{
key: 'componentWillMount',
value: function componentWillMount() {
var _props = this.props,
children = _props.children,
name = _props.name,
hintText = _props.hintText,
floatingLabelText = _props.floatingLabelText,
id = _props.id;
var propsLeaf = children ? children.props : this.props;
this.setState({
errorText: this.props.errorText,
hasValue: isValid(propsLeaf.value) || isValid(propsLeaf.defaultValue)
});
process.env.NODE_ENV !== "production" ? (0, _warning2.default)(name || hintText || floatingLabelText || id, 'Material-UI: We don\'t have enough information\n to build a robust unique id for the TextField component. Please provide an id or a name.') : void 0;
var uniqueId = name + '-' + hintText + '-' + floatingLabelText + '-' + Math.floor(Math.random() * 0xFFFF);
this.uniqueId = uniqueId.replace(/[^A-Za-z0-9-]/gi, '');
}
}, {
key: 'componentWillReceiveProps',
value: function componentWillReceiveProps(nextProps) {
if (nextProps.disabled && !this.props.disabled) {
this.setState({
isFocused: false
});
}
if (nextProps.errorText !== this.props.errorText) {
this.setState({
errorText: nextProps.errorText
});
}
if (nextProps.children && nextProps.children.props) {
nextProps = nextProps.children.props;
}
if (nextProps.hasOwnProperty('value')) {
var hasValue = isValid(nextProps.value);
this.setState({
hasValue: hasValue
});
}
}
}, {
key: 'shouldComponentUpdate',
value: function shouldComponentUpdate(nextProps, nextState, nextContext) {
return !(0, _shallowEqual2.default)(this.props, nextProps) || !(0, _shallowEqual2.default)(this.state, nextState) || !(0, _shallowEqual2.default)(this.context, nextContext);
}
}, {
key: 'blur',
value: function blur() {
if (this.input) {
this.getInputNode().blur();
}
}
}, {
key: 'focus',
value: function focus() {
if (this.input) {
this.getInputNode().focus();
}
}
}, {
key: 'select',
value: function select() {
if (this.input) {
this.getInputNode().select();
}
}
}, {
key: 'getValue',
value: function getValue() {
return this.input ? this.getInputNode().value : undefined;
}
}, {
key: 'getInputNode',
value: function getInputNode() {
return this.props.children || this.props.multiLine ? this.input.getInputNode() : _reactDom2.default.findDOMNode(this.input);
}
}, {
key: '_isControlled',
value: function _isControlled() {
return this.props.hasOwnProperty('value');
}
}, {
key: 'render',
value: function render() {
var _this2 = this;
var _props2 = this.props,
children = _props2.children,
className = _props2.className,
disabled = _props2.disabled,
errorStyle = _props2.errorStyle,
errorText = _props2.errorText,
floatingLabelFixed = _props2.floatingLabelFixed,
floatingLabelFocusStyle = _props2.floatingLabelFocusStyle,
floatingLabelShrinkStyle = _props2.floatingLabelShrinkStyle,
floatingLabelStyle = _props2.floatingLabelStyle,
floatingLabelText = _props2.floatingLabelText,
fullWidth = _props2.fullWidth,
hintText = _props2.hintText,
hintStyle = _props2.hintStyle,
id = _props2.id,
inputStyle = _props2.inputStyle,
multiLine = _props2.multiLine,
onBlur = _props2.onBlur,
onChange = _props2.onChange,
onFocus = _props2.onFocus,
style = _props2.style,
type = _props2.type,
underlineDisabledStyle = _props2.underlineDisabledStyle,
underlineFocusStyle = _props2.underlineFocusStyle,
underlineShow = _props2.underlineShow,
underlineStyle = _props2.underlineStyle,
rows = _props2.rows,
rowsMax = _props2.rowsMax,
textareaStyle = _props2.textareaStyle,
other = (0, _objectWithoutProperties3.default)(_props2, ['children', 'className', 'disabled', 'errorStyle', 'errorText', 'floatingLabelFixed', 'floatingLabelFocusStyle', 'floatingLabelShrinkStyle', 'floatingLabelStyle', 'floatingLabelText', 'fullWidth', 'hintText', 'hintStyle', 'id', 'inputStyle', 'multiLine', 'onBlur', 'onChange', 'onFocus', 'style', 'type', 'underlineDisabledStyle', 'underlineFocusStyle', 'underlineShow', 'underlineStyle', 'rows', 'rowsMax', 'textareaStyle']);
var prepareStyles = this.context.muiTheme.prepareStyles;
var styles = getStyles(this.props, this.context, this.state);
var inputId = id || this.uniqueId;
var errorTextElement = this.state.errorText && _react2.default.createElement(
'div',
{ style: prepareStyles((0, _simpleAssign2.default)(styles.error, errorStyle)) },
this.state.errorText
);
var floatingLabelTextElement = floatingLabelText && _react2.default.createElement(
_TextFieldLabel2.default,
{
muiTheme: this.context.muiTheme,
style: (0, _simpleAssign2.default)(styles.floatingLabel, floatingLabelStyle, this.state.isFocused ? floatingLabelFocusStyle : null),
shrinkStyle: floatingLabelShrinkStyle,
htmlFor: inputId,
shrink: this.state.hasValue || this.state.isFocused || floatingLabelFixed,
disabled: disabled
},
floatingLabelText
);
var inputProps = {
id: inputId,
ref: function ref(elem) {
return _this2.input = elem;
},
disabled: this.props.disabled,
onBlur: this.handleInputBlur,
onChange: this.handleInputChange,
onFocus: this.handleInputFocus
};
var childStyleMerged = (0, _simpleAssign2.default)(styles.input, inputStyle);
var inputElement = void 0;
if (children) {
inputElement = _react2.default.cloneElement(children, (0, _extends3.default)({}, inputProps, children.props, {
style: (0, _simpleAssign2.default)(childStyleMerged, children.props.style)
}));
} else {
inputElement = multiLine ? _react2.default.createElement(_EnhancedTextarea2.default, (0, _extends3.default)({
style: childStyleMerged,
textareaStyle: (0, _simpleAssign2.default)(styles.textarea, styles.inputNative, textareaStyle),
rows: rows,
rowsMax: rowsMax,
hintText: hintText
}, other, inputProps, {
onHeightChange: this.handleHeightChange
})) : _react2.default.createElement('input', (0, _extends3.default)({
type: type,
style: prepareStyles((0, _simpleAssign2.default)(styles.inputNative, childStyleMerged))
}, other, inputProps));
}
var rootProps = {};
if (children) {
rootProps = other;
}
return _react2.default.createElement(
'div',
(0, _extends3.default)({}, rootProps, {
className: className,
style: prepareStyles((0, _simpleAssign2.default)(styles.root, style))
}),
floatingLabelTextElement,
hintText ? _react2.default.createElement(_TextFieldHint2.default, {
muiTheme: this.context.muiTheme,
show: !(this.state.hasValue || floatingLabelText && !this.state.isFocused) || !this.state.hasValue && floatingLabelText && floatingLabelFixed && !this.state.isFocused,
style: hintStyle,
text: hintText
}) : null,
inputElement,
underlineShow ? _react2.default.createElement(_TextFieldUnderline2.default, {
disabled: disabled,
disabledStyle: underlineDisabledStyle,
error: !!this.state.errorText,
errorStyle: errorStyle,
focus: this.state.isFocused,
focusStyle: underlineFocusStyle,
muiTheme: this.context.muiTheme,
style: underlineStyle
}) : null,
errorTextElement
);
}
}]);
return TextField;
}(_react.Component);
TextField.defaultProps = {
disabled: false,
floatingLabelFixed: false,
multiLine: false,
fullWidth: false,
type: 'text',
underlineShow: true,
rows: 1
};
TextField.contextTypes = {
muiTheme: _propTypes2.default.object.isRequired
};
TextField.propTypes = process.env.NODE_ENV !== "production" ? {
children: _propTypes2.default.node,
/**
* The css class name of the root element.
*/
className: _propTypes2.default.string,
/**
* The text string to use for the default value.
*/
defaultValue: _propTypes2.default.any,
/**
* Disables the text field if set to true.
*/
disabled: _propTypes2.default.bool,
/**
* The style object to use to override error styles.
*/
errorStyle: _propTypes2.default.object,
/**
* The error content to display.
*/
errorText: _propTypes2.default.node,
/**
* If true, the floating label will float even when there is no value.
*/
floatingLabelFixed: _propTypes2.default.bool,
/**
* The style object to use to override floating label styles when focused.
*/
floatingLabelFocusStyle: _propTypes2.default.object,
/**
* The style object to use to override floating label styles when shrunk.
*/
floatingLabelShrinkStyle: _propTypes2.default.object,
/**
* The style object to use to override floating label styles.
*/
floatingLabelStyle: _propTypes2.default.object,
/**
* The content to use for the floating label element.
*/
floatingLabelText: _propTypes2.default.node,
/**
* If true, the field receives the property width 100%.
*/
fullWidth: _propTypes2.default.bool,
/**
* Override the inline-styles of the TextField's hint text element.
*/
hintStyle: _propTypes2.default.object,
/**
* The hint content to display.
*/
hintText: _propTypes2.default.node,
/**
* The id prop for the text field.
*/
id: _propTypes2.default.string,
/**
* Override the inline-styles of the TextField's input element.
* When multiLine is false: define the style of the input element.
* When multiLine is true: define the style of the container of the textarea.
*/
inputStyle: _propTypes2.default.object,
/**
* If true, a textarea element will be rendered.
* The textarea also grows and shrinks according to the number of lines.
*/
multiLine: _propTypes2.default.bool,
/**
* Name applied to the input.
*/
name: _propTypes2.default.string,
/** @ignore */
onBlur: _propTypes2.default.func,
/**
* Callback function that is fired when the textfield's value changes.
*
* @param {object} event Change event targeting the text field.
* @param {string} newValue The new value of the text field.
*/
onChange: _propTypes2.default.func,
/** @ignore */
onFocus: _propTypes2.default.func,
/**
* Number of rows to display when multiLine option is set to true.
*/
rows: _propTypes2.default.number,
/**
* Maximum number of rows to display when
* multiLine option is set to true.
*/
rowsMax: _propTypes2.default.number,
/**
* Override the inline-styles of the root element.
*/
style: _propTypes2.default.object,
/**
* Override the inline-styles of the TextField's textarea element.
* The TextField use either a textarea or an input,
* this property has effects only when multiLine is true.
*/
textareaStyle: _propTypes2.default.object,
/**
* Specifies the type of input to display
* such as "password" or "text".
*/
type: _propTypes2.default.string,
/**
* Override the inline-styles of the
* TextField's underline element when disabled.
*/
underlineDisabledStyle: _propTypes2.default.object,
/**
* Override the inline-styles of the TextField's
* underline element when focussed.
*/
underlineFocusStyle: _propTypes2.default.object,
/**
* If true, shows the underline for the text field.
*/
underlineShow: _propTypes2.default.bool,
/**
* Override the inline-styles of the TextField's underline element.
*/
underlineStyle: _propTypes2.default.object,
/**
* The value of the text field.
*/
value: _propTypes2.default.any
} : {};
exports.default = TextField;