diff --git a/docs/src/app/components/pages/components/snackbar.jsx b/docs/src/app/components/pages/components/snackbar.jsx
index d92c56227acdac..ece71d615ce48d 100644
--- a/docs/src/app/components/pages/components/snackbar.jsx
+++ b/docs/src/app/components/pages/components/snackbar.jsx
@@ -10,10 +10,12 @@ export default class SnackbarPage extends React.Component {
constructor() {
super();
this._handleClick = this._handleClick.bind(this);
+ this._handleClickDouble = this._handleClickDouble.bind(this);
this._updateAutoHideDuration = this._updateAutoHideDuration.bind(this);
this.state = {
autoHideDuration: 0,
+ message: 'Event added to your calendar',
};
}
@@ -101,6 +103,13 @@ export default class SnackbarPage extends React.Component {
onTouchTap={this._handleClick}
label="Add to my calendar" />
+
+
+
+
+
@@ -123,6 +132,18 @@ export default class SnackbarPage extends React.Component {
this.refs.snackbar.show();
}
+ _handleClickDouble() {
+ this.refs.snackbar.show();
+
+ const duration = this.state.autoHideDuration / 2 || 2000;
+
+ setTimeout(() => {
+ this.setState({
+ message: 'Event ' + Math.round(Math.random() * 100) + ' added to your calendar',
+ });
+ }, duration);
+ }
+
_handleAction() {
//We can add more code here! In this example, we'll just include an alert.
window.alert("We removed the event from your calendar.");
diff --git a/src/snackbar.jsx b/src/snackbar.jsx
index 36ad17d4fd00dd..2d1d81ab25640c 100644
--- a/src/snackbar.jsx
+++ b/src/snackbar.jsx
@@ -1,28 +1,61 @@
const React = require('react');
-const ReactDOM = require('react-dom');
-const CssEvent = require('./utils/css-event');
const StylePropable = require('./mixins/style-propable');
const Transitions = require('./styles/transitions');
const ClickAwayable = require('./mixins/click-awayable');
const FlatButton = require('./flat-button');
const DefaultRawTheme = require('./styles/raw-themes/light-raw-theme');
const ThemeManager = require('./styles/theme-manager');
+const ContextPure = require('./mixins/context-pure');
+const StyleResizable = require('./mixins/style-resizable');
const Snackbar = React.createClass({
- mixins: [StylePropable, ClickAwayable],
+ mixins: [
+ StylePropable,
+ StyleResizable,
+ ClickAwayable,
+ ContextPure,
+ ],
manuallyBindClickAway: true,
// ID of the active timer.
_autoHideTimerId: undefined,
+ _oneAtTheTimeTimerId: undefined,
+
contextTypes: {
muiTheme: React.PropTypes.object,
},
+ getDefaultProps: function() {
+ return {
+ openOnMount: false,
+ };
+ },
+
+ statics: {
+ getRelevantContextKeys(muiTheme) {
+ const theme = muiTheme.snackbar;
+ const spacing = muiTheme.rawTheme.spacing;
+
+ return {
+ textColor: theme.textColor,
+ backgroundColor: theme.backgroundColor,
+ desktopGutter: spacing.desktopGutter,
+ desktopSubheaderHeight: spacing.desktopSubheaderHeight,
+ actionColor: theme.actionColor,
+ };
+ },
+ getChildrenClasses() {
+ return [
+ FlatButton,
+ ];
+ },
+ },
+
propTypes: {
- message: React.PropTypes.string.isRequired,
+ message: React.PropTypes.node.isRequired,
action: React.PropTypes.string,
autoHideDuration: React.PropTypes.number,
onActionTouchTap: React.PropTypes.func,
@@ -44,16 +77,40 @@ const Snackbar = React.createClass({
getInitialState() {
return {
- open: this.props.openOnMount || false,
+ open: this.props.openOnMount,
+ message: this.props.message,
+ action: this.props.action,
muiTheme: this.context.muiTheme ? this.context.muiTheme : ThemeManager.getMuiTheme(DefaultRawTheme),
};
},
- //to update theme inside state whenever a new theme is passed down
- //from the parent / owner using context
- componentWillReceiveProps (nextProps, nextContext) {
+ componentWillReceiveProps(nextProps, nextContext) {
+ //to update theme inside state whenever a new theme is passed down
+ //from the parent / owner using context
let newMuiTheme = nextContext.muiTheme ? nextContext.muiTheme : this.state.muiTheme;
this.setState({muiTheme: newMuiTheme});
+
+ if (this.state.open) {
+ this.setState({
+ open: false,
+ });
+
+ clearTimeout(this._oneAtTheTimeTimerId);
+ this._oneAtTheTimeTimerId = setTimeout(() => {
+ if (this.isMounted()) {
+ this.setState({
+ message: nextProps.message,
+ action: nextProps.action,
+ open: true,
+ });
+ }
+ }, 400);
+ } else {
+ this.setState({
+ message: nextProps.message,
+ action: nextProps.action,
+ });
+ }
},
componentDidMount() {
@@ -73,86 +130,103 @@ const Snackbar = React.createClass({
this._setAutoHideTimer();
//Only Bind clickaway after transition finishes
- CssEvent.onTransitionEnd(ReactDOM.findDOMNode(this), () => {
- this._bindClickAway();
- });
- }
- else {
+ setTimeout(() => {
+ if (this.isMounted()) {
+ this._bindClickAway();
+ }
+ }, 400);
+ } else {
+ clearTimeout(this._autoHideTimerId);
this._unbindClickAway();
}
}
},
componentWillUnmount() {
- this._clearAutoHideTimer();
+ clearTimeout(this._autoHideTimerId);
this._unbindClickAway();
},
- getTheme() {
- return this.state.muiTheme.snackbar;
- },
+ getStyles() {
+ const {
+ textColor,
+ backgroundColor,
+ desktopGutter,
+ desktopSubheaderHeight,
+ actionColor,
+ } = this.constructor.getRelevantContextKeys(this.state.muiTheme);
- getSpacing() {
- return this.state.muiTheme.rawTheme.spacing;
- },
+ const isSmall = this.state.deviceSize === this.constructor.Sizes.SMALL;
- getStyles() {
const styles = {
root: {
- color: this.getTheme().textColor,
- backgroundColor: this.getTheme().backgroundColor,
- borderRadius: 2,
- padding: '0px ' + this.getSpacing().desktopGutter + 'px',
- height: this.getSpacing().desktopSubheaderHeight,
- lineHeight: this.getSpacing().desktopSubheaderHeight + 'px',
- minWidth: 288,
- maxWidth: 568,
-
position: 'fixed',
- zIndex: 10,
- bottom: this.getSpacing().desktopGutter,
- marginLeft: this.getSpacing().desktopGutter,
-
left: 0,
- opacity: 0,
+ display: '-webkit-box; display: -webkit-flex; display: flex',
+ right: 0,
+ bottom: 0,
+ zIndex: 10,
visibility: 'hidden',
- transform: 'translate3d(0, 20px, 0)',
+ transform: 'translate3d(0, ' + desktopSubheaderHeight + 'px, 0)',
transition:
- Transitions.easeOut('0ms', 'left', '400ms') + ',' +
- Transitions.easeOut('400ms', 'opacity') + ',' +
Transitions.easeOut('400ms', 'transform') + ',' +
Transitions.easeOut('400ms', 'visibility'),
},
+ rootWhenOpen: {
+ visibility: 'visible',
+ transform: 'translate3d(0, 0, 0)',
+ },
+ body: {
+ backgroundColor: backgroundColor,
+ padding: '0 ' + desktopGutter + 'px',
+ height: desktopSubheaderHeight,
+ lineHeight: desktopSubheaderHeight + 'px',
+ borderRadius: isSmall ? 0 : 2,
+ maxWidth: isSmall ? 'inherit' : 568,
+ minWidth: isSmall ? 'inherit' : 288,
+ flexGrow: isSmall ? 1 : 0,
+ margin: 'auto',
+ },
+ content: {
+ fontSize: 14,
+ color: textColor,
+ opacity: 0,
+ transition: Transitions.easeOut('400ms', 'opacity'),
+ },
+ contentWhenOpen: {
+ opacity: 1,
+ transition: Transitions.easeOut('500ms', 'opacity', '100ms'),
+ },
action: {
- color: this.getTheme().actionColor,
+ color: actionColor,
float: 'right',
marginTop: 6,
marginRight: -16,
- marginLeft: this.getSpacing().desktopGutter,
+ marginLeft: desktopGutter,
backgroundColor: 'transparent',
},
- rootWhenOpen: {
- opacity: 1,
- visibility: 'visible',
- transform: 'translate3d(0, 0, 0)',
- transition:
- Transitions.easeOut('0ms', 'left', '0ms') + ',' +
- Transitions.easeOut('400ms', 'opacity', '0ms') + ',' +
- Transitions.easeOut('400ms', 'transform', '0ms') + ',' +
- Transitions.easeOut('400ms', 'visibility', '0ms'),
- },
};
return styles;
},
render() {
- const {action, message, onActionTouchTap, style, ...others } = this.props;
+ const {
+ onActionTouchTap,
+ style,
+ ...others,
+ } = this.props;
const styles = this.getStyles();
- const rootStyles = this.state.open ?
- this.prepareStyles(styles.root, styles.rootWhenOpen, style) :
- this.prepareStyles(styles.root, style);
+ const {
+ open,
+ action,
+ message,
+ } = this.state;
+
+ const rootStyles = open ?
+ this.mergeStyles(styles.root, styles.rootWhenOpen, style) :
+ this.mergeStyles(styles.root, style);
let actionButton;
if (action) {
@@ -164,35 +238,48 @@ const Snackbar = React.createClass({
);
}
+ const contentStyle = open ? this.mergeStyles(styles.content, styles.contentWhenOpen) : styles.content;
+
return (
-
- {message}
- {actionButton}
-
+
+
+
+ {message}
+ {actionButton}
+
+
+
);
},
show() {
- this.setState({ open: true });
- if (this.props.onShow) this.props.onShow();
+ this.setState({
+ open: true,
+ });
+
+ if (this.props.onShow) {
+ this.props.onShow();
+ }
},
dismiss() {
- this._clearAutoHideTimer();
- this.setState({ open: false });
- if (this.props.onDismiss) this.props.onDismiss();
- },
+ this.setState({
+ open: false,
+ });
- _clearAutoHideTimer() {
- if (this._autoHideTimerId !== undefined) {
- this._autoHideTimerId = clearTimeout(this._autoHideTimerId);
+ if (this.props.onDismiss) {
+ this.props.onDismiss();
}
},
_setAutoHideTimer() {
if (this.props.autoHideDuration > 0) {
- this._clearAutoHideTimer();
- this._autoHideTimerId = setTimeout(() => { this.dismiss(); }, this.props.autoHideDuration);
+ clearTimeout(this._autoHideTimerId);
+ this._autoHideTimerId = setTimeout(() => {
+ if (this.isMounted()) {
+ this.dismiss();
+ }
+ }, this.props.autoHideDuration);
}
},