Skip to content

Commit

Permalink
Combined event wrappers; got controlled component calling back to con…
Browse files Browse the repository at this point in the history
…sumer when it would normally close
  • Loading branch information
rpearce committed Sep 20, 2017
1 parent e6f9fa2 commit 1e3e9f3
Show file tree
Hide file tree
Showing 8 changed files with 455 additions and 561 deletions.
722 changes: 317 additions & 405 deletions example/build/app.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion nodemon.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"exec": "npm run build && npm start",
"exec": "npm run build && node index.js",
"ext": "js",
"verbose": true,
"watch": [
Expand Down
105 changes: 105 additions & 0 deletions src/EventsWrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import React, { Component } from 'react'
import { element, func } from 'prop-types'
import defaults from './defaults'

export default class EventsWrapper extends Component {
constructor() {
super()
this.unzoom = this.unzoom.bind(this)
this._handleScroll = this._handleScroll.bind(this)
this._handleKeyUp = this._handleKeyUp.bind(this)
this._handleResize = this._handleResize.bind(this)
this._handleTouchStart = this._handleTouchStart.bind(this)
this._handleTouchMove = this._handleTouchMove.bind(this)
this._handleTouchEnd = this._handleTouchEnd.bind(this)
}

componentDidMount() {
this.pageYOffset = window.pageYOffset
setTimeout(() => {
window.addEventListener('scroll', this._handleScroll, true)
window.addEventListener('keyup', this._handleKeyUp)
window.addEventListener('ontouchstart', this._handleTouchStart)
window.addEventListener('ontouchmove', this._handleTouchMove)
window.addEventListener('ontouchend', this._handleTouchEnd)
window.addEventListener('ontouchcancel', this._handleTouchEnd)
window.addEventListener('resize', this._handleResize)
}, defaults.transitionDuration)
}

componentWillUnmount() {
window.removeEventListener('scroll', this._handleScroll, true)
window.removeEventListener('keyup', this._handleKeyUp)
window.removeEventListener('ontouchstart', this._handleTouchStart)
window.removeEventListener('ontouchmove', this._handleTouchMove)
window.removeEventListener('ontouchend', this._handleTouchEnd)
window.removeEventListener('ontouchcancel', this._handleTouchEnd)
window.removeEventListener('resize', this._handleResize)
}

render() {
return (
<div onClick={this.unzoom}>
{this._cloneChild()}
</div>
)
}

unzoom({ force=false }={}) {
if (this.props.controlledEventFn && !force) {
return this.props.controlledEventFn()
}

return this.refs.child.unzoom()
}

_cloneChild() {
return React.cloneElement(
React.Children.only(this.props.children),
{ ref: 'child' }
)
}

_handleKeyUp({ which }) {
const opts = {
27: this.unzoom
}

if (opts[which]) {
return opts[which]()
}
}

_handleResize() {
this.forceUpdate()
}

_handleScroll(e) {
this.forceUpdate()
const scrollChange = Math.abs(window.pageYOffset - this.pageYOffset)
if (scrollChange > 10) {
this.unzoom()
}
}

_handleTouchStart(e) {
this.yTouchPosition = e.touches[0].clientY
}

_handleTouchMove(e) {
this.forceUpdate()
const touchChange = Math.abs(e.touches[0].clientY - this.yTouchPosition)
if (touchChange > 10) {
this.unzoom()
}
}

_handleTouchEnd(e) {
delete this.yTouchPosition
}
}

EventsWrapper.propTypes = {
children: element.isRequired,
getControlledEventFn: func
}
83 changes: 0 additions & 83 deletions src/FullWrapper.js

This file was deleted.

49 changes: 25 additions & 24 deletions src/ImageZoom.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { bool, element, func, object, number, shape, string } from 'prop-types'
import ReactDOM from 'react-dom'
import defaults from './defaults'
import { createPortal, removePortal } from './helpers'

import FullWrapper from './FullWrapper'
import EventsWrapper from './EventsWrapper'
import Overlay from './Overlay'
import ResizeWrapper from './ResizeWrapper'
import Zoom from './Zoom'

const { bool, element, func, object, number, shape, string } = PropTypes

const isControlled = isZoomed => isZoomed != null

export default class ImageZoom extends Component {
constructor(props) {
Expand Down Expand Up @@ -43,7 +43,7 @@ export default class ImageZoom extends Component {
}

componentDidMount() {
if (this.state.isZoomed || this.props.isZoomed) this.renderZoomed()
if (this.props.isZoomed) this._renderZoomed()
}

// Clean up any mess we made of the DOM before we unmount
Expand All @@ -52,9 +52,9 @@ export default class ImageZoom extends Component {
}

componentWillReceiveProps(nextProps) {
if (this.props.isZoomed == null && nextProps.isZoomed != null) {
if (!isControlled(this.props.isZoomed) && isControlled(nextProps.isZoomed)) {
throw new Error(defaults.errors.uncontrolled)
} else if (this.props.isZoomed != null && nextProps.isZoomed == null) {
} else if (isControlled(this.props.isZoomed) && !isControlled(nextProps.isZoomed)) {
throw new Error(defaults.errors.controlled)
}

Expand All @@ -72,11 +72,14 @@ export default class ImageZoom extends Component {
* based off of that.
*/
componentDidUpdate(prevProps, prevState) {
const prevIsZoomed = prevProps.isZoomed != null ? prevProps.isZoomed : prevState.isZoomed
const isZoomed = this.props.isZoomed != null ? this.props.isZoomed : this.state.isZoomed
const prevIsZoomed = isControlled(prevProps.isZoomed) ? prevProps.isZoomed : prevState.isZoomed
const isZoomed = isControlled(this.props.isZoomed) ? this.props.isZoomed : this.state.isZoomed
if (prevIsZoomed !== isZoomed) {
if (isZoomed) this._renderZoomed()
else if (this.portalInstance) this.portalInstance.unzoom()
if (isZoomed) {
this._renderZoomed()
} else if (this.portalInstance) {
this.portalInstance.unzoom({ force: true })
}
}
}

Expand All @@ -94,12 +97,9 @@ export default class ImageZoom extends Component {
}

_renderZoomed() {
/**
* If it's an uncontrolled component, include all wrap controls.
* If it's a controlled component, only wrap it in the resize controls.
*/
const innerComponent = (
<ResizeWrapper>
this.portal = createPortal('div')
this.portalInstance = ReactDOM.render(
<EventsWrapper controlledEventFn={this._getControlledEventFn()}>
<Zoom
defaultStyles={ this.props.defaultStyles }
image={ ReactDOM.findDOMNode(this.refs.image) }
Expand All @@ -109,13 +109,8 @@ export default class ImageZoom extends Component {
zoomMargin={ this.props.zoomMargin }
onUnzoom={ this._handleUnzoom }
/>
</ResizeWrapper>
)
const component = this.props.isZoomed == null
? <FullWrapper>{innerComponent}</FullWrapper>
: innerComponent
this.portal = createPortal('div')
this.portalInstance = ReactDOM.render(component, this.portal)
</EventsWrapper>
, this.portal)
}

_removeZoomed() {
Expand Down Expand Up @@ -177,6 +172,12 @@ export default class ImageZoom extends Component {
this.setState(changes, this.props.onUnzoom)
}
}

_getControlledEventFn() {
return isControlled(this.props.isZoomed)
? this.props.onUnzoom
: null
}
}

ImageZoom.propTypes = {
Expand Down
6 changes: 2 additions & 4 deletions src/Overlay.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { bool, object } from 'prop-types'
import defaults from './defaults'

const { bool, object } = PropTypes

export default class Overlay extends Component {
constructor(props) {
super(props)
Expand All @@ -27,7 +25,7 @@ export default class Overlay extends Component {
}

render() {
return <div style={ this._getStyle() }></div>
return <div style={ this._getStyle() } />
}

_getStyle() {
Expand Down
40 changes: 0 additions & 40 deletions src/ResizeWrapper.js

This file was deleted.

9 changes: 5 additions & 4 deletions src/Zoom.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { bool, func, object, number, shape, string } from 'prop-types'
import defaults from './defaults'
import { fetchImage, getMaxDimensionScale, getScale } from './helpers'

import Overlay from './Overlay'

const { bool, func, object, number, shape, string } = PropTypes

export default class Zoom extends Component {
constructor(props) {
super(props)
Expand All @@ -31,7 +29,10 @@ export default class Zoom extends Component {
const { hasAlreadyLoaded, zoomImage: { src, srcSet } } = this.props

this.setState({ hasLoaded: true })
if ((src || srcSet) && !hasAlreadyLoaded) fetchImage(this.props.zoomImage, this._handleImageLoad)

if ((src || srcSet) && !hasAlreadyLoaded) {
fetchImage(this.props.zoomImage, this._handleImageLoad)
}
}

render() {
Expand Down

0 comments on commit 1e3e9f3

Please sign in to comment.