+
+ Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid.
+ Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident.
+
+
+
+
+ );
+ }
+}
+
+React.render(, mountNode);
diff --git a/src/Collapse.js b/src/Collapse.js
new file mode 100644
index 0000000000..0b14eec0b4
--- /dev/null
+++ b/src/Collapse.js
@@ -0,0 +1,200 @@
+/*eslint-disable react/prop-types */
+'use strict';
+import React from 'react';
+import Transition from './Transition';
+import domUtils from './utils/domUtils';
+import createChainedFunction from './utils/createChainedFunction';
+
+let capitalize = str => str[0].toUpperCase() + str.substr(1);
+
+// reading a dimension prop will cause the browser to recalculate,
+// which will let our animations work
+let triggerBrowserReflow = node => node.offsetHeight; //eslint-disable-line no-unused-expressions
+
+const MARGINS = {
+ height: ['marginTop', 'marginBottom'],
+ width: ['marginLeft', 'marginRight']
+};
+
+function getDimensionValue(dimension, elem){
+ let value = elem[`offset${capitalize(dimension)}`];
+ let computedStyles = domUtils.getComputedStyles(elem);
+ let margins = MARGINS[dimension];
+
+ return (value +
+ parseInt(computedStyles[margins[0]], 10) +
+ parseInt(computedStyles[margins[1]], 10)
+ );
+}
+
+class Collapse extends React.Component {
+
+ constructor(props, context){
+ super(props, context);
+
+ this.onEnterListener = this.handleEnter.bind(this);
+ this.onEnteringListener = this.handleEntering.bind(this);
+ this.onEnteredListener = this.handleEntered.bind(this);
+ this.onExitListener = this.handleExit.bind(this);
+ this.onExitingListener = this.handleExiting.bind(this);
+ }
+
+ render() {
+ let enter = createChainedFunction(this.onEnterListener, this.props.onEnter);
+ let entering = createChainedFunction(this.onEnteringListener, this.props.onEntering);
+ let entered = createChainedFunction(this.onEnteredListener, this.props.onEntered);
+ let exit = createChainedFunction(this.onExitListener, this.props.onExit);
+ let exiting = createChainedFunction(this.onExitingListener, this.props.onExiting);
+
+ return (
+
+ { this.props.children }
+
+ );
+ }
+
+ /* -- Expanding -- */
+ handleEnter(elem){
+ let dimension = this._dimension();
+ elem.style[dimension] = '0';
+ }
+
+ handleEntering(elem){
+ let dimension = this._dimension();
+
+ elem.style[dimension] = this._getScrollDimensionValue(elem, dimension);
+ }
+
+ handleEntered(elem){
+ let dimension = this._dimension();
+ elem.style[dimension] = null;
+ }
+
+ /* -- Collapsing -- */
+ handleExit(elem){
+ let dimension = this._dimension();
+
+ elem.style[dimension] = this.props.getDimensionValue(dimension, elem) + 'px';
+ }
+
+ handleExiting(elem){
+ let dimension = this._dimension();
+
+ triggerBrowserReflow(elem);
+ elem.style[dimension] = '0';
+ }
+
+ _dimension(){
+ return typeof this.props.dimension === 'function'
+ ? this.props.dimension()
+ : this.props.dimension;
+ }
+
+ //for testing
+ _getTransitionInstance(){
+ return this.refs.transition;
+ }
+
+ _getScrollDimensionValue(elem, dimension){
+ return elem[`scroll${capitalize(dimension)}`] + 'px';
+ }
+}
+
+Collapse.propTypes = {
+ /**
+ * Collapse the Component in or out.
+ */
+ in: React.PropTypes.bool,
+
+ /**
+ * Provide the durration of the animation in milliseconds, used to ensure that finishing callbacks are fired even if the
+ * original browser transition end events are canceled.
+ */
+ duration: React.PropTypes.number,
+
+ /**
+ * Specifies the dimension used when collapsing.
+ *
+ * _Note: Bootstrap only partially supports this!
+ * You will need to supply your own css animation for the `.width` css class._
+ */
+ dimension: React.PropTypes.oneOfType([
+ React.PropTypes.oneOf(['height', 'width']),
+ React.PropTypes.func
+ ]),
+
+ /**
+ * A function that returns the height or width of the animating DOM node. Allows for providing some custom logic how much
+ * Collapse component should animation in its specified dimension.
+ *
+ * `getDimensionValue` is called with the current dimension prop value and the DOM node.
+ */
+ getDimensionValue: React.PropTypes.func,
+
+ /**
+ * A Callback fired before the component starts to expand.
+ */
+ onEnter: React.PropTypes.func,
+
+ /**
+ * A Callback fired immediately after the component starts to expand.
+ */
+ onEntering: React.PropTypes.func,
+
+ /**
+ * A Callback fired after the component has expanded.
+ */
+ onEntered: React.PropTypes.func,
+
+ /**
+ * A Callback fired before the component starts to collapse.
+ */
+ onExit: React.PropTypes.func,
+
+ /**
+ * A Callback fired immediately after the component starts to collapse.
+ */
+ onExiting: React.PropTypes.func,
+
+ /**
+ * A Callback fired after the component has collapsed.
+ */
+ onExited: React.PropTypes.func,
+
+ /**
+ * Specify whether the transitioning component should be unmounted (removed from the DOM) once the exit animation finishes.
+ */
+ unmountOnExit: React.PropTypes.bool,
+
+ /**
+ * Specify whether the component should collapse or expand when it mounts.
+ */
+ transitionAppear: React.PropTypes.bool
+};
+
+Collapse.defaultProps = {
+ in: false,
+ duration: 300,
+ dimension: 'height',
+ transitionAppear: false,
+ unmountOnExit: false,
+ getDimensionValue
+};
+
+export default Collapse;
+
diff --git a/src/CollapsibleNav.js b/src/CollapsibleNav.js
index 427d0b30f3..027f7cb29a 100644
--- a/src/CollapsibleNav.js
+++ b/src/CollapsibleNav.js
@@ -1,14 +1,13 @@
import React, { cloneElement } from 'react';
import BootstrapMixin from './BootstrapMixin';
-import CollapsibleMixin from './CollapsibleMixin';
+import Collapse from './Collapse';
import classNames from 'classnames';
-import domUtils from './utils/domUtils';
import ValidComponentChildren from './utils/ValidComponentChildren';
import createChainedFunction from './utils/createChainedFunction';
const CollapsibleNav = React.createClass({
- mixins: [BootstrapMixin, CollapsibleMixin],
+ mixins: [BootstrapMixin],
propTypes: {
onSelect: React.PropTypes.func,
@@ -19,41 +18,48 @@ const CollapsibleNav = React.createClass({
eventKey: React.PropTypes.any
},
- getCollapsibleDOMNode() {
- return React.findDOMNode(this);
- },
- getCollapsibleDimensionValue() {
- let height = 0;
- let nodes = this.refs;
- for (let key in nodes) {
- if (nodes.hasOwnProperty(key)) {
+ // getCollapsibleDimensionValue() {
+ // let height = 0;
+ // let nodes = this.refs;
+ // for (let key in nodes) {
+ // if (nodes.hasOwnProperty(key)) {
- let n = React.findDOMNode(nodes[key]);
- let h = n.offsetHeight;
- let computedStyles = domUtils.getComputedStyles(n);
+ // let n = React.findDOMNode(nodes[key]);
+ // let h = n.offsetHeight;
+ // let computedStyles = domUtils.getComputedStyles(n);
- height += (h +
- parseInt(computedStyles.marginTop, 10) +
- parseInt(computedStyles.marginBottom, 10)
- );
- }
- }
- return height;
- },
+ // height += (h +
+ // parseInt(computedStyles.marginTop, 10) +
+ // parseInt(computedStyles.marginBottom, 10)
+ // );
+ // }
+ // }
+ // return height;
+ // },
render() {
/*
* this.props.collapsible is set in NavBar when an eventKey is supplied.
*/
- const classes = this.props.collapsible ? this.getCollapsibleClassSet('navbar-collapse') : null;
+ const classes = this.props.collapsible ? 'navbar-collapse' : null;
const renderChildren = this.props.collapsible ? this.renderCollapsibleNavChildren : this.renderChildren;
- return (
+ let nav = (