UNPKG

carbon-react

Version:

A library of reusable React components and an interface for easily building user interfaces based on Flux.

449 lines (362 loc) • 15.9 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; var _css = require('./../../css'); var _css2 = _interopRequireDefault(_css); var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _propTypes = require('prop-types'); var _propTypes2 = _interopRequireDefault(_propTypes); var _shouldComponentUpdate2 = require('./../../helpers/should-component-update'); var _shouldComponentUpdate3 = _interopRequireDefault(_shouldComponentUpdate2); var _lodash = require('lodash'); var _guid = require('./../../helpers/guid'); var _guid2 = _interopRequireDefault(_guid); var _classnames = require('classnames'); var _classnames2 = _interopRequireDefault(_classnames); var _icon = require('./../../../components/icon'); var _icon2 = _interopRequireDefault(_icon); var _help = require('./../../../components/help'); var _help2 = _interopRequireDefault(_help); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** * Input decorator. * * This decorator provides useful base operators for a typical input. * * == How to use Input decorator in a component: * * In your file: * * import Input from 'carbon/lib/utils/decorators/input; * * To use the decorator, wrap your component with it: * * const MyComponent = Input( * class MyComponent extends React.Component { * ... * }) * * This decorator provides methods you can use in your component class: * * * `mainClasses` - classes to apply to the main component element * * `inputClasses` - classes to apply to the input element * * `inputProps` - props to apply to the input element * * `inputHTML` - the html for the actual input * * `additionalInputContent` - extension point to add additional content * alongside the input * * You can also change the default input type from `input` to something else, * for example `textarea`, by defining a `inputType` getter method in your * components class. * * Inputs also accept a prop of `prefix` which outputs a prefix to the input: * * <Textbox prefix="foo" /> * * Inputs also accept a prop of `icon` which outputs an icon inside the input: * * <Textbox icon="foo" /> * * @method Input * @param {Class} ComposedComponent class to decorate * @return {Object} Decorated Component */ var Input = function Input(ComposedComponent) { var _class, _temp; return _temp = _class = function (_ComposedComponent) { _inherits(Component, _ComposedComponent); function Component() { var _ref; _classCallCheck(this, Component); for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } /** * A unique identifier for the input. * * @prop _guid * @return {String} */ var _this = _possibleConstructorReturn(this, (_ref = Component.__proto__ || Object.getPrototypeOf(Component)).call.apply(_ref, [this].concat(args))); _this._handleOnChange = function (ev) { if (_this.props.onChange) { // we also send the props so more information can be extracted by the action _this.props.onChange(ev, _this.props); } }; _this.setTextIndentation = function () { if (_this._input) { if (_this._prefix) { _this._input.style.paddingLeft = _this._prefix.offsetWidth + 11 + 'px'; } else { _this._input.style.paddingLeft = ""; } } }; _this._guid = (0, _guid2.default)(); return _this; } // common safeprops _createClass(Component, [{ key: 'componentDidMount', /** * A lifecycle method for when the component has rendered. * * @method componentWillReceiveProps * @return {void} */ value: function componentDidMount() { // call the components super method if it exists if (_get(Component.prototype.__proto__ || Object.getPrototypeOf(Component.prototype), 'componentDidMount', this)) { _get(Component.prototype.__proto__ || Object.getPrototypeOf(Component.prototype), 'componentDidMount', this).call(this); } if (this.props.prefix) { this.setTextIndentation(); } } /** * A lifecycle method for when the component has re-rendered. * * @method componentDidUpdate * @return {void} */ }, { key: 'componentDidUpdate', value: function componentDidUpdate(prevProps, prevState) { // call the components super method if it exists if (_get(Component.prototype.__proto__ || Object.getPrototypeOf(Component.prototype), 'componentDidUpdate', this)) { _get(Component.prototype.__proto__ || Object.getPrototypeOf(Component.prototype), 'componentDidUpdate', this).call(this, prevProps, prevState); } if (this.props.prefix != prevProps.prefix || this.props.icon != prevProps.icon) { this.setTextIndentation(); } } /** * A lifecycle method to determine if the component should re-render for better performance. * * @method shouldComponentUpdate * @param {Object} nextProps the updated props * @param {Object} nextState the updated state * @return {Boolean} true if the component should update */ }, { key: 'shouldComponentUpdate', value: function shouldComponentUpdate(nextProps, nextState) { // call super method if one is defined var changeDetected = _get(Component.prototype.__proto__ || Object.getPrototypeOf(Component.prototype), 'shouldComponentUpdate', this) ? _get(Component.prototype.__proto__ || Object.getPrototypeOf(Component.prototype), 'shouldComponentUpdate', this).call(this, nextProps, nextState) : false; // determine if anything has changed that should result in a re-render if (changeDetected || (0, _shouldComponentUpdate3.default)(this, nextProps, nextState)) { return true; } return false; } /** * Calls the onChange event defined by the dev with more useful information. * * @method _handleChange * @param {Event} ev the change event * @returns {void} */ /** * Sets indentation of input value based on prefix width. * * @method setTextIndentation * @return {void} */ }, { key: 'mainClasses', /** * Extends main classes to add ones for the input. * * @method mainClasses * @return {String} Main class names */ get: function get() { var _classNames; var classes = _get(Component.prototype.__proto__ || Object.getPrototypeOf(Component.prototype), 'mainClasses', this); return (0, _classnames2.default)(classes, this.props.className, _css2.default.input, (_classNames = {}, _defineProperty(_classNames, _css2.default.input + '--readonly', this.props.readOnly), _defineProperty(_classNames, _css2.default.input + '--align-' + this.props.align, this.props.align), _defineProperty(_classNames, _css2.default.input + '--with-prefix', this.props.prefix), _defineProperty(_classNames, _css2.default.input + '--with-input-help', this.props.inputHelp), _defineProperty(_classNames, _css2.default.input + '--disabled', this.props.disabled), _classNames)); } /** * Extends input classes to add ones for the input. * * @method inputClasses * @return {String} Input class names */ }, { key: 'inputClasses', get: function get() { var classes = _get(Component.prototype.__proto__ || Object.getPrototypeOf(Component.prototype), 'inputClasses', this) || ""; return classes + ' common-input__input'; } /** * Extends input props add additional properties for the input. * * @method inputProps * @return {Object} Input props */ }, { key: 'inputProps', get: function get() { var _this2 = this; var inputProps = _get(Component.prototype.__proto__ || Object.getPrototypeOf(Component.prototype), 'inputProps', this) || {}; // store ref to input inputProps.ref = function (c) { _this2._input = c; }; // disable autoComplete (causes performance issues in IE) inputProps.autoComplete = this.props.autoComplete || "off"; // only thread the onChange event through the handler if the event is defined by the dev if (this.props.onChange === inputProps.onChange) { inputProps.onChange = this._handleOnChange; } // Pass onPaste action to input element inputProps.onPaste = this.props.onPaste; // Adds data tag for automation inputProps["data-element"] = "input"; // Remove data-role as this should be applied on the top level element delete inputProps["data-role"]; return inputProps; } /** * Extends field props add additional properties for the containing field. * * @method fieldProps * @return {Object} Field props */ }, { key: 'fieldProps', get: function get() { var fieldProps = _get(Component.prototype.__proto__ || Object.getPrototypeOf(Component.prototype), 'fieldProps', this) || {}; fieldProps.className = 'common-input__field'; return fieldProps; } /** * Defaults to `input`, but a developer can override it in their own class * to something different. * * @method inputType * @return {String} HTML input type */ }, { key: 'inputType', get: function get() { return _get(Component.prototype.__proto__ || Object.getPrototypeOf(Component.prototype), 'inputType', this) || 'input'; } /** * Extension point to add additional content to the input * * @method additionalInputContent * @return {Object | HTML | String | Number} additional content from composed class */ }, { key: 'additionalInputContent', get: function get() { return _get(Component.prototype.__proto__ || Object.getPrototypeOf(Component.prototype), 'additionalInputContent', this) || null; } /** * Adds a prefix if it is defined * * @method prefixHTML * @return {Object} */ }, { key: 'prefixHTML', get: function get() { var _this3 = this; if (this.props.prefix) { return _react2.default.createElement( 'div', { ref: function ref(c) { _this3._prefix = c; }, className: 'common-input__prefix' }, this.props.prefix ); } } /** * Adds an icon if it is defined * * @method iconHTML * @return {Object} */ }, { key: 'iconHTML', get: function get() { if (this.props.icon) { return _react2.default.createElement( 'div', { className: 'common-input__input-icon' }, _react2.default.createElement(_icon2.default, { type: this.props.icon }) ); } } /** * Supplies the HTML for inputHelp component * * @method inputHelpHTML * @return {Object} JSX for help */ }, { key: 'inputHelpHTML', get: function get() { if (this.props.inputHelp) { return _react2.default.createElement( _help2.default, { className: 'common-input__input-help', tooltipPosition: this.props.inputHelpPosition, tooltipAlign: this.props.inputHelpAlign, href: this.props.inputHelpHref }, this.props.inputHelp ); } } /** * Returns HTML for the input. * * @method inputHTML * @return {HTML} HTML for input */ }, { key: 'inputHTML', get: function get() { var input = void 0; if (this.props.fakeInput) { // renders a fake input - useful for screens with lots of inputs var classes = (0, _classnames2.default)(this.inputProps.className, 'common-input__input--fake'); input = _react2.default.createElement( 'div', { className: classes, onMouseOver: this.inputProps.onMouseOver }, this.inputProps.value || this.inputProps.placeholder ); } else { // builds the input with a variable input type - see `inputType` input = _react2.default.createElement(this.inputType, _extends({}, this.inputProps)); } return _react2.default.createElement( 'div', this.fieldProps, this.iconHTML, this.prefixHTML, input, this.additionalInputContent, this.inputHelpHTML ); } }]); return Component; }(ComposedComponent), _class.contextTypes = (0, _lodash.assign)({}, ComposedComponent.contextTypes, { form: _propTypes2.default.object }), _class.propTypes = (0, _lodash.assign)({}, ComposedComponent.propTypes, {}), _class.safeProps = (0, _lodash.union)([], ComposedComponent.safeProps, ['value']), _temp; }; exports.default = Input;