Skip to content

Instantly share code, notes, and snippets.

@zdavis
Last active October 2, 2015 21:23
Show Gist options
  • Save zdavis/3f3c168faf30a6e39dad to your computer and use it in GitHub Desktop.
Save zdavis/3f3c168faf30a6e39dad to your computer and use it in GitHub Desktop.
// The composing class, adds drodown functionality to Navigation
export default function dropdown(Composed) {
class Dropdown extends Component {
static propTypes() {
return {
};
}
constructor(props) {
super(props);
this.state = {
active: false
};
this.handler = null;
this.__outsideClickHandler = null;
}
isSourceFound(source, localNode) {
if(source == localNode) {
return true;
}
if(source.correspondingElement) {
return source.correspondingElement.classList.contains('ignore-react-onclickoutside');
}
return source.classList.contains('ignore-react-onclickoutside');
}
componentDidMount() {
const fn = this.__outsideClickHandler = ((localNode, eventHandler) => {
return (evt) => {
evt.stopPropagation();
let source = evt.target;
let found = false;
while(source.parentNode) {
found = this.isSourceFound(source, localNode);
if(found) return;
source = source.parentNode;
}
eventHandler(evt);
};
})(ReactDOM.findDOMNode(this), (e) => {this.handleClickOutside(e);});
this.handler = fn;
this.enableOnClickOutside();
}
handleClickOutside() {
if(this.state.active == true) {
this.close();
}
}
componentWillUnmount() {
this.disableOnClickOutside();
this.__outsideClickHandler = false;
this.handler = null;
}
enableOnClickOutside() {
document.addEventListener("mousedown", this.__outsideClickHandler);
document.addEventListener("touchstart", this.__outsideClickHandler);
}
disableOnClickOutside() {
document.removeEventListener("mousedown", this.__outsideClickHandler);
document.removeEventListener("touchstart", this.__outsideClickHandler);
}
close() {
this.setState({active: false});
this.props.actions.lowerCurtain();
}
open() {
this.setState({active: true});
this.props.actions.raiseCurtain();
}
toggle() {
if(this.state.active) {
this.close();
} else {
this.open();
}
}
render() {
return <Composed toggleCallback={(e, trigger) => { this.toggle(event, trigger);}} {...this.props} {...this.state} />;
}
}
return Dropdown;
)
// This is our navigation class. We don't want to reinvent dropdown logic every time we need a nav...
class Navigation extends Component {
static propTypes() {
return {
text: React.PropTypes.object
};
}
handleTriggerClick(e) {
if(e.target == this.refs.trigger) {
this.props.toggleCallback();
}
}
dropdown() {
if(this.props.active) {
return (
<nav ref="dropdown" className={'nested-nav'} >
<NavigationBranch contentDocumentsMap={this.props.contentDocumentsMap} level={1} textId={this.props.text.id} branch={this.props.text.toc} />
</nav>
);
}
}
render() {
const toc = this.props.text.toc;
if(this.props.text.toc) {
return (
<li className={cn('bar-nav-item', 'bar-nav-item-toc', {active: this.props.active})}>
<a className={'bar-nav-item-trigger'} ref="trigger" onClick={(e) => this.handleTriggerClick(e)}>
TOC
</a>
{this.dropdown()}
</li>
);
} else {
return null;
}
}
}
// The magic that ties it together.
export default dropdown(Navigation);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment