UNPKG

carbon-react

Version:

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

608 lines (500 loc) • 16.6 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _react2 = require('react'); var _react3 = _interopRequireDefault(_react2); var _babelTransform = require('livereactload/babel-transform'); var _babelTransform2 = _interopRequireDefault(_babelTransform); 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 _class, _temp2; var _propTypes = require('prop-types'); var _propTypes2 = _interopRequireDefault(_propTypes); var _i18nJs = require('i18n-js'); var _i18nJs2 = _interopRequireDefault(_i18nJs); var _classnames = require('classnames'); var _classnames2 = _interopRequireDefault(_classnames); var _reactAddonsCssTransitionGroup = require('react-addons-css-transition-group'); var _reactAddonsCssTransitionGroup2 = _interopRequireDefault(_reactAddonsCssTransitionGroup); var _lodash = require('lodash'); var _shouldComponentUpdate2 = require('./../../utils/helpers/should-component-update'); var _shouldComponentUpdate3 = _interopRequireDefault(_shouldComponentUpdate2); var _icon = require('./../icon'); var _icon2 = _interopRequireDefault(_icon); var _alert = require('./../alert'); var _alert2 = _interopRequireDefault(_alert); var _link = require('./../link'); var _link2 = _interopRequireDefault(_link); var _tags = require('../../utils/helpers/tags'); 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; } var _components = { Flash: { displayName: 'Flash' } }; var _livereactloadBabelTransform2 = (0, _babelTransform2.default)({ filename: 'src/components/flash/flash.js', components: _components, locals: [], imports: [_react3.default] }); function _wrapComponent(id) { return function (Component) { return _livereactloadBabelTransform2(Component, id); }; } /** * A Flash widget. * * The flash is rendered in two sections: a ventral message 'flash', and a * dorsal coloured, expanding 'slider'. * * == How to use an Flash in a component: * * In your file * * import Flash from 'carbon/lib/components/flash'; * * To render a Flash, setup open and cancel handlers in your view to trigger * the message on and off: * * <Flash open={ openStatus } onDismiss={ myOnDismiss } message='Alert!' /> * * By default, the flash renders with a clickable close icon that hooks up with the onDismiss function. * * To instead have the flash disappear after a given time period, pass a prop of timeout in milliseconds. * * <Flash open={ openStatus } onDismiss={ myOnDismiss } message='Alert!' timeout={ 2000 }/> * * The flash message can be formatted in the following ways: * * * A string: "Alert" * * An array: ["Message One", "Message Two"] * * An object with description: { description: "My description" } * * An object of key/value pairs: { first_name: "is required", last_name: "is required" } * * An object with description with nested key/value pairs: * { description: { first_name: "is required", last_name: "is required" } } * * If a message is too long, it can be proxied to a dialog by adding `::more::` in your description. * * let message = "This is too long ::more:: This sentence is proxied to a dialog." * * @class Flash * @constructor */ var Flash = _wrapComponent('Flash')((_temp2 = _class = function (_React$Component) { _inherits(Flash, _React$Component); function Flash() { var _ref; var _temp, _this, _ret; _classCallCheck(this, Flash); for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = Flash.__proto__ || Object.getPrototypeOf(Flash)).call.apply(_ref, [this].concat(args))), _this), _this.state = { /** * Keeps track on the open state of each dialog * * @property dialogs * @type {Object} */ dialogs: {} /** * Resets the dialog open states if flash is opened/closed. * * @method componentWillReceiveProps * @return(Void) */ }, _this.dialogs = [], _this.timeout = null, _this.startTimeout = function () { _this.stopTimeout(); if (_this.shouldStartTimeout()) { _this.timeout = setTimeout(function () { _this.props.onDismiss(); }, _this.props.timeout); } }, _this.shouldStartTimeout = function () { if (!_this.props.timeout || !_this.props.open) { return false; } var shouldStartTimeout = true; for (var key in _this.state.dialogs) { if (_this.state.dialogs[key]) { shouldStartTimeout = false; } } return shouldStartTimeout; }, _this.stopTimeout = function () { clearTimeout(_this.timeout); }, _this.toggleDialog = function (key) { return function (ev) { if (ev) { ev.preventDefault(); } var state = _this.state.dialogs[key]; // open/close the dialog _this.setState({ dialogs: _defineProperty({}, key, !state) }); // start/stop the timer if the dialog opens or closes if (state) { _this.startTimeout(); } else { _this.stopTimeout(); } }; }, _this.formatDescription = function (description) { var object = (0, _lodash.isObject)(description), array = (0, _lodash.isArray)(description); _this.dialogs = []; if (array || object) { var items = []; // iterate through the object or array (0, _lodash.forEach)(description, function (value, key) { var itemValue = void 0; // pass the value through the find more parser var text = _this.findMore(value); if (!array && !/(^base|\.base)$/.test(key)) { // if object, apply key to each item itemValue = _react3.default.createElement( 'span', null, key, ': ', text ); } else { // otherwise just set value itemValue = text; } // add item to list items.push(_react3.default.createElement( 'li', { key: key }, itemValue )); }); return _react3.default.createElement( 'ul', null, items ); } // if just a string, pass it through the find more parser return _this.findMore(description); }, _this.findMore = function (text) { var value = text; if (typeof text !== 'string') { return value; } // detect any instances of "::more::" in the text var parts = text.split('::more::'); if (parts.length > 1) { var title = parts[0].trim(), desc = parts[1].trim(), info = _i18nJs2.default.t('notifications.more_info', { defaultValue: 'More Information' }); // create dialog for additional content _this.dialogs.push(_react3.default.createElement( _alert2.default, { 'data-element': 'info-dialog', key: title, title: title, open: _this.state.dialogs[title] || false, onCancel: _this.toggleDialog(title) }, desc )); // create text for item value = _react3.default.createElement( 'span', null, title, '\xA0', _react3.default.createElement( _link2.default, { onClick: _this.toggleDialog(title), className: 'carbon-flash__link', 'data-element': 'more-info' }, info ) ); } return value; }, _temp), _possibleConstructorReturn(_this, _ret); } _createClass(Flash, [{ key: 'componentWillReceiveProps', value: function componentWillReceiveProps(prevProps) { if (prevProps.open !== this.props.open) { this.setState({ dialogs: {} }); } } /** * Determines if the component should be updated or not. Required for this component * as it determines if the timeout should be reset or not. * * @method shouldComponentUpdate * @return {Boolean} */ }, { key: 'shouldComponentUpdate', value: function shouldComponentUpdate(nextProps, nextState) { return (0, _shouldComponentUpdate3.default)(this, nextProps, nextState); } /** * Conditionally triggers close action after flash displayed. * * @method componentDidUpdate * @return(Void) */ }, { key: 'componentDidUpdate', value: function componentDidUpdate() { // reset dialogs to render this.dialogs = []; this.startTimeout(); } /** * Keeps track of additional dialogs to render for "more info" links * * @property dialogs * @type {Array} */ /** * A timeout for when a flash should auto-dismiss * * @property timeout * @type {Timer} */ /** * Starts the timer to auto dismiss flash messages. * * @method startTimeout * @return(Void) */ /** * Determines if the timeout should be started. * * @method shouldStartTimeout * @return {Boolean} */ /** * Stops the timer to auto dismiss flash messages. * * @method stopTimeout * @return(Void) */ /** * Opens/closes the dialog for the given key. * * @method toggleDialog * @param {String} key * @param {Object} ev * @return(Void) */ /** * Given a description, format it accordingly. * * @method toggleDialog * @param {String} description * @return {HTML} */ /** * Splits the string and sets additional content inside a dialog. * * @method findMore * @param {String} text * @return {HTML} */ }, { key: 'render', /** * @method render * @return {Object} JSX */ value: function render() { var flashHTML = void 0, sliderHTML = void 0; if (this.props.open) { flashHTML = this.flashHTML; sliderHTML = this.sliderHTML; } return _react3.default.createElement( 'div', (0, _tags.tagComponent)('flash', this.props), _react3.default.createElement( 'div', { className: this.classes }, _react3.default.createElement( _reactAddonsCssTransitionGroup2.default, { transitionName: 'carbon-flash__slider', transitionEnterTimeout: 600, transitionLeaveTimeout: 600 }, sliderHTML, _react3.default.createElement( _reactAddonsCssTransitionGroup2.default, { transitionName: 'carbon-flash__content', transitionEnterTimeout: 800, transitionLeaveTimeout: 500 }, flashHTML ) ) ), this.dialogs ); } }, { key: 'iconType', /** * Returns the icon to display depending on type * * @method iconType * @return {String} */ get: function get() { var icon = void 0; switch (this.props.as) { case 'success': icon = 'tick'; break; default: icon = this.props.as; break; } return icon; } /** * Parses the message object to get the appropriate description * * @method description * @return {String} */ }, { key: 'description', get: function get() { var message = this.props.message; if ((0, _lodash.isObject)(message) && message.description) { // if defined, return description return message.description; } // otherwise, just return itself return message; } /** * Returns the computed HTML for the flash. * * @method flashHTML * @return {Object} JSX for flash */ }, { key: 'flashHTML', get: function get() { var contents = []; // add icon contents.push(_react3.default.createElement(_icon2.default, { className: 'carbon-flash__icon', type: this.iconType, key: 'icon' })); // add message content contents.push(_react3.default.createElement( 'div', { className: 'carbon-flash__message', key: 'message', 'data-element': 'message' }, this.formatDescription(this.description) )); // if auto-dismiss is not enabled, add a close icon if (!this.props.timeout) { contents.push(_react3.default.createElement(_icon2.default, { className: 'carbon-flash__close', 'data-element': 'close', key: 'close', onClick: this.props.onDismiss, type: 'close' })); } return _react3.default.createElement( 'div', { className: 'carbon-flash__content' }, contents ); } /** * Returns the computed HTML for the slider. * * @method flashHTML * @return {Object} JSX for flash */ }, { key: 'sliderHTML', get: function get() { return _react3.default.createElement('div', { className: 'carbon-flash__slider', key: 'slider' }); } /** * Returns the classes for the component. * * @method classes * @return {String} */ }, { key: 'classes', get: function get() { return (0, _classnames2.default)('carbon-flash', this.props.className, 'carbon-flash--' + this.props.as); } }]); return Flash; }(_react3.default.Component), _class.propTypes = { /** * Custom className * * @property className * @type {String} */ className: _propTypes2.default.string, /** * A custom close event handler * * @property onDismiss * @type {Function} */ onDismiss: _propTypes2.default.func.isRequired, /** * Sets the open state of the flash. * * @property open * @type {Boolean} * @default false */ open: _propTypes2.default.bool.isRequired, /** * Type of notification. * (see the 'iconColorSets' for possible values) * * @property as * @type {String} * @default 'success' */ as: _propTypes2.default.string, /** * Contents of message. * * @property message * @type {String|Object|Array} */ message: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.object, _propTypes2.default.array]).isRequired, /** * Time for flash to remain on screen * * @property timeout * @type {Number} in milliseconds */ timeout: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.number]) }, _class.defaultProps = { as: 'success', className: '', timeout: 0 }, _temp2)); exports.default = Flash;