diff --git a/docs/examples/Overlay.js b/docs/examples/Overlay.js new file mode 100644 index 0000000000..73586f5b45 --- /dev/null +++ b/docs/examples/Overlay.js @@ -0,0 +1,43 @@ + +const Example = React.createClass({ + getInitialState(){ + return { show: true }; + }, + + toggle(){ + this.setState({ show: !this.state.show }); + }, + + render(){ + const style = { + position: 'absolute', + backgroundColor: '#EEE', + border: '1px solid #CCC', + borderRadius: 3, + marginLeft: 5, + padding: 10 + }; + + return ( + <div style={{ height: 100, position: 'relative' }}> + <Button ref='target' onClick={this.toggle}> + I am an Overlay target + </Button> + + <Overlay + show={this.state.show} + onHide={() => this.setState({ show: false })} + placement="right" + container={this} + target={ props => React.findDOMNode(this.refs.target)} + > + <div style={style}> + <strong>Holy guacamole!</strong> Check this info. + </div> + </Overlay> + </div> + ); + } +}); + +React.render(<Example/>, mountNode); diff --git a/src/ModalBody.js b/src/ModalBody.js new file mode 100644 index 0000000000..40c667c001 --- /dev/null +++ b/src/ModalBody.js @@ -0,0 +1,26 @@ +import React from 'react'; +import classnames from 'classnames'; + +class ModalBody extends React.Component { + render() { + return ( + <div {...this.props} className={classnames(this.props.className, this.props.modalClassName)}> + {this.props.children} + </div> + ); + } +} + +ModalBody.propTypes = { + /** + * A css class applied to the Component + */ + modalClassName: React.PropTypes.string +}; + +ModalBody.defaultProps = { + modalClassName: 'modal-body' +}; + + +export default ModalBody; diff --git a/src/ModalFooter.js b/src/ModalFooter.js new file mode 100644 index 0000000000..39f8759fe5 --- /dev/null +++ b/src/ModalFooter.js @@ -0,0 +1,27 @@ +import React from 'react'; +import classnames from 'classnames'; + + +class ModalFooter extends React.Component { + + render() { + return ( + <div {...this.props} className={classnames(this.props.className, this.props.modalClassName)}> + {this.props.children} + </div> + ); + } +} + +ModalFooter.propTypes = { + /** + * A css class applied to the Component + */ + modalClassName: React.PropTypes.string +}; + +ModalFooter.defaultProps = { + modalClassName: 'modal-footer' +}; + +export default ModalFooter; diff --git a/src/ModalHeader.js b/src/ModalHeader.js new file mode 100644 index 0000000000..25eeaac440 --- /dev/null +++ b/src/ModalHeader.js @@ -0,0 +1,55 @@ +import React from 'react'; +import classnames from 'classnames'; + +class ModalHeader extends React.Component { + + render() { + return ( + <div + {...this.props} + className={classnames(this.props.className, this.props.modalClassName)} + > + { this.props.closeButton && + <button + className='close' + aria-label={this.props['aria-label'] || 'Close'} + onClick={this.props.onHide} + style={{ marginTop: -2 }} + > + <span aria-hidden="true"> + × + </span> + </button> + } + { this.props.children } + </div> + ); + } +} + +//used in liue of parent contexts right now to auto wire the close button +ModalHeader.__isModalHeader = true; + +ModalHeader.propTypes = { + /** + * A css class applied to the Component + */ + modalClassName: React.PropTypes.string, + /** + * Specify whether the Component should contain a close button + */ + closeButton: React.PropTypes.bool, + /** + * A Callback fired when the close button is clicked. If used directly inside a Modal component, the onHide will automatically + * be propagated up to the parent Modal `onHide`. + */ + onHide: React.PropTypes.func +}; + +ModalHeader.defaultProps = { + modalClassName: 'modal-header', + closeButton: false +}; + + +export default ModalHeader; diff --git a/src/ModalTitle.js b/src/ModalTitle.js new file mode 100644 index 0000000000..0c44bbaee5 --- /dev/null +++ b/src/ModalTitle.js @@ -0,0 +1,26 @@ +import React from 'react'; +import classnames from 'classnames'; + +class ModalTitle extends React.Component { + + render() { + return ( + <h4 {...this.props} className={classnames(this.props.className, 'modal-title')}> + { this.props.children } + </h4> + ); + } +} + +ModalTitle.propTypes = { + /** + * A css class applied to the Component + */ + modalClassName: React.PropTypes.string +}; + +ModalTitle.defaultProps = { + modalClassName: 'modal-title' +}; + +export default ModalTitle; diff --git a/src/Overlay.js b/src/Overlay.js new file mode 100644 index 0000000000..d425fe91a4 --- /dev/null +++ b/src/Overlay.js @@ -0,0 +1,63 @@ +/*eslint-disable object-shorthand, react/prop-types */ +import React from 'react'; +import Portal from './Portal'; +import Position from './Position'; +import RootCloseWrapper from './RootCloseWrapper'; + +class Overlay extends React.Component { + + constructor(props, context){ + super(props, context); + } + + render(){ + let { + container + , containerPadding + , target + , placement + , rootClose + , ...props } = this.props; + + let positionedChild = ( + <Position {...{ container, containerPadding, target, placement }}> + { this.props.children } + </Position> + ); + + if (rootClose) { + positionedChild = ( + <RootCloseWrapper onRootClose={this.props.onHide}> + { positionedChild } + </RootCloseWrapper> + ); + } + + return ( + <Portal container={container} rootClose={rootClose} onRootClose={this.props.onHide}> + { props.show && + positionedChild + } + </Portal> + ); + } +} + +Overlay.propTypes = { + ...Portal.propTypes, + ...Position.propTypes, + /** + * Set the visibility of the Overlay + */ + show: React.PropTypes.bool, + /** + * Specify whether the overlay should trigger onHide when the user clicks outside the overlay + */ + rootClose: React.PropTypes.bool, + /** + * A Callback fired by the Overlay when it wishes to be hidden. + */ + onHide: React.PropTypes.func +}; + +export default Overlay;