material-ui
Version:
Material Design UI components built with React
383 lines (321 loc) • 10.8 kB
JavaScript
'use strict';
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; };
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
var React = require('react/addons');
var WindowListenable = require('./mixins/window-listenable');
var CssEvent = require('./utils/css-event');
var KeyCode = require('./utils/key-code');
var Transitions = require('./styles/transitions');
var StylePropable = require('./mixins/style-propable');
var FlatButton = require('./flat-button');
var Overlay = require('./overlay');
var Paper = require('./paper');
var ReactTransitionGroup = React.addons.TransitionGroup;
var TransitionItem = React.createClass({
displayName: 'TransitionItem',
mixins: [StylePropable],
contextTypes: {
muiTheme: React.PropTypes.object
},
getInitialState: function getInitialState() {
return {
style: {}
};
},
componentWillEnter: function componentWillEnter(callback) {
var spacing = this.context.muiTheme.spacing;
this.setState({
style: {
opacity: 1,
transform: 'translate3d(0, ' + spacing.desktopKeylineIncrement + 'px, 0)'
}
});
setTimeout(callback, 450); // matches transition duration
},
componentWillLeave: function componentWillLeave(callback) {
var _this = this;
this.setState({
style: {
opacity: 0,
transform: 'translate3d(0, 0, 0)'
}
});
setTimeout((function () {
if (_this.isMounted()) callback();
}).bind(this), 450); // matches transition duration
},
render: function render() {
var _props = this.props;
var style = _props.style;
var other = _objectWithoutProperties(_props, ['style']);
return React.createElement(
'div',
_extends({}, other, { style: this.mergeAndPrefix(this.state.style, style) }),
this.props.children
);
}
});
var Dialog = React.createClass({
displayName: 'Dialog',
mixins: [WindowListenable, StylePropable],
contextTypes: {
muiTheme: React.PropTypes.object
},
propTypes: {
actions: React.PropTypes.array,
autoDetectWindowHeight: React.PropTypes.bool,
autoScrollBodyContent: React.PropTypes.bool,
bodyStyle: React.PropTypes.object,
contentClassName: React.PropTypes.string,
contentStyle: React.PropTypes.object,
modal: React.PropTypes.bool,
openImmediately: React.PropTypes.bool,
onClickAway: React.PropTypes.func,
onDismiss: React.PropTypes.func,
onShow: React.PropTypes.func,
repositionOnUpdate: React.PropTypes.bool,
title: React.PropTypes.node
},
windowListeners: {
keyup: '_handleWindowKeyUp',
resize: '_positionDialog'
},
getDefaultProps: function getDefaultProps() {
return {
autoDetectWindowHeight: false,
autoScrollBodyContent: false,
actions: [],
modal: false,
repositionOnUpdate: true
};
},
getInitialState: function getInitialState() {
return {
open: this.props.openImmediately || false
};
},
componentDidMount: function componentDidMount() {
this._positionDialog();
if (this.props.openImmediately) {
this.refs.dialogOverlay.preventScrolling();
this._onShow();
}
},
componentDidUpdate: function componentDidUpdate() {
this._positionDialog();
},
getStyles: function getStyles() {
var spacing = this.context.muiTheme.spacing;
var main = {
position: 'fixed',
boxSizing: 'border-box',
WebkitTapHighlightColor: 'rgba(0,0,0,0)',
zIndex: 10,
top: 0,
left: -10000,
width: '100%',
height: '100%',
transition: Transitions.easeOut('0ms', 'left', '450ms')
};
var content = {
boxSizing: 'border-box',
WebkitTapHighlightColor: 'rgba(0,0,0,0)',
transition: Transitions.easeOut(),
position: 'relative',
width: '75%',
maxWidth: spacing.desktopKeylineIncrement * 12,
margin: '0 auto',
zIndex: 10
};
var body = {
padding: spacing.desktopGutter,
overflowY: this.props.autoScrollBodyContent ? 'auto' : 'hidden',
overflowX: 'hidden'
};
var gutter = spacing.desktopGutter + 'px ';
var title = {
margin: 0,
padding: gutter + gutter + '0 ' + gutter,
color: this.context.muiTheme.palette.textColor,
fontSize: 24,
lineHeight: '32px',
fontWeight: '400'
};
if (this.state.open) {
main = this.mergeAndPrefix(main, {
left: 0,
transition: Transitions.easeOut('0ms', 'left', '0ms')
});
}
return {
main: this.mergeAndPrefix(main, this.props.style),
content: this.mergeAndPrefix(content, this.props.contentStyle),
paper: {
background: this.context.muiTheme.canvasColor
},
body: this.mergeStyles(body, this.props.bodyStyle),
title: this.mergeStyles(title, this.props.titleStyle)
};
},
render: function render() {
var styles = this.getStyles();
var actions = this._getActionsContainer(this.props.actions);
var title = undefined;
if (this.props.title) {
// If the title is a string, wrap in an h3 tag.
// If not, just use it as a node.
title = Object.prototype.toString.call(this.props.title) === '[object String]' ? React.createElement(
'h3',
{ style: styles.title },
this.props.title
) : this.props.title;
}
return React.createElement(
'div',
{ ref: 'container', style: styles.main },
React.createElement(
ReactTransitionGroup,
{ component: 'div', ref: 'dialogWindow' },
this.state.open && React.createElement(
TransitionItem,
{
className: this.props.contentClassName,
style: styles.content },
React.createElement(
Paper,
{
style: styles.paper,
zDepth: 4 },
title,
React.createElement(
'div',
{ ref: 'dialogContent', style: styles.body },
this.props.children
),
actions
)
)
),
React.createElement(Overlay, {
ref: 'dialogOverlay',
show: this.state.open,
autoLockScrolling: false,
onTouchTap: this._handleOverlayTouchTap })
);
},
isOpen: function isOpen() {
return this.state.open;
},
dismiss: function dismiss() {
var _this2 = this;
CssEvent.onTransitionEnd(this.getDOMNode(), (function () {
_this2.refs.dialogOverlay.allowScrolling();
}).bind(this));
this.setState({ open: false });
this._onDismiss();
},
show: function show() {
this.refs.dialogOverlay.preventScrolling();
this.setState({ open: true }, this._onShow);
},
_getAction: function _getAction(actionJSON, key) {
var _this3 = this;
var styles = { marginRight: 8 };
var props = {
key: key,
secondary: true,
onClick: actionJSON.onClick,
onTouchTap: function onTouchTap() {
if (actionJSON.onTouchTap) {
actionJSON.onTouchTap.call(undefined);
}
if (!(actionJSON.onClick || actionJSON.onTouchTap)) {
_this3.dismiss();
}
},
label: actionJSON.text,
style: styles
};
if (actionJSON.ref) {
props.ref = actionJSON.ref;
props.keyboardFocused = actionJSON.ref === this.props.actionFocus;
}
return React.createElement(FlatButton, props);
},
_getActionsContainer: function _getActionsContainer(actions) {
var actionContainer = undefined;
var actionObjects = [];
var actionStyle = {
boxSizing: 'border-box',
WebkitTapHighlightColor: 'rgba(0,0,0,0)',
padding: 8,
marginBottom: 8,
width: '100%',
textAlign: 'right'
};
if (actions.length) {
for (var i = 0; i < actions.length; i++) {
var currentAction = actions[i];
//if the current action isn't a react object, create one
if (!React.isValidElement(currentAction)) {
currentAction = this._getAction(currentAction, i);
}
actionObjects.push(currentAction);
}
actionContainer = React.createElement(
'div',
{ style: actionStyle },
actionObjects
);
}
return actionContainer;
},
_positionDialog: function _positionDialog() {
if (this.state.open) {
var clientHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
var container = this.getDOMNode();
var dialogWindow = this.refs.dialogWindow.getDOMNode();
var dialogContent = this.refs.dialogContent.getDOMNode();
var minPaddingTop = 16;
//Reset the height in case the window was resized.
dialogWindow.style.height = '';
dialogContent.style.height = '';
var dialogWindowHeight = dialogWindow.offsetHeight;
var paddingTop = (clientHeight - dialogWindowHeight) / 2 - 64;
if (paddingTop < minPaddingTop) paddingTop = minPaddingTop;
//Vertically center the dialog window, but make sure it doesn't
//transition to that position.
if (this.props.repositionOnUpdate || !container.style.paddingTop) {
container.style.paddingTop = paddingTop + 'px';
}
// Force a height if the dialog is taller than clientHeight
if (this.props.autoDetectWindowHeight || this.props.autoScrollBodyContent) {
var styles = this.getStyles();
var maxDialogContentHeight = clientHeight - 2 * (styles.body.padding + 64);
if (this.props.title) maxDialogContentHeight -= dialogContent.previousSibling.offsetHeight;
if (this.props.actions) maxDialogContentHeight -= dialogContent.nextSibling.offsetHeight;
dialogContent.style.maxHeight = maxDialogContentHeight + 'px';
}
}
},
_onShow: function _onShow() {
if (this.props.onShow) this.props.onShow();
},
_onDismiss: function _onDismiss() {
if (this.props.onDismiss) this.props.onDismiss();
},
_handleOverlayTouchTap: function _handleOverlayTouchTap(e) {
if (this.props.modal) {
e.stopPropagation();
} else {
this.dismiss();
if (this.props.onClickAway) this.props.onClickAway();
}
},
_handleWindowKeyUp: function _handleWindowKeyUp(e) {
if (e.keyCode === KeyCode.ESC && !this.props.modal) {
this.dismiss();
}
}
});
module.exports = Dialog;