@material-ui/core
Version:
React components that implement Google's Material Design.
720 lines (605 loc) • 22.8 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.formControlState = formControlState;
exports.default = exports.styles = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _react = _interopRequireDefault(require("react"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _classnames = _interopRequireDefault(require("classnames"));
var _withStyles = _interopRequireDefault(require("../styles/withStyles"));
var _reactHelpers = require("../utils/reactHelpers");
var _Textarea = _interopRequireDefault(require("./Textarea"));
var _utils = require("./utils");
/* eslint-disable jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */
var styles = function styles(theme) {
var light = theme.palette.type === 'light';
var placeholder = {
color: 'currentColor',
opacity: light ? 0.42 : 0.5,
transition: theme.transitions.create('opacity', {
duration: theme.transitions.duration.shorter
})
};
var placeholderHidden = {
opacity: 0
};
var placeholderVisible = {
opacity: light ? 0.42 : 0.5
};
return {
/* Styles applied to the root element. */
root: {
// Mimics the default input display property used by browsers for an input.
fontFamily: theme.typography.fontFamily,
color: theme.palette.text.primary,
fontSize: theme.typography.pxToRem(16),
lineHeight: '1.1875em',
// Reset (19px), match the native input line-height
cursor: 'text',
display: 'inline-flex',
alignItems: 'center',
'&$disabled': {
color: theme.palette.text.disabled,
cursor: 'default'
}
},
/* Styles applied to the root element if the component is a descendant of `FormControl`. */
formControl: {},
/* Styles applied to the root element if the component is focused. */
focused: {},
/* Styles applied to the root element if `disabled={true}`. */
disabled: {},
/* Styles applied to the root element if `startAdornment` is provided. */
adornedStart: {},
/* Styles applied to the root element if `endAdornment` is provided. */
adornedEnd: {},
/* Styles applied to the root element if `error={true}`. */
error: {},
/* Styles applied to the `input` element if `margin="dense"`. */
marginDense: {},
/* Styles applied to the root element if `multiline={true}`. */
multiline: {
padding: "".concat(8 - 2, "px 0 ").concat(8 - 1, "px")
},
/* Styles applied to the root element if `fullWidth={true}`. */
fullWidth: {
width: '100%'
},
/* Styles applied to the `input` element. */
input: {
font: 'inherit',
color: 'currentColor',
padding: "".concat(8 - 2, "px 0 ").concat(8 - 1, "px"),
border: 0,
boxSizing: 'content-box',
background: 'none',
margin: 0,
// Reset for Safari
// Remove grey highlight
WebkitTapHighlightColor: 'transparent',
display: 'block',
// Make the flex item shrink with Firefox
minWidth: 0,
width: '100%',
// Fix IE11 width issue
'&::-webkit-input-placeholder': placeholder,
'&::-moz-placeholder': placeholder,
// Firefox 19+
'&:-ms-input-placeholder': placeholder,
// IE 11
'&::-ms-input-placeholder': placeholder,
// Edge
'&:focus': {
outline: 0
},
// Reset Firefox invalid required input style
'&:invalid': {
boxShadow: 'none'
},
'&::-webkit-search-decoration': {
// Remove the padding when type=search.
'-webkit-appearance': 'none'
},
// Show and hide the placeholder logic
'label[data-shrink=false] + $formControl &': {
'&::-webkit-input-placeholder': placeholderHidden,
'&::-moz-placeholder': placeholderHidden,
// Firefox 19+
'&:-ms-input-placeholder': placeholderHidden,
// IE 11
'&::-ms-input-placeholder': placeholderHidden,
// Edge
'&:focus::-webkit-input-placeholder': placeholderVisible,
'&:focus::-moz-placeholder': placeholderVisible,
// Firefox 19+
'&:focus:-ms-input-placeholder': placeholderVisible,
// IE 11
'&:focus::-ms-input-placeholder': placeholderVisible // Edge
},
'&$disabled': {
opacity: 1 // Reset iOS opacity
}
},
/* Styles applied to the `input` element if `margin="dense"`. */
inputMarginDense: {
paddingTop: 4 - 1
},
/* Styles applied to the `input` element if `multiline={true}`. */
inputMultiline: {
resize: 'none',
padding: 0
},
/* Styles applied to the `input` element if `type` is not "text"`. */
inputType: {
// type="date" or type="time", etc. have specific styles we need to reset.
height: '1.1875em' // Reset (19px), match the native input line-height
},
/* Styles applied to the `input` element if `type="search"`. */
inputTypeSearch: {
// Improve type search style.
'-moz-appearance': 'textfield',
'-webkit-appearance': 'textfield'
},
/* Styles applied to the `input` element if `startAdornment` is provided. */
inputAdornedStart: {},
/* Styles applied to the `input` element if `endAdornment` is provided. */
inputAdornedEnd: {}
};
};
exports.styles = styles;
function formControlState(_ref) {
var props = _ref.props,
states = _ref.states,
context = _ref.context;
return states.reduce(function (acc, state) {
acc[state] = props[state];
if (context && context.muiFormControl) {
if (typeof props[state] === 'undefined') {
acc[state] = context.muiFormControl[state];
}
}
return acc;
}, {});
}
/**
* `InputBase` contains as few styles as possible.
* It aims to be a simple building block for creating an input.
* It contains a load of style reset and some state logic.
*/
var InputBase =
/*#__PURE__*/
function (_React$Component) {
(0, _inherits2.default)(InputBase, _React$Component);
function InputBase(props, context) {
var _this;
(0, _classCallCheck2.default)(this, InputBase);
_this = (0, _possibleConstructorReturn2.default)(this, (0, _getPrototypeOf2.default)(InputBase).call(this, props, context));
_this.state = {
focused: false
};
_this.handleFocus = function (event) {
// Fix a bug with IE11 where the focus/blur events are triggered
// while the input is disabled.
if (formControlState({
props: _this.props,
context: _this.context,
states: ['disabled']
}).disabled) {
event.stopPropagation();
return;
}
_this.setState({
focused: true
});
if (_this.props.onFocus) {
_this.props.onFocus(event);
}
var muiFormControl = _this.context.muiFormControl;
if (muiFormControl && muiFormControl.onFocus) {
muiFormControl.onFocus(event);
}
};
_this.handleBlur = function (event) {
_this.setState({
focused: false
});
if (_this.props.onBlur) {
_this.props.onBlur(event);
}
var muiFormControl = _this.context.muiFormControl;
if (muiFormControl && muiFormControl.onBlur) {
muiFormControl.onBlur(event);
}
};
_this.handleChange = function () {
if (!_this.isControlled) {
_this.checkDirty(_this.inputRef);
} // Perform in the willUpdate
if (_this.props.onChange) {
var _this$props;
(_this$props = _this.props).onChange.apply(_this$props, arguments);
}
};
_this.handleRefInput = function (ref) {
_this.inputRef = ref;
var refProp;
if (_this.props.inputRef) {
refProp = _this.props.inputRef;
} else if (_this.props.inputProps && _this.props.inputProps.ref) {
refProp = _this.props.inputProps.ref;
}
(0, _reactHelpers.setRef)(refProp, ref);
};
_this.handleClick = function (event) {
if (_this.inputRef && event.currentTarget === event.target) {
_this.inputRef.focus();
}
if (_this.props.onClick) {
_this.props.onClick(event);
}
};
_this.isControlled = props.value != null;
if (_this.isControlled) {
_this.checkDirty(props);
}
var componentWillReceiveProps = function componentWillReceiveProps(nextProps, nextContext) {
// The blur won't fire when the disabled state is set on a focused input.
// We need to book keep the focused state manually.
if (!formControlState({
props: _this.props,
context: _this.context,
states: ['disabled']
}).disabled && formControlState({
props: nextProps,
context: nextContext,
states: ['disabled']
}).disabled) {
_this.setState({
focused: false
});
}
};
var componentWillUpdate = function componentWillUpdate(nextProps, nextState, nextContext) {
// Book keep the focused state.
if (!formControlState({
props: _this.props,
context: _this.context,
states: ['disabled']
}).disabled && formControlState({
props: nextProps,
context: nextContext,
states: ['disabled']
}).disabled) {
var muiFormControl = _this.context.muiFormControl;
if (muiFormControl && muiFormControl.onBlur) {
muiFormControl.onBlur();
}
}
};
/* eslint-disable no-underscore-dangle */
_this.componentWillReceiveProps = componentWillReceiveProps;
_this.componentWillReceiveProps.__suppressDeprecationWarning = true;
_this.componentWillUpdate = componentWillUpdate;
_this.componentWillUpdate.__suppressDeprecationWarning = true;
/* eslint-enable no-underscore-dangle */
return _this;
}
(0, _createClass2.default)(InputBase, [{
key: "getChildContext",
value: function getChildContext() {
// We are consuming the parent muiFormControl context.
// We don't want a child to consume it a second time.
return {
muiFormControl: null
};
}
}, {
key: "componentDidMount",
value: function componentDidMount() {
if (!this.isControlled) {
this.checkDirty(this.inputRef);
}
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate() {
if (this.isControlled) {
this.checkDirty(this.props);
} // else performed in the onChange
}
}, {
key: "checkDirty",
value: function checkDirty(obj) {
var muiFormControl = this.context.muiFormControl;
if ((0, _utils.isFilled)(obj)) {
if (muiFormControl && muiFormControl.onFilled) {
muiFormControl.onFilled();
}
if (this.props.onFilled) {
this.props.onFilled();
}
return;
}
if (muiFormControl && muiFormControl.onEmpty) {
muiFormControl.onEmpty();
}
if (this.props.onEmpty) {
this.props.onEmpty();
}
}
}, {
key: "render",
value: function render() {
var _classNames, _classNames2;
var _this$props2 = this.props,
autoComplete = _this$props2.autoComplete,
autoFocus = _this$props2.autoFocus,
classes = _this$props2.classes,
classNameProp = _this$props2.className,
defaultValue = _this$props2.defaultValue,
disabled = _this$props2.disabled,
endAdornment = _this$props2.endAdornment,
error = _this$props2.error,
fullWidth = _this$props2.fullWidth,
id = _this$props2.id,
inputComponent = _this$props2.inputComponent,
_this$props2$inputPro = _this$props2.inputProps;
_this$props2$inputPro = _this$props2$inputPro === void 0 ? {} : _this$props2$inputPro;
var inputPropsClassName = _this$props2$inputPro.className,
inputPropsProp = (0, _objectWithoutProperties2.default)(_this$props2$inputPro, ["className"]),
inputRef = _this$props2.inputRef,
margin = _this$props2.margin,
multiline = _this$props2.multiline,
name = _this$props2.name,
onBlur = _this$props2.onBlur,
onChange = _this$props2.onChange,
onClick = _this$props2.onClick,
onEmpty = _this$props2.onEmpty,
onFilled = _this$props2.onFilled,
onFocus = _this$props2.onFocus,
onKeyDown = _this$props2.onKeyDown,
onKeyUp = _this$props2.onKeyUp,
placeholder = _this$props2.placeholder,
readOnly = _this$props2.readOnly,
renderPrefix = _this$props2.renderPrefix,
rows = _this$props2.rows,
rowsMax = _this$props2.rowsMax,
startAdornment = _this$props2.startAdornment,
type = _this$props2.type,
value = _this$props2.value,
other = (0, _objectWithoutProperties2.default)(_this$props2, ["autoComplete", "autoFocus", "classes", "className", "defaultValue", "disabled", "endAdornment", "error", "fullWidth", "id", "inputComponent", "inputProps", "inputRef", "margin", "multiline", "name", "onBlur", "onChange", "onClick", "onEmpty", "onFilled", "onFocus", "onKeyDown", "onKeyUp", "placeholder", "readOnly", "renderPrefix", "rows", "rowsMax", "startAdornment", "type", "value"]);
var muiFormControl = this.context.muiFormControl;
var fcs = formControlState({
props: this.props,
context: this.context,
states: ['disabled', 'error', 'margin', 'required', 'filled']
});
var className = (0, _classnames.default)(classes.root, (_classNames = {}, (0, _defineProperty2.default)(_classNames, classes.disabled, fcs.disabled), (0, _defineProperty2.default)(_classNames, classes.error, fcs.error), (0, _defineProperty2.default)(_classNames, classes.fullWidth, fullWidth), (0, _defineProperty2.default)(_classNames, classes.focused, this.state.focused), (0, _defineProperty2.default)(_classNames, classes.formControl, muiFormControl), (0, _defineProperty2.default)(_classNames, classes.marginDense, fcs.margin === 'dense'), (0, _defineProperty2.default)(_classNames, classes.multiline, multiline), (0, _defineProperty2.default)(_classNames, classes.adornedStart, startAdornment), (0, _defineProperty2.default)(_classNames, classes.adornedEnd, endAdornment), _classNames), classNameProp);
var inputClassName = (0, _classnames.default)(classes.input, (_classNames2 = {}, (0, _defineProperty2.default)(_classNames2, classes.disabled, fcs.disabled), (0, _defineProperty2.default)(_classNames2, classes.inputType, type !== 'text'), (0, _defineProperty2.default)(_classNames2, classes.inputTypeSearch, type === 'search'), (0, _defineProperty2.default)(_classNames2, classes.inputMultiline, multiline), (0, _defineProperty2.default)(_classNames2, classes.inputMarginDense, fcs.margin === 'dense'), (0, _defineProperty2.default)(_classNames2, classes.inputAdornedStart, startAdornment), (0, _defineProperty2.default)(_classNames2, classes.inputAdornedEnd, endAdornment), _classNames2), inputPropsClassName);
var InputComponent = inputComponent;
var inputProps = (0, _extends2.default)({}, inputPropsProp, {
ref: this.handleRefInput
});
if (typeof InputComponent !== 'string') {
inputProps = (0, _extends2.default)({
// Rename ref to inputRef as we don't know the
// provided `inputComponent` structure.
inputRef: this.handleRefInput,
type: type
}, inputProps, {
ref: null
});
} else if (multiline) {
if (rows && !rowsMax) {
InputComponent = 'textarea';
} else {
inputProps = (0, _extends2.default)({
rowsMax: rowsMax,
textareaRef: this.handleRefInput
}, inputProps, {
ref: null
});
InputComponent = _Textarea.default;
}
} else {
inputProps = (0, _extends2.default)({
type: type
}, inputProps);
}
return _react.default.createElement("div", (0, _extends2.default)({
className: className,
onClick: this.handleClick
}, other), renderPrefix ? renderPrefix((0, _extends2.default)({}, fcs, {
startAdornment: startAdornment,
focused: this.state.focused
})) : null, startAdornment, _react.default.createElement(InputComponent, (0, _extends2.default)({
"aria-invalid": fcs.error,
autoComplete: autoComplete,
autoFocus: autoFocus,
className: inputClassName,
defaultValue: defaultValue,
disabled: fcs.disabled,
id: id,
name: name,
onBlur: this.handleBlur,
onChange: this.handleChange,
onFocus: this.handleFocus,
onKeyDown: onKeyDown,
onKeyUp: onKeyUp,
placeholder: placeholder,
readOnly: readOnly,
required: fcs.required,
rows: rows,
value: value
}, inputProps)), endAdornment);
}
}]);
return InputBase;
}(_react.default.Component);
InputBase.propTypes = process.env.NODE_ENV !== "production" ? {
/**
* This property helps users to fill forms faster, especially on mobile devices.
* The name can be confusing, as it's more like an autofill.
* You can learn more about it here:
* https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill
*/
autoComplete: _propTypes.default.string,
/**
* If `true`, the input will be focused during the first mount.
*/
autoFocus: _propTypes.default.bool,
/**
* Override or extend the styles applied to the component.
* See [CSS API](#css-api) below for more details.
*/
classes: _propTypes.default.object.isRequired,
/**
* The CSS class name of the wrapper element.
*/
className: _propTypes.default.string,
/**
* The default input value, useful when not controlling the component.
*/
defaultValue: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number]),
/**
* If `true`, the input will be disabled.
*/
disabled: _propTypes.default.bool,
/**
* End `InputAdornment` for this component.
*/
endAdornment: _propTypes.default.node,
/**
* If `true`, the input will indicate an error. This is normally obtained via context from
* FormControl.
*/
error: _propTypes.default.bool,
/**
* If `true`, the input will take up the full width of its container.
*/
fullWidth: _propTypes.default.bool,
/**
* The id of the `input` element.
*/
id: _propTypes.default.string,
/**
* The component used for the native input.
* Either a string to use a DOM element or a component.
*/
inputComponent: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.func, _propTypes.default.object]),
/**
* Attributes applied to the `input` element.
*/
inputProps: _propTypes.default.object,
/**
* Use that property to pass a ref callback to the native input component.
*/
inputRef: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.object]),
/**
* If `dense`, will adjust vertical spacing. This is normally obtained via context from
* FormControl.
*/
margin: _propTypes.default.oneOf(['dense', 'none']),
/**
* If `true`, a textarea element will be rendered.
*/
multiline: _propTypes.default.bool,
/**
* Name attribute of the `input` element.
*/
name: _propTypes.default.string,
/**
* @ignore
*/
onBlur: _propTypes.default.func,
/**
* Callback fired when the value is changed.
*
* @param {object} event The event source of the callback.
* You can pull out the new value by accessing `event.target.value`.
*/
onChange: _propTypes.default.func,
/**
* @ignore
*/
onEmpty: _propTypes.default.func,
/**
* @ignore
*/
onFilled: _propTypes.default.func,
/**
* @ignore
*/
onFocus: _propTypes.default.func,
/**
* @ignore
*/
onKeyDown: _propTypes.default.func,
/**
* @ignore
*/
onKeyUp: _propTypes.default.func,
/**
* The short hint displayed in the input before the user enters a value.
*/
placeholder: _propTypes.default.string,
/**
* It prevents the user from changing the value of the field
* (not from interacting with the field).
*/
readOnly: _propTypes.default.bool,
/**
* @ignore
*/
renderPrefix: _propTypes.default.func,
/**
* If `true`, the input will be required.
*/
required: _propTypes.default.bool,
/**
* Number of rows to display when multiline option is set to true.
*/
rows: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number]),
/**
* Maximum number of rows to display when multiline option is set to true.
*/
rowsMax: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number]),
/**
* Start `InputAdornment` for this component.
*/
startAdornment: _propTypes.default.node,
/**
* Type of the input element. It should be a valid HTML5 input type.
*/
type: _propTypes.default.string,
/**
* The input value, required for a controlled component.
*/
value: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number, _propTypes.default.bool, _propTypes.default.arrayOf(_propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number, _propTypes.default.bool]))])
} : {};
InputBase.defaultProps = {
fullWidth: false,
inputComponent: 'input',
multiline: false,
type: 'text'
};
InputBase.contextTypes = {
muiFormControl: _propTypes.default.object
};
InputBase.childContextTypes = {
muiFormControl: _propTypes.default.object
};
var _default = (0, _withStyles.default)(styles, {
name: 'MuiInputBase'
})(InputBase);
exports.default = _default;