Skip to content

Commit

Permalink
Introduce Hint Feature
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanselzer committed Oct 31, 2017
1 parent 8d646ed commit e654b21
Show file tree
Hide file tree
Showing 11 changed files with 465 additions and 23 deletions.
17 changes: 12 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

A responsive React image zoom component for touch and mouse.

Includes "hint" instructions feature.

Supports hover intnet, long-press gesture, and fade transitions.

Use for shopping sites or anywhere image detail is desired.
Expand Down Expand Up @@ -72,8 +74,8 @@ If you would like more information on responsive images, please try these resour
### Desktop and Touch
| Prop | Type | Required | Default | Description |
|-------------------------------|--------|----------|---------|------------------------------------------------------------|
| `smallImage` | Object | Yes | | Small image information. See [Small Image](#small-image) below. |
| `largeImage` | Object | Yes | | Large image information. See [Large Image](#large-image) below. |
| `smallImage` | Object | Yes | | Small image information. See [Small Image](#small-image) below.|
| `largeImage` | Object | Yes | | Large image information. See [Large Image](#large-image) below.|
| `className` | String | No | | CSS class applied to root container element. |
| `style` | Object | No | | Style applied to root container element. |
| `fadeDurationInMs` | Number | No | 300 | Milliseconds duration of magnified image fade in/fade out. |
Expand All @@ -83,21 +85,26 @@ If you would like more information on responsive images, please try these resour
| `enlargedImageContainerStyle` | Object | No | | Style applied to enlarged image container element. |
| `enlargedImageClassName` | String | No | | CSS class applied to enlarged image element. |
| `enlargedImageStyle` | Object | No | | Style applied to enlarged image element. |
| `hintComponent` |Function| No |(Provided)| Reference to a component class or functional component. A Default is provided.|
| `shouldHideHintAfterFirstActivation`| Boolean | No | true | Only show hint until the first interaction begins. |
| `isHintEnabled` | Boolean| No | false | Enable hint feature. |
| `hintTextMouse` | String | No |Hover to Zoom| Hint text for mouse. |
| `hintTextTouch` | String | No |Long-Touch to Zoom| Hint text for touch. |

### Mouse Specific
| Prop | Type | Required | Default | Description |
|-------------------------------|--------|----------|---------|------------------------------------------------------------|
| `hoverDelayInMs` | Number | No | 250 | Milliseconds to delay hover trigger. |
| `hoverOffDelayInMs` | Number | No | 150 | Milliseconds to delay hover-off trigger. |
| `lensStyle` | Object | No | | Style applied to tinted lens. |
| `lensStyle` | Object | No | | Style applied to tinted lens. |
| `enlargedImagePosition` | String | No | beside | Enlarged image position. Can be 'beside' or 'over'. |

### Touch Specific
| Prop | Type | Required | Default | Description |
|-------------------------------|--------|----------|---------|------------------------------------------------------------|
| `isActivatedOnTouch` | Boolean| No | false | Activate magnification immediately on touch. May impact scrolling.|
| `pressDuration` | Number | No | 500 | Milliseconds to delay long-press activation (long touch). |
| `pressMoveThreshold` | Number | No | 5 | Pixels of movement allowed during long-press activation. |
| `pressDuration` | Number | No | 500 | Milliseconds to delay long-press activation (long touch). |
| `pressMoveThreshold` | Number | No | 5 | Pixels of movement allowed during long-press activation. |
| `enlargedImagePosition` | String | No | over | Enlarged image position. Can be 'beside' or 'over'. |

### Small Image
Expand Down
49 changes: 49 additions & 0 deletions example/src/components/ExampleHint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';
import PropTypes from 'prop-types';


function ExampleHint({ isTouchDetected }) {
return (
<div style={{
width: '100%',
display: 'flex',
justifyContent: 'center',
position: 'absolute',
bottom: '25px'
}}>
<div style={{
display: 'flex',
alignItems: 'center',
padding: '5px 10px',
backgroundColor: '#333',
borderRadius: '10px',
opacity: '0.90'
}}>
<img
style={{
widht: '25px',
height: '25px'
}}
src="data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDQ5MC4yIDQ5MC4yIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA0OTAuMiA0OTAuMjsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHdpZHRoPSI1MTJweCIgaGVpZ2h0PSI1MTJweCI+CjxnPgoJPGc+CgkJPHBhdGggZD0iTTQxOC41LDQxOC41Yzk1LjYtOTUuNiw5NS42LTI1MS4yLDAtMzQ2LjhzLTI1MS4yLTk1LjYtMzQ2LjgsMHMtOTUuNiwyNTEuMiwwLDM0Ni44UzMyMi45LDUxNC4xLDQxOC41LDQxOC41eiBNODksODkgICAgYzg2LjEtODYuMSwyMjYuMS04Ni4xLDMxMi4yLDBzODYuMSwyMjYuMSwwLDMxMi4ycy0yMjYuMSw4Ni4xLTMxMi4yLDBTMywxNzUuMSw4OSw4OXoiIGZpbGw9IiNGRkZGRkYiLz4KCQk8cGF0aCBkPSJNMjQ1LjEsMzM2LjljMy40LDAsNi40LTEuNCw4LjctMy42YzIuMi0yLjIsMy42LTUuMywzLjYtOC43di02Ny4zaDY3LjNjMy40LDAsNi40LTEuNCw4LjctMy42YzIuMi0yLjIsMy42LTUuMywzLjYtOC43ICAgIGMwLTYuOC01LjUtMTIuMy0xMi4yLTEyLjJoLTY3LjN2LTY3LjNjMC02LjgtNS41LTEyLjMtMTIuMi0xMi4yYy02LjgsMC0xMi4zLDUuNS0xMi4yLDEyLjJ2NjcuM2gtNjcuM2MtNi44LDAtMTIuMyw1LjUtMTIuMiwxMi4yICAgIGMwLDYuOCw1LjUsMTIuMywxMi4yLDEyLjJoNjcuM3Y2Ny4zQzIzMi44LDMzMS40LDIzOC4zLDMzNi45LDI0NS4xLDMzNi45eiIgZmlsbD0iI0ZGRkZGRiIvPgoJPC9nPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+Cjwvc3ZnPgo="
/>
<span style={{
fontSize: '14px',
padding: '2px 0 0 5px',
color: 'white'
}}>
{ isTouchDetected ? 'Long-Press to Zoom' : 'Rollover to Zoom' }
</span>
</div>
</div>
);
}

ExampleHint.displayName = 'ExampleHint'

ExampleHint.propTypes = {
isTouchDetected: PropTypes.bool,
hintTextMouse: PropTypes.string,
hintTextTouch: PropTypes.string
}

export default ExampleHint;
3 changes: 2 additions & 1 deletion example/src/pages/FixedWidthSmallImage.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ export default class extends Component {
src: watchImg,
width: 300,
height: 450
}
},
isHintEnabled: true
}} />
</div>
<div className="fluid__instructions">
Expand Down
3 changes: 2 additions & 1 deletion example/src/pages/FluidWidthSmallImage.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ class App extends Component {
src: watchImg1200,
srcSet: this.srcSet,
sizes: '(min-width: 480px) 30vw, 80vw'
}
},
isHintEnabled: true
}} />
</div>
<div className="fluid__instructions">
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"product"
],
"main": "dist/ReactImageMagnify.js",
"module": "dist/es/ReactImageMagnify.js",
"files": [
"dist",
"LICENCE"
Expand Down Expand Up @@ -92,8 +93,9 @@
},
"dependencies": {
"clamp": "1.0.1",
"detect-it": "3.0.3",
"object-assign": "4.1.1",
"prop-types": "^15.6.0",
"prop-types": "15.6.0",
"react-cursor-position": "2.2.2",
"react-required-if": "1.0.1"
}
Expand Down
33 changes: 31 additions & 2 deletions src/ReactImageMagnify.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ import PropTypes from 'prop-types';
import requiredIf from 'react-required-if';
import ReactCursorPosition from 'react-cursor-position';
import objectAssign from 'object-assign';
import detectIt from 'detect-it';

import ImageLensShaded from './ImageLensShaded';
import EnlargedImage from './EnlargedImage';
import DisplayUntilActive from './hint/DisplayUntilActive';
import Hint from './hint/DefaultHint';
import ImageShape from './ImageShape';
import noop from './noop';

Expand All @@ -18,8 +21,8 @@ class ReactImageMagnify extends React.Component {
smallImageWidth: 0,
smallImageHeight: 0,
detectedEnvironment: {
isMouseDeteced: false,
isTouchDetected: false
isMouseDeteced: detectIt.hasMouse,
isTouchDetected: detectIt.hasTouch
},
isActive: false
}
Expand All @@ -37,6 +40,11 @@ class ReactImageMagnify extends React.Component {
enlargedImageClassName: PropTypes.string,
enlargedImageStyle: PropTypes.object,
fadeDurationInMs: PropTypes.number,
hintComponent: PropTypes.func,
shouldHideHintAfterFirstActivation: PropTypes.bool,
isHintEnabled: PropTypes.bool,
hintTextMouse: PropTypes.string,
hinTextTouch: PropTypes.string,
hoverDelayInMs: PropTypes.number,
hoverOffDelayInMs: PropTypes.number,
isActivatedOnTouch: PropTypes.bool,
Expand All @@ -62,6 +70,11 @@ class ReactImageMagnify extends React.Component {

static defaultProps = {
fadeDurationInMs: 300,
hintComponent: Hint,
shouldHideHintAfterFirstActivation: true,
isHintEnabled: false,
hintTextMouse: 'Hover to Zoom',
hintTextTouch: 'Long-Touch to Zoom',
hoverDelayInMs: 250,
hoverOffDelayInMs: 150
};
Expand Down Expand Up @@ -140,6 +153,11 @@ class ReactImageMagnify extends React.Component {
enlargedImageClassName,
enlargedImageStyle,
fadeDurationInMs,
hintComponent: HintComponent,
shouldHideHintAfterFirstActivation,
isHintEnabled,
hintTextMouse,
hintTextTouch,
hoverDelayInMs,
hoverOffDelayInMs,
isActivatedOnTouch,
Expand Down Expand Up @@ -249,6 +267,17 @@ class ReactImageMagnify extends React.Component {
ref: (el) => this.smallImageEl = el,
onLoad: this.onSmallImageLoad
}} />
{isHintEnabled &&
<DisplayUntilActive {...{
shouldHideAfterFirstActivation: shouldHideHintAfterFirstActivation
}}>
<HintComponent {...{
isTouchDetected,
hintTextMouse,
hintTextTouch
}}/>
</DisplayUntilActive>
}
{shouldShowLens &&
<ImageLensShaded {...{
cursorOffset,
Expand Down
48 changes: 48 additions & 0 deletions src/hint/DefaultHint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react';
import PropTypes from 'prop-types';

function DefaultHint({ isTouchDetected, hintTextMouse, hintTextTouch }) {
return (
<div style={{
width: '100%',
display: 'flex',
justifyContent: 'center',
position: 'absolute',
bottom: '25px'
}}>
<div style={{
display: 'flex',
alignItems: 'center',
padding: '5px 10px',
backgroundColor: '#333',
borderRadius: '10px',
opacity: '0.90'
}}>
<img
style={{
widht: '25px',
height: '25px'
}}
src="data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDQ5MC4yIDQ5MC4yIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA0OTAuMiA0OTAuMjsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHdpZHRoPSI1MTJweCIgaGVpZ2h0PSI1MTJweCI+CjxnPgoJPGc+CgkJPHBhdGggZD0iTTQxOC41LDQxOC41Yzk1LjYtOTUuNiw5NS42LTI1MS4yLDAtMzQ2LjhzLTI1MS4yLTk1LjYtMzQ2LjgsMHMtOTUuNiwyNTEuMiwwLDM0Ni44UzMyMi45LDUxNC4xLDQxOC41LDQxOC41eiBNODksODkgICAgYzg2LjEtODYuMSwyMjYuMS04Ni4xLDMxMi4yLDBzODYuMSwyMjYuMSwwLDMxMi4ycy0yMjYuMSw4Ni4xLTMxMi4yLDBTMywxNzUuMSw4OSw4OXoiIGZpbGw9IiNGRkZGRkYiLz4KCQk8cGF0aCBkPSJNMjQ1LjEsMzM2LjljMy40LDAsNi40LTEuNCw4LjctMy42YzIuMi0yLjIsMy42LTUuMywzLjYtOC43di02Ny4zaDY3LjNjMy40LDAsNi40LTEuNCw4LjctMy42YzIuMi0yLjIsMy42LTUuMywzLjYtOC43ICAgIGMwLTYuOC01LjUtMTIuMy0xMi4yLTEyLjJoLTY3LjN2LTY3LjNjMC02LjgtNS41LTEyLjMtMTIuMi0xMi4yYy02LjgsMC0xMi4zLDUuNS0xMi4yLDEyLjJ2NjcuM2gtNjcuM2MtNi44LDAtMTIuMyw1LjUtMTIuMiwxMi4yICAgIGMwLDYuOCw1LjUsMTIuMywxMi4yLDEyLjJoNjcuM3Y2Ny4zQzIzMi44LDMzMS40LDIzOC4zLDMzNi45LDI0NS4xLDMzNi45eiIgZmlsbD0iI0ZGRkZGRiIvPgoJPC9nPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+Cjwvc3ZnPgo="
/>
<span style={{
fontSize: '14px',
padding: '2px 0 0 5px',
color: 'white'
}}>
{ isTouchDetected ? hintTextTouch : hintTextMouse }
</span>
</div>
</div>
);
}

DefaultHint.displayName = 'DefaultHint';

DefaultHint.propTypes = {
isTouchDetected: PropTypes.bool,
hintTextMouse: PropTypes.string,
hintTextTouch: PropTypes.string
}

export default DefaultHint;
42 changes: 42 additions & 0 deletions src/hint/DisplayUntilActive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react';
import PropTypes from 'prop-types';

export default class DisplayUntilActive extends React.Component {
constructor(props) {
super(props);

this.hasShown = false;
}

static propTypes = {
children: PropTypes.element,
isActive: PropTypes.bool,
shouldHideAfterFirstActivation: PropTypes.bool
};

static defaultProps = {
shouldHideAfterFirstActivation: true
};

setHasShown() {
this.hasShown = true;
}

render () {
const {
props: {
children,
isActive,
shouldHideAfterFirstActivation
},
hasShown,
} = this;
const shouldShow = !isActive && (!hasShown || !shouldHideAfterFirstActivation);

if (isActive && !hasShown) {
this.setHasShown();
}

return shouldShow ? children : null;
}
}
Loading

0 comments on commit e654b21

Please sign in to comment.