Created
April 23, 2018 21:12
-
-
Save TimCodes/abe11532ad73f62c72bfc662625b08ff to your computer and use it in GitHub Desktop.
Revisions
-
TimCodes created this gist
Apr 23, 2018 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1 @@ <div id="root"></div> This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,9 @@ React Horizontal Scrolling Menu ------------------------------- ES6 & React - Simple Click Counter Example Forked from [Brad Daily](http://codepen.io/bradleyboy/)'s Pen [ES6 playground](http://codepen.io/bradleyboy/pen/vEeENy/). A [Pen](https://codepen.io/TimCodes/pen/yjNEKe) by [Tim Hardy](https://codepen.io/TimCodes) on [CodePen](https://codepen.io). [License](https://codepen.io/TimCodes/pen/yjNEKe/license). This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,228 @@ let linkArr = [ { label : "Tables", isSelected : true} , { label : "Chairs", isSelected : false} , { label : "Beds", isSelected : false}, { label : "Ricks", isSelected : false} , { label : "Morty's", isSelected : false},{ label : "Sanchez", isSelected : false}, { label :"PinkBear", isSelected : false} , { label :"Raven", isSelected : false}, { label : "Penis", isSelected : false} ] const leftButton = ({onClick}) => ( <button onClick={onClick} className="pn-Advancer pn-Advancer_Left" type="button"> <svg className="pn-Advancer_Icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 551 1024"> <path d="M445.44 38.183L-2.53 512l447.97 473.817 85.857-81.173-409.6-433.23v81.172l409.6-433.23L445.44 38.18z"/> </svg> </button> ); class Counter extends React.Component { constructor(props) { super(props); this.state = { count: 6, linkArr : linkArr, navBarTravelling: false, navBarTravelDirection: "", navBarTravelDistance: 150, last_known_scroll_position: 0, ticking: false, hrNavOverFlow : "right" }; this.setSelected = this.setSelected.bind(this); this.determineOverflow = this.determineOverflow.bind(this); this.handleNavScroll = this.handleNavScroll.bind(this); this.setNavOverFlow = this.setNavOverFlow.bind(this); this.handleAdvanceLeftClick = this.handleAdvanceLeftClick.bind(this); this.handleAdvanceRightClick = this.handleAdvanceRightClick.bind(this); this.handleNavTransition = this.handleNavTransition.bind(this); } componentWillMount(){ document.documentElement.classList.remove("no-js"); document.documentElement.classList.add("js"); } componentDidMount(){ console.log("--- container monuted ---"); console.log(this.refs.hrzSlideNavContents) this.setNavOverFlow(); let { hrzSlideNav, hrzSlideNavContents } = this.refs; hrzSlideNavContents.getDOMNode().addEventListener( "transitionend",this.handleNavTransition ,false); } setNavOverFlow(){ let {hrzSlideNav, hrzSlideNavContents} = this.refs; this.setState( {hrNavOverflow : this.determineOverflow(hrzSlideNavContents.getDOMNode(), hrzSlideNav.getDOMNode())} ); hrzSlideNav.getDOMNode() .setAttribute( "data-overflowing", this.determineOverflow(hrzSlideNavContents.getDOMNode(), hrzSlideNav.getDOMNode()) ) } handleNavScroll(){ console.log("handlenavScroll") let{last_known_scroll_position, ticking} = this.state; this.setState({last_known_scroll_position :window.scrollY}); if (!ticking) { // use animation frame to get off of main thread window.requestAnimationFrame(function() { doSomething(last_known_scroll_position); this.setStae({ticking : false}); }); } this.setStae({ticking : true}); } handleAdvanceLeftClick(){ let { navBarTravelDistance, navBarTravelling, navBarTravelDirection } = this.state; let { hrzSlideNav, hrzSlideNavContents } = this.refs; // If in the middle of a move return if (navBarTravelling === true) { return; } // If we have content overflowing both sides or on the left if (this.determineOverflow(hrzSlideNavContents.getDOMNode(), hrzSlideNav.getDOMNode()) === "left" || this.determineOverflow(hrzSlideNavContents.getDOMNode(), hrzSlideNav.getDOMNode()) === "both") { // Find how far this panel has been scrolled var availableScrollLeft = hrzSlideNav.getDOMNode().scrollLeft; // If the space available is less than two lots of our desired distance, just move the whole amount // otherwise, move by the amount in the settings if (availableScrollLeft < navBarTravelDistance * 2) { hrzSlideNavContents.getDOMNode().style.transform = "translateX(" + availableScrollLeft + "px)"; } else { hrzSlideNavContents.getDOMNode().style.transform = "translateX(" + navBarTravelDistance + "px)"; } // We do want a transition (this is set in CSS) when moving so remove the class that would prevent that hrzSlideNavContents.getDOMNode().classList.remove("pn-ProductNav_Contents-no-transition"); // Update our settings this.setState({ navBarTravelDirection : "left", navBarTravelling: true }) } // Now update the attribute in the DOM hrzSlideNav.getDOMNode().setAttribute( "data-overflowing", this.determineOverflow(hrzSlideNavContents.getDOMNode(), hrzSlideNav.getDOMNode()) ); } handleAdvanceRightClick(){ console.log("handle right"); let { navBarTravelDistance, navBarTravelling, navBarTravelDirection } = this.state; let { hrzSlideNav, hrzSlideNavContents } = this.refs; // If in the middle of a move return if (navBarTravelling === true) { return; } // If we have content overflowing both sides or on the right if (this.determineOverflow(hrzSlideNavContents.getDOMNode(), hrzSlideNav.getDOMNode()) === "right" || this.determineOverflow(hrzSlideNavContents.getDOMNode(), hrzSlideNav.getDOMNode()) === "both") { // Get the right edge of the container and content var navBarRightEdge = hrzSlideNavContents.getDOMNode().getBoundingClientRect().right; var navBarScrollerRightEdge = hrzSlideNav.getDOMNode().getBoundingClientRect().right; // Now we know how much space we have available to scroll var availableScrollRight = Math.floor(navBarRightEdge - navBarScrollerRightEdge); // If the space available is less than two lots of our desired distance, just move the whole amount // otherwise, move by the amount in the settings if (availableScrollRight < navBarTravelDistance * 2) { hrzSlideNavContents.getDOMNode().style.transform = "translateX(-" + availableScrollRight + "px)"; } else { hrzSlideNavContents.getDOMNode().style.transform = "translateX(-" + navBarTravelDistance + "px)"; } // We do want a transition (this is set in CSS) when moving so remove the class that would prevent that hrzSlideNavContents.getDOMNode().classList.remove("pn-ProductNav_Contents-no-transition"); // Update our settings this.setState({ navBarTravelDirection : "right", navBarTravelling: true }) } // Now update the attribute in the DOM hrzSlideNav.getDOMNode().setAttribute("data-overflowing", this.determineOverflow(hrzSlideNavContents.getDOMNode(), hrzSlideNav.getDOMNode() )); } handleNavTransition(){ console.log("on transtion handler"); let { hrzSlideNav, hrzSlideNavContents } = this.refs; let { navBarTravelDistance, navBarTravelling, navBarTravelDirection } = this.state; hrzSlideNav = hrzSlideNav.getDOMNode(); hrzSlideNavContents = hrzSlideNavContents.getDOMNode(); // get the value of the transform, apply that to the current scroll position (so get the scroll pos first) and then remove the transform var styleOfTransform = window.getComputedStyle( hrzSlideNavContents, null); var tr = styleOfTransform.getPropertyValue("-webkit-transform") || styleOfTransform.getPropertyValue("transform"); // If there is no transition we want to default to 0 and not null var amount = Math.abs(parseInt(tr.split(",")[4]) || 0); hrzSlideNavContents.style.transform = "none"; hrzSlideNavContents.classList.add("pn-ProductNav_Contents-no-transition"); // Now lets set the scroll position if (navBarTravelDirection === "left") { hrzSlideNav.scrollLeft = hrzSlideNav.scrollLeft - amount; } else { hrzSlideNav.scrollLeft = hrzSlideNav.scrollLeft + amount; } this.setState({navBarTravelling : false}); } setSelected(label){ let newLinkArr =this.state.linkArr.map( l => { console.log(l) if(l.label == label){ l.isSelected = true; }else{ l.isSelected = false; } return l; }) this.setState({linkArr: newLinkArr}); } determineOverflow =(content, container) => { var containerMetrics = container.getBoundingClientRect(); var containerMetricsRight = Math.floor(containerMetrics.right); var containerMetricsLeft = Math.floor(containerMetrics.left); var contentMetrics = content.getBoundingClientRect(); var contentMetricsRight = Math.floor(contentMetrics.right); var contentMetricsLeft = Math.floor(contentMetrics.left); if (containerMetricsLeft > contentMetricsLeft && containerMetricsRight < contentMetricsRight) { return "both"; } else if (contentMetricsLeft < containerMetricsLeft) { return "left"; } else if (contentMetricsRight > containerMetricsRight) { return "right"; } else { return "none"; } }; render() { let{linkArr, hrNavOverFlow} = this.state; let links = linkArr.map(l => <a className = "pn-ProductNav_Link" aria-selected={l.isSelected} onClick = {() => this.setSelected(l.label)} > {l.label} </a> ); return ( <div className="pn-ProductNav_Wrapper"> <nav ref="hrzSlideNav" className="pn-ProductNav" data-overflowing="right"> <div ref="hrzSlideNavContents" onTransitionEnd = {() => this.handleNavTransition } className="pn-ProductNav_Contents"> {links} </div> <span classNameName="pn-ProductNav_Indicator"></span> </nav> <button onClick={this.handleAdvanceRightClick} className="pn-Advancer pn-Advancer_Right" type="button"> <svg className="pn-Advancer_Icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 551 1024"><path d="M105.56 985.817L553.53 512 105.56 38.183l-85.857 81.173 409.6 433.23v-81.172l-409.6 433.23 85.856 81.174z"/></svg> </button> </div> ); } } React.render( <Counter/>, document.getElementById("root") ); This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1 @@ <script src="//cdnjs.cloudflare.com/ajax/libs/react/0.13.0/react.min.js"></script> This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,107 @@ .pn-ProductNav_Wrapper { position: relative; padding: 0 11px; width: 200px; } .pn-ProductNav { /* Make this scrollable when needed */ overflow-x: auto; /* We don't want vertical scrolling */ overflow-y: hidden; /* For WebKit implementations, provide inertia scrolling */ -webkit-overflow-scrolling: touch; /* We don't want internal inline elements to wrap */ white-space: nowrap; /* If JS present, let's hide the default scrollbar */ .js & { /* Make an auto-hiding scroller for the 3 people using a IE */ -ms-overflow-style: -ms-autohiding-scrollbar; /* Remove the default scrollbar for WebKit implementations */ &::-webkit-scrollbar { display: none; } } /* positioning context for advancers */ position: relative; } .pn-ProductNav_Contents { float: left; transition: transform .2s ease-in-out; } .pn-ProductNav_Contents-no-transition { transition: none; } .pn-ProductNav_Link { text-decoration: none; color: #888; font-size: 1.2em; font-family: -apple-system, sans-serif; display: inline-flex; align-items: center; min-height: 44px; & + & { margin-left: 11px; padding-left: 11px; border-left: 1px solid #eee; } &[aria-selected="true"] { color: #111; } } .pn-Advancer { /* Reset the button */ appearance: none; background: transparent; padding: 0; border: 0; &:focus { outline: 0; } &:hover { cursor: pointer; } /* Now style it as needed */ position: absolute; top: 0; bottom: 0; /* Set the buttons invisible by default */ opacity: 0; transition: opacity .3s; } .pn-Advancer_Left { left: 0; [data-overflowing="both"] ~ &, [data-overflowing="left"] ~ & { opacity: 1; } } .pn-Advancer_Right { right: 0; [data-overflowing="both"] ~ &, [data-overflowing="right"] ~ & { opacity: 1; } } .pn-Advancer_Icon { width: 20px; height: 44px; fill: #bbb; } .pn-ProductNav_Indicator { position: absolute; bottom: 0; left: 0; height: 4px; width: 100px; background-color: #f90; }