Skip to content

Commit

Permalink
Merge 1bd408f into 5dadc6f
Browse files Browse the repository at this point in the history
  • Loading branch information
eddywashere committed Mar 13, 2016
2 parents 5dadc6f + 1bd408f commit a5b4eeb
Show file tree
Hide file tree
Showing 14 changed files with 720 additions and 1 deletion.
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ addons:
firefox: "latest"

before_install:
- export CHROME_BIN=chromium-browser
- export CHROME_BIN=/usr/bin/google-chrome
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- sudo apt-get install -y libappindicator1 fonts-liberation
- wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
- sudo dpkg -i google-chrome*.deb

after_script:
- npm run coverage
2 changes: 2 additions & 0 deletions example/js/Layout.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import ButtonsExample from './ButtonsExample';
import DropdownsExample from './DropdownsExample';
import ModalExample from './ModalExample';
import TetherExample from './TetherExample';
import TooltipExample from './TooltipExample';
import LabelsExample from './LabelsExample';
Expand All @@ -19,6 +20,7 @@ class Layout extends React.Component {
<DropdownsExample/>
<TetherExample/>
<TooltipExample/>
<ModalExample/>
<PopoverExample/>
<LabelsExample/>
</div>
Expand Down
45 changes: 45 additions & 0 deletions example/js/ModalExample.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* eslint react/no-multi-comp: 0, react/prop-types: 0 */

import React from 'react';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'lib/index';

class ModalExample extends React.Component {
constructor(props) {
super(props);
this.state = {
modal: false
};

this.toggle = this.toggle.bind(this);
}

toggle() {
this.setState({
modal: !this.state.modal
});
}

render() {
return (
<div>
<h3>Modals</h3>
<hr/>
<p>
<Button color="danger" onClick={this.toggle}>Launch Modal</Button>
</p>
<Modal isOpen={this.state.modal} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>Modal title</ModalHeader>
<ModalBody>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={this.toggle}>Do Something</Button>
<Button color="secondary" onClick={this.toggle}>Cancel</Button>
</ModalFooter>
</Modal>
</div>
);
}
}

export default ModalExample;
59 changes: 59 additions & 0 deletions lib/Fade.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React, { PropTypes } from 'react';
import classNames from 'classnames';

const propTypes = {
className: PropTypes.string
};

const defaultProps = {};

class Fade extends React.Component {
constructor(props) {
super(props);

this.state = {
fadeIn: false
};

this.fade = this.fade.bind(this);
this.fadeIn = this.fadeIn.bind(this);
this.fadeOut = this.fadeOut.bind(this);
}

fade(fade, cb, delay) {
this.setState({
fadeIn: fade
});

if (cb) {
setTimeout(cb, delay);
}
}

fadeIn(cb, delay) {
this.fade(true, cb, delay);
}

fadeOut(cb, delay) {
this.fade(false, cb, delay);
}

render() {
const classes = classNames(
this.props.className,
'fade',
{
in: this.state.fadeIn
}
);

return (
<div {...this.props} className={classes} />
);
}
}

Fade.propTypes = propTypes;
Fade.defaultProps = defaultProps;

export default Fade;
158 changes: 158 additions & 0 deletions lib/Modal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom';
import classNames from 'classnames';
import Fade from './Fade';

const propTypes = {
isOpen: PropTypes.bool,
size: PropTypes.string,
toggle: PropTypes.func.isRequired,
onEnter: PropTypes.func,
onExit: PropTypes.func
};

const defaultProps = {
isOpen: false
};

class Modal extends React.Component {
constructor(props) {
super(props);

this.handleProps = this.handleProps.bind(this);
this.handleBackdropClick = this.handleBackdropClick.bind(this);
this.handleEscape = this.handleEscape.bind(this);
this.destroy = this.destroy.bind(this);
this.onEnter = this.onEnter.bind(this);
this.onExit = this.onExit.bind(this);
}

componentDidMount() {
if (this.props.isOpen) {
this.show();
}
}

componentDidUpdate(prevProps) {
if (this.props.isOpen !== prevProps.isOpen) {
this.handleProps();
}
}

componentWillUnmount() {
this.hide();
}

onEnter() {
this._modal.fadeIn();
if (this.props.onEnter) {
this.props.onEnter();
}
}

onExit() {
this.destroy();
if (this.props.onExit) {
this.props.onExit();
}
}

handleEscape(e) {
if (e.keyCode === 27) {
this.props.toggle();
}
}

handleBackdropClick(e) {
const container = ReactDOM.findDOMNode(this._dialog);

if (e.target && !container.contains(e.target)) {
this.props.toggle();
}
}

handleProps() {
if (this.props.isOpen) {
this.show();
} else {
this.hide();
}
}

destroy() {
let classes = document.body.className.replace('modal-open', '');
this.removeEvents();

if (this._element) {
ReactDOM.unmountComponentAtNode(this._element);
document.body.removeChild(this._element);
this._element = null;
}

document.body.className = classNames(classes).trim();
}

removeEvents() {
document.removeEventListener('click', this.handleBackdropClick, false);
document.removeEventListener('keyup', this.handleEscape, false);
}

hide() {
this.removeEvents();

if (this._modal) {
this._modal.fadeOut();
}
if (this._backdrop) {
this._backdrop.fadeOut(this.onExit, 250);
}
}

show() {
const classes = document.body.className;
this._element = document.createElement('div');
this._element.setAttribute('tabindex', '-1');

document.body.appendChild(this._element);
document.addEventListener('click', this.handleBackdropClick, false);
document.addEventListener('keyup', this.handleEscape, false);

document.body.className = classNames(
classes,
'modal-open'
);

ReactDOM.unstable_renderSubtreeIntoContainer(
this,
this.renderChildren(),
this._element
);

this._element.focus();
this._backdrop.fadeIn(this.onEnter, 100);
}

renderChildren() {
return (
<div>
<Fade className="modal" style={{ display: 'block' }} tabIndex="-1" ref={(c) => this._modal = c }>
<div className="modal-dialog" role="document" ref={(c) => this._dialog = c }>
<div className="modal-content">
{ this.props.children }
</div>
</div>
</Fade>
<Fade className="modal-backdrop" ref={(c) => this._backdrop = c }/>
</div>
);
}

render() {
return null;
}
}

Modal.propTypes = propTypes;
Modal.defaultProps = defaultProps;

export default Modal;
21 changes: 21 additions & 0 deletions lib/ModalBody.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React, { PropTypes } from 'react';
import classNames from 'classnames';

const propTypes = {
className: PropTypes.string
};

const ModalBody = (props) => {
const classes = classNames(
props.className,
'modal-body'
);

return (
<div {...props} className={classes} />
);
};

ModalBody.propTypes = propTypes;

export default ModalBody;
21 changes: 21 additions & 0 deletions lib/ModalFooter.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React, { PropTypes } from 'react';
import classNames from 'classnames';

const propTypes = {
className: PropTypes.string
};

const ModalFooter = (props) => {
const classes = classNames(
props.className,
'modal-footer'
);

return (
<div {...props} className={classes} />
);
};

ModalFooter.propTypes = propTypes;

export default ModalFooter;
46 changes: 46 additions & 0 deletions lib/ModalHeader.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React, { PropTypes } from 'react';
import classNames from 'classnames';

const propTypes = {
toggle: PropTypes.func
};

const defaultProps = {};

class ModalHeader extends React.Component {
render() {
let closeButton;
const {
className,
children,
toggle,
...props } = this.props;

const classes = classNames(
className,
'modal-header'
);

if (toggle) {
closeButton = (
<button type="button" onClick={toggle} className="close" dataDismiss="modal" ariaLabel="Close">
<span ariaHidden="true">&times;</span>
</button>
);
}

return (
<div {...props} className={classes}>
{ closeButton }
<h4 className="modal-title">
{ children }
</h4>
</div>
);
}
}

ModalHeader.propTypes = propTypes;
ModalHeader.defaultProps = defaultProps;

export default ModalHeader;

0 comments on commit a5b4eeb

Please sign in to comment.