react-customized-notifications
Version:
A React Notification System forked from https://github.com/igorprado/react-notification-system
352 lines (292 loc) • 8.66 kB
JSX
var React = require('react');
var createReactClass = require('create-react-class');
var PropTypes = require('prop-types');
var ReactDOM = require('react-dom');
var Constants = require('./constants');
var Helpers = require('./helpers');
var merge = require('object-assign');
/* From Modernizr */
var whichTransitionEvent = function() {
var el = document.createElement('fakeelement');
var transition;
var transitions = {
transition: 'transitionend',
OTransition: 'oTransitionEnd',
MozTransition: 'transitionend',
WebkitTransition: 'webkitTransitionEnd'
};
Object.keys(transitions).forEach(function(transitionKey) {
if (el.style[transitionKey] !== undefined) {
transition = transitions[transitionKey];
}
});
return transition;
};
var NotificationItem = createReactClass({
propTypes: {
notification: PropTypes.object,
getStyles: PropTypes.object,
onRemove: PropTypes.func,
allowHTML: PropTypes.bool,
noAnimation: PropTypes.bool,
children: PropTypes.oneOfType([
PropTypes.string,
PropTypes.element
]),
itemClassName: PropTypes.string,
itemOnClick: PropTypes.func
},
getDefaultProps: function() {
return {
noAnimation: false,
onRemove: function() {},
allowHTML: false
};
},
getInitialState: function() {
return {
visible: undefined,
removed: false
};
},
componentWillMount: function() {
var getStyles = this.props.getStyles;
var level = this.props.notification.level;
this._noAnimation = this.props.noAnimation;
this._styles = {
notification: getStyles.byElement('notification')(level),
title: getStyles.byElement('title')(level),
dismiss: getStyles.byElement('dismiss')(level),
messageWrapper: getStyles.byElement('messageWrapper')(level),
actionWrapper: getStyles.byElement('actionWrapper')(level),
action: getStyles.byElement('action')(level)
};
if (!this.props.notification.dismissible) {
this._styles.notification.cursor = 'default';
}
},
_styles: {},
_notificationTimer: null,
_height: 0,
_noAnimation: null,
_isMounted: false,
_removeCount: 0,
_getCssPropertyByPosition: function() {
var position = this.props.notification.position;
var css = {};
switch (position) {
case Constants.positions.tl:
case Constants.positions.bl:
css = {
property: 'left',
value: -200
};
break;
case Constants.positions.tr:
case Constants.positions.br:
// TODO introduce way of overwriting this
css = {
property: 'right',
value: -200
};
break;
case Constants.positions.tc:
css = {
property: 'top',
value: -100
};
break;
case Constants.positions.bc:
css = {
property: 'bottom',
value: -100
};
break;
default:
}
return css;
},
_defaultAction: function(event) {
var notification = this.props.notification;
event.preventDefault();
this._hideNotification();
if (typeof notification.action.callback === 'function') {
notification.action.callback();
}
},
_hideNotification: function() {
if (this._notificationTimer) {
this._notificationTimer.clear();
}
if (this._isMounted) {
this.setState({
visible: false,
removed: true
});
}
if (this._noAnimation) {
this._removeNotification();
}
},
_removeNotification: function() {
this.props.onRemove(this.props.notification.uid);
},
_dismiss: function() {
if (!this.props.notification.dismissible) {
return;
}
this._hideNotification();
},
_showNotification: function() {
var self = this;
setTimeout(function() {
if (self._isMounted) {
self.setState({
visible: true
});
}
}, 50);
},
_onTransitionEnd: function() {
if (this._removeCount > 0) return;
if (this.state.removed) {
this._removeCount += 1;
this._removeNotification();
}
},
componentDidMount: function() {
var self = this;
var transitionEvent = whichTransitionEvent();
var notification = this.props.notification;
var element = ReactDOM.findDOMNode(this);
this._height = element.offsetHeight;
this._isMounted = true;
// Watch for transition end
if (!this._noAnimation) {
if (transitionEvent) {
element.addEventListener(transitionEvent, this._onTransitionEnd);
} else {
this._noAnimation = true;
}
}
if (notification.autoDismiss) {
this._notificationTimer = new Helpers.Timer(function() {
self._hideNotification();
}, notification.autoDismiss * 1000);
}
this._showNotification();
},
_handleMouseEnter: function() {
var notification = this.props.notification;
if (notification.autoDismiss) {
this._notificationTimer.pause();
}
},
_handleMouseLeave: function() {
var notification = this.props.notification;
if (notification.autoDismiss) {
this._notificationTimer.resume();
}
},
componentWillUnmount: function() {
var element = ReactDOM.findDOMNode(this);
var transitionEvent = whichTransitionEvent();
element.removeEventListener(transitionEvent, this._onTransitionEnd);
this._isMounted = false;
},
_allowHTML: function(string) {
return { __html: string };
},
render: function() {
var notification = this.props.notification;
var className = 'notification notification-' + notification.level + ' ' + this.props.itemClassName;
var notificationStyle = merge({}, this._styles.notification);
var cssByPos = this._getCssPropertyByPosition();
var dismiss = null;
var actionButton = null;
var children = null;
var title = null;
var message = null;
var itemOnClick = this.props.itemOnClick;
if (this.state.visible) {
className += ' notification-visible';
} else if (this.state.visible === false) {
className += ' notification-hidden';
}
if (!notification.dismissible) {
className += ' notification-not-dismissible';
}
if (this.props.getStyles.overrideStyle) {
if (!this.state.visible && !this.state.removed) {
notificationStyle[cssByPos.property] = cssByPos.value;
}
if (this.state.visible && !this.state.removed) {
notificationStyle.height = this._height;
notificationStyle[cssByPos.property] = 0;
}
if (this.state.removed) {
notificationStyle.overlay = 'hidden';
notificationStyle.height = 0;
notificationStyle.marginTop = 0;
notificationStyle.paddingTop = 0;
notificationStyle.paddingBottom = 0;
}
notificationStyle.opacity = this.state.visible ? this._styles.notification.isVisible.opacity : this._styles.notification.isHidden.opacity;
}
if (notification.title) {
title = <h4 className="notification-title" style={ this._styles.title }>{ notification.title }</h4>;
}
if (notification.message) {
if (this.props.allowHTML) {
message = (
<div className="notification-message" style={ this._styles.messageWrapper } dangerouslySetInnerHTML={ this._allowHTML(notification.message) } />
);
} else {
message = (
<div className="notification-message" style={ this._styles.messageWrapper }>{ notification.message }</div>
);
}
}
if (notification.dismissible) {
dismiss = (
<span
className="notification-dismiss"
style={ this._styles.dismiss }
onClick={ this._dismiss }
>
×
</span>
);
}
if (notification.action) {
actionButton = (
<div className="notification-action-wrapper" style={ this._styles.actionWrapper }>
<button className="notification-action-button"
onClick={ this._defaultAction }
style={ this._styles.action }>
{ notification.action.label }
</button>
</div>
);
}
if (notification.children) {
children = notification.children;
}
return (
<div
className={ className }
onClick={ itemOnClick || this._dismiss }
onMouseEnter={ this._handleMouseEnter }
onMouseLeave={ this._handleMouseLeave }
style={ notificationStyle }
>
{ title }
{ message }
{ children }
{ dismiss }
{ actionButton }
</div>
);
}
});
module.exports = NotificationItem;