Created
January 24, 2019 02:29
-
-
Save forgo/183f70a771c2fdddd5de455d6fbed699 to your computer and use it in GitHub Desktop.
Wrapper React component, allowing for more complex interaction-based styles "out-of-the props", so to speak.
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 characters
| import React from 'react' | |
| export const Key = { | |
| TAB: 9, | |
| ENTER: 13, | |
| SPACE: 32, | |
| END: 35, | |
| HOME: 36, | |
| LEFT: 37, | |
| UP: 38, | |
| RIGHT: 39, | |
| DOWN: 40, | |
| } | |
| class Interactive extends React.PureComponent { | |
| componentWillMount() { | |
| this.setState({ | |
| hovering: false, | |
| pressing: false, | |
| pressingGlobal: false, | |
| focusing: false, | |
| keying: false, | |
| isTouch: false, | |
| touching: false, | |
| touchingGlobal: false, | |
| }) | |
| } | |
| componentDidMount() { | |
| document.addEventListener('mouseup', this.interactiveGlobalMouseUp, false) | |
| document.addEventListener( | |
| 'mousedown', | |
| this.interactiveGlobalMouseDown, | |
| false | |
| ) | |
| document.addEventListener( | |
| 'touchstart', | |
| this.interactiveGlobalTouchStart, | |
| false | |
| ) | |
| document.addEventListener('touchend', this.interactiveGlobalTouchEnd, false) | |
| } | |
| componentWillUnmount() { | |
| document.removeEventListener( | |
| 'mouseup', | |
| this.interactiveGlobalMouseUp, | |
| false | |
| ) | |
| document.removeEventListener( | |
| 'mousedown', | |
| this.interactiveGlobalMouseDown, | |
| false | |
| ) | |
| document.removeEventListener( | |
| 'touchstart', | |
| this.interactiveGlobalTouchStart, | |
| false | |
| ) | |
| document.removeEventListener( | |
| 'touchend', | |
| this.interactiveGlobalTouchEnd, | |
| false | |
| ) | |
| } | |
| interactiveMouseOver = event => { | |
| this.setState(prevState => { | |
| return { | |
| ...prevState, | |
| hovering: !prevState.isTouch, | |
| pressing: prevState.pressingGlobal, | |
| touching: false, | |
| isTouch: false, | |
| } | |
| }) | |
| } | |
| interactiveMouseOut = event => { | |
| this.setState(prevState => { | |
| return { | |
| ...prevState, | |
| hovering: false, | |
| pressing: false, | |
| } | |
| }) | |
| } | |
| interactiveGlobalMouseUp = event => { | |
| this.setState(prevState => { | |
| return { | |
| ...prevState, | |
| pressingGlobal: false, | |
| } | |
| }) | |
| } | |
| interactiveGlobalMouseDown = event => { | |
| this.setState(prevState => { | |
| return { | |
| ...prevState, | |
| pressingGlobal: !prevState.isTouch, | |
| } | |
| }) | |
| } | |
| interactiveMouseDown = event => { | |
| this.setState(prevState => { | |
| return { | |
| ...prevState, | |
| pressing: true, | |
| } | |
| }) | |
| } | |
| interactiveMouseUp = event => { | |
| this.setState(prevState => { | |
| return { | |
| ...prevState, | |
| pressing: false, | |
| } | |
| }) | |
| } | |
| interactiveFocus = event => { | |
| this.setState(prevState => { | |
| return { | |
| ...prevState, | |
| focusing: true, | |
| } | |
| }) | |
| } | |
| interactiveBlur = event => { | |
| this.setState(prevState => { | |
| return { | |
| ...prevState, | |
| focusing: false, | |
| keying: false, | |
| } | |
| }) | |
| } | |
| interactiveKeyDown = event => { | |
| const stateKeyingIfFocusing = prevState => { | |
| return { | |
| ...prevState, | |
| keying: prevState.focusing, | |
| } | |
| } | |
| if (event.keyCode === Key.ENTER) { | |
| this.setState(stateKeyingIfFocusing) | |
| } | |
| else if (event.keyCode === Key.SPACE) { | |
| // prevent scrolling down on space press | |
| event.preventDefault() | |
| // TODO: allow boolean to style with SPACE if SPACE is a valid key interaction? | |
| // this.setState(stateKeyingIfFocusing) | |
| } | |
| } | |
| interactiveKeyUp = event => { | |
| this.setState(prevState => { | |
| return { | |
| ...prevState, | |
| keying: false, | |
| } | |
| }) | |
| } | |
| interactiveTouchStart = event => { | |
| this.setState(prevState => { | |
| return { | |
| ...prevState, | |
| isTouch: true, | |
| touching: true, | |
| } | |
| }) | |
| } | |
| interactiveGlobalTouchStart = event => { | |
| this.setState(prevState => { | |
| return { | |
| ...prevState, | |
| touchingGlobal: true, | |
| } | |
| }) | |
| } | |
| interactiveTouchMove = event => { | |
| this.setState(prevState => { | |
| return { | |
| ...prevState, | |
| } | |
| }) | |
| } | |
| interactiveTouchCancel = event => { | |
| this.setState(prevState => { | |
| return { | |
| ...prevState, | |
| touching: false, | |
| } | |
| }) | |
| } | |
| interactiveTouchEnd = event => { | |
| this.setState(prevState => { | |
| return { | |
| ...prevState, | |
| touching: false, | |
| } | |
| }) | |
| } | |
| interactiveGlobalTouchEnd = event => { | |
| this.setState(prevState => { | |
| return { | |
| ...prevState, | |
| touchingGlobal: false, | |
| } | |
| }) | |
| } | |
| interactiveEvents = () => { | |
| return { | |
| tabIndex: 0, | |
| onMouseOver: this.interactiveMouseOver, | |
| onMouseOut: this.interactiveMouseOut, | |
| onMouseDown: this.interactiveMouseDown, | |
| onMouseUp: this.interactiveMouseUp, | |
| onFocus: this.interactiveFocus, | |
| onBlur: this.interactiveBlur, | |
| onKeyDown: this.interactiveKeyDown, | |
| onKeyUp: this.interactiveKeyUp, | |
| onTouchStart: this.interactiveTouchStart, | |
| onTouchMove: this.interactiveTouchMove, | |
| onTouchCancel: this.interactiveTouchCancel, | |
| onTouchEnd: this.interactiveTouchEnd, | |
| } | |
| } | |
| interactiveStylesMerge = (styles, styleProp) => { | |
| if (styles[styleProp]) { | |
| const normalStyle = styles[styleProp].normal | |
| const hoverStyle = | |
| this.state.hovering && !this.state.isTouch | |
| ? styles[styleProp].hover | |
| : {} | |
| const pressStyle = | |
| (this.state.pressing || this.state.keying) && !this.state.isTouch | |
| ? styles[styleProp].press | |
| : {} | |
| const focusStyle = | |
| this.state.focusing && !this.state.isTouch | |
| ? styles[styleProp].focus | |
| : {} | |
| const keyStyle = | |
| this.state.keying && !this.state.isTouch ? styles[styleProp].key : {} | |
| const touchStyle = this.state.touching ? styles[styleProp].touch : {} | |
| return { | |
| ...normalStyle, | |
| ...hoverStyle, | |
| ...pressStyle, | |
| ...focusStyle, | |
| ...keyStyle, | |
| ...touchStyle, | |
| } | |
| } | |
| return {} | |
| } | |
| interactiveStyles = styles => { | |
| return (...styleProps) => { | |
| return styleProps.reduce((styleAccumulator, styleProp) => { | |
| return { | |
| ...styleAccumulator, | |
| ...this.interactiveStylesMerge(styles, styleProp), | |
| } | |
| }, {}) | |
| } | |
| } | |
| render() { | |
| const {userProps, Component, istyles} = this.props | |
| const mergedProps = { | |
| ...userProps, | |
| ievents: this.interactiveEvents(), | |
| istate: this.state, | |
| istyles: this.interactiveStyles(istyles), | |
| } | |
| return <Component {...mergedProps} /> | |
| } | |
| } | |
| export default (Component, istyles) => { | |
| return props => { | |
| const mergedProps = { | |
| userProps: props, | |
| Component: Component, | |
| istyles: istyles ? istyles : {}, | |
| } | |
| return <Interactive {...mergedProps} /> | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment