react-native-hotspot
Advanced tools
Comparing version 0.0.2 to 0.0.3
@@ -1,71 +0,64 @@ | ||
import React, { Component } from "react"; | ||
import { | ||
Alert, | ||
Button, | ||
ScrollView, | ||
StyleSheet, | ||
Text, | ||
View | ||
} from "react-native"; | ||
import { RNHotspot, RNHotspotHelper } from "react-native-hotspot"; | ||
import React, { Component } from 'react'; | ||
import { Alert, Button, ScrollView, StyleSheet, Text, View } from 'react-native'; | ||
import { RNHotspot, RNHotspotHelper } from 'react-native-hotspot'; | ||
export default class App extends Component { | ||
constructor(props) { | ||
super(props); | ||
constructor(props) { | ||
super(props); | ||
// in your screen's constructor, use the helper with an array of onPress actions you want your hotspots to trigger | ||
this.componentRefs = RNHotspotHelper([ | ||
() => { | ||
Alert.alert(null, "This button does this thing."); | ||
}, | ||
() => { | ||
Alert.alert(null, "This button does other thing."); | ||
} | ||
]); | ||
} | ||
// in your screen's constructor, use the helper with an array of onPress actions you want your hotspots to trigger | ||
this.componentRefs = RNHotspotHelper([ | ||
() => { | ||
Alert.alert(null, 'This button does this thing.'); | ||
}, | ||
() => { | ||
Alert.alert(null, 'This button does other thing.'); | ||
}, | ||
]); | ||
} | ||
render() { | ||
return ( | ||
<ScrollView style={{ flex: 1 }}> | ||
// drop the component below the outer wrapping parent of your screen | ||
<RNHotspot componentRefs={this.componentRefs} /> | ||
<View style={styles.container}> | ||
<Text style={styles.welcome}>Welcome to React Native!</Text> | ||
<Text style={styles.instructions}>To get started, edit App.js</Text> | ||
<View style={{ marginVertical: 100 }}> | ||
// expose the refs in the same order as the array definied above | ||
<Button | ||
title="Button #1" | ||
onPress={() => {}} | ||
ref={this.componentRefs[0].ref} | ||
/> | ||
</View> | ||
<View style={{ marginVertical: 200 }}> | ||
<Button | ||
title="Button #2" | ||
onPress={() => {}} | ||
ref={this.componentRefs[1].ref} | ||
/> | ||
</View> | ||
</View> | ||
</ScrollView> | ||
); | ||
} | ||
render() { | ||
return ( | ||
<ScrollView style={{ flex: 1 }}> | ||
{/* drop the component below the outer wrapping parent of your screen */} | ||
<RNHotspot componentRefs={this.componentRefs} /> | ||
<View style={styles.container}> | ||
<Text style={styles.welcome}>Welcome to React Native!</Text> | ||
<Text style={styles.instructions}>To get started, edit App.js</Text> | ||
<View style={{ marginVertical: 100 }}> | ||
{/* expose the refs in the same order as the array definied above */} | ||
<Button | ||
title="Button #1" | ||
onPress={() => {}} | ||
ref={this.componentRefs[0].ref} | ||
/> | ||
</View> | ||
<View style={{ marginVertical: 200 }}> | ||
<Button | ||
title="Button #2" | ||
onPress={() => {}} | ||
ref={this.componentRefs[1].ref} | ||
/> | ||
</View> | ||
</View> | ||
</ScrollView> | ||
); | ||
} | ||
} | ||
const styles = StyleSheet.create({ | ||
container: { | ||
backgroundColor: "#F5FCFF" | ||
}, | ||
welcome: { | ||
marginTop: 50, | ||
fontSize: 20, | ||
textAlign: "center", | ||
marginBottom: 100 | ||
}, | ||
instructions: { | ||
textAlign: "center", | ||
color: "#333333", | ||
marginBottom: 5 | ||
} | ||
container: { | ||
backgroundColor: '#F5FCFF', | ||
}, | ||
welcome: { | ||
marginTop: 50, | ||
fontSize: 20, | ||
textAlign: 'center', | ||
marginBottom: 100, | ||
}, | ||
instructions: { | ||
textAlign: 'center', | ||
color: '#333333', | ||
marginBottom: 5, | ||
}, | ||
}); |
{ | ||
"name": "react-native-hotspot", | ||
"version": "0.0.2", | ||
"description": "A React Native component that displays hotspots over desired components to help lead your users through an onboarding flow or direct them towards new UI elements", | ||
"license": "MIT", | ||
"author": "Michael Lefkowitz <lefkowitz.michael@gmail.com>", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/lawnstarter/react-native-hotspot.git" | ||
}, | ||
"main": "src/index.js", | ||
"keywords": [ | ||
"hotspot", | ||
"tutorial", | ||
"onboarding", | ||
"react", | ||
"react-native", | ||
"react native", | ||
"expo" | ||
], | ||
"devDependencies": { | ||
"react": "16.6.1", | ||
"react-native": "0.57.5" | ||
} | ||
"name": "react-native-hotspot", | ||
"version": "0.0.3", | ||
"description": "A React Native component that displays hotspots over desired components to help lead your users through an onboarding flow or direct them towards new UI elements", | ||
"license": "MIT", | ||
"author": "Michael Lefkowitz <lefkowitz.michael@gmail.com>", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/lawnstarter/react-native-hotspot.git" | ||
}, | ||
"main": "src/index.js", | ||
"keywords": [ | ||
"hotspot", | ||
"tutorial", | ||
"onboarding", | ||
"react", | ||
"react-native", | ||
"react native", | ||
"expo" | ||
], | ||
"devDependencies": { | ||
"eslint-config-ls-react": "https://github.com/lawnstarter/eslint-config-ls-react#2.0.3", | ||
"husky": "^0.14.3", | ||
"prettier": "^1.15.2", | ||
"pretty-quick": "^1.8.0", | ||
"prop-types": "^15.6.2", | ||
"react": "16.6.1", | ||
"react-native": "0.57.5" | ||
}, | ||
"scripts": { | ||
"precommit": "pretty-quick --staged", | ||
"prepush": "echo \"---Running ESLint---\" && eslint src/**", | ||
"prettier:debug-check": "prettier --config ./.prettierrc.js --debug-check \"{src,test}/**/*.js\"", | ||
"preprettier:all": "yarn run prettier:debug-check", | ||
"prettier:all": "prettier --config ./.prettierrc.js --write \"{src,test}/**/*.js\"" | ||
} | ||
} |
@@ -11,3 +11,3 @@ # react-native-hotspot | ||
<!-- [Run example](https://snack.expo.io/@lfkwtz/react-native-hotspot) --> | ||
[Run example](https://snack.expo.io/@lfkwtz/react-native-hotspot) | ||
@@ -14,0 +14,0 @@ ## Getting Started |
@@ -1,10 +0,10 @@ | ||
import React from "react"; | ||
import React from 'react'; | ||
export function RNHotspotHelper(actions) { | ||
return actions.map(action => { | ||
return { | ||
ref: React.createRef(), | ||
onPress: action | ||
}; | ||
}); | ||
return actions.map((action) => { | ||
return { | ||
ref: React.createRef(), | ||
onPress: action, | ||
}; | ||
}); | ||
} |
@@ -1,77 +0,77 @@ | ||
import React, { PureComponent } from "react"; | ||
import { findNodeHandle } from "react-native"; | ||
import { Pulse } from "./"; | ||
import React, { PureComponent } from 'react'; | ||
import { findNodeHandle } from 'react-native'; | ||
import { Pulse } from './'; | ||
const RCTUIManager = require("NativeModules").UIManager; | ||
const RCTUIManager = require('NativeModules').UIManager; | ||
export class RNHotspot extends PureComponent { | ||
constructor(props) { | ||
super(props); | ||
constructor(props) { | ||
super(props); | ||
this.state = { | ||
hotspots: null | ||
}; | ||
this.state = { | ||
hotspots: null, | ||
}; | ||
this.onPress = this.onPress.bind(this); | ||
} | ||
this.onPress = this.onPress.bind(this); | ||
} | ||
componentDidMount() { | ||
const { componentRefs } = this.props; | ||
componentDidMount() { | ||
const { componentRefs } = this.props; | ||
const hotspots = []; | ||
const hotspots = []; | ||
setTimeout(() => { | ||
componentRefs.map(({ ref, onPress }) => { | ||
RCTUIManager.measure( | ||
findNodeHandle(ref.current), | ||
(x, y, width, height, pageX, pageY) => { | ||
hotspots.push({ | ||
width, | ||
height, | ||
pageX, | ||
pageY, | ||
onPress | ||
setTimeout(() => { | ||
componentRefs.map(({ ref, onPress }) => { | ||
RCTUIManager.measure( | ||
findNodeHandle(ref.current), | ||
(x, y, width, height, pageX, pageY) => { | ||
hotspots.push({ | ||
width, | ||
height, | ||
pageX, | ||
pageY, | ||
onPress, | ||
}); | ||
} | ||
); | ||
}); | ||
} | ||
); | ||
}); | ||
setTimeout(() => { | ||
this.setState({ | ||
hotspots | ||
setTimeout(() => { | ||
this.setState({ | ||
hotspots, | ||
}); | ||
}, 1); | ||
}); | ||
}, 1); | ||
}); | ||
} | ||
} | ||
onPress(idx) { | ||
const { hotspots } = this.state; | ||
onPress(idx) { | ||
const { hotspots } = this.state; | ||
hotspots[idx].onPress(); | ||
const newArr = hotspots.concat([]); | ||
hotspots[idx].onPress(); | ||
const newArr = hotspots.concat([]); | ||
newArr.splice(idx, 1); | ||
newArr.splice(idx, 1); | ||
this.setState({ | ||
hotspots: newArr | ||
}); | ||
} | ||
this.setState({ | ||
hotspots: newArr, | ||
}); | ||
} | ||
render() { | ||
const { hotspots } = this.state; | ||
render() { | ||
const { hotspots } = this.state; | ||
if (!hotspots) { | ||
return null; | ||
if (!hotspots) { | ||
return null; | ||
} | ||
return hotspots.map(({ width, height, pageX, pageY }, idx) => { | ||
return ( | ||
<Pulse | ||
key={idx} | ||
idx={idx} | ||
dimensions={{ width, height, pageX, pageY }} | ||
onPress={this.onPress} | ||
/> | ||
); | ||
}); | ||
} | ||
return hotspots.map(({ width, height, pageX, pageY }, idx) => { | ||
return ( | ||
<Pulse | ||
key={idx} | ||
idx={idx} | ||
dimensions={{ width, height, pageX, pageY }} | ||
onPress={this.onPress} | ||
/> | ||
); | ||
}); | ||
} | ||
} |
@@ -1,3 +0,3 @@ | ||
export * from "./helper"; | ||
export * from "./hotspot"; | ||
export * from "./pulse"; | ||
export * from './helper'; | ||
export * from './hotspot'; | ||
export * from './pulse'; |
282
src/pulse.js
@@ -1,10 +0,4 @@ | ||
import React, { PureComponent } from "react"; | ||
import { | ||
Animated, | ||
Easing, | ||
StyleSheet, | ||
TouchableWithoutFeedback, | ||
View | ||
} from "react-native"; | ||
import PropTypes from "prop-types"; | ||
import React, { PureComponent } from 'react'; | ||
import { Animated, Easing, StyleSheet, TouchableWithoutFeedback, View } from 'react-native'; | ||
import PropTypes from 'prop-types'; | ||
@@ -16,150 +10,146 @@ // https://github.com/facebook/react-native/issues/8968#issuecomment-314322836 | ||
export class Pulse extends PureComponent { | ||
static propTypes = { | ||
dimensions: PropTypes.shape({ | ||
width: PropTypes.number.isRequired, | ||
height: PropTypes.number.isRequired, | ||
pageX: PropTypes.number.isRequired, | ||
pageY: PropTypes.number.isRequired | ||
}).isRequired, | ||
onPress: PropTypes.func, | ||
offset: PropTypes.shape({ | ||
top: PropTypes.number, | ||
left: PropTypes.number | ||
}), | ||
startSize: PropTypes.number, | ||
endSize: PropTypes.number, | ||
borderWidth: PropTypes.number, | ||
borderColor: PropTypes.string, | ||
backgroundColor: PropTypes.string, | ||
duration: PropTypes.number, | ||
stagger: PropTypes.number | ||
}; | ||
static propTypes = { | ||
dimensions: PropTypes.shape({ | ||
width: PropTypes.number.isRequired, | ||
height: PropTypes.number.isRequired, | ||
pageX: PropTypes.number.isRequired, | ||
pageY: PropTypes.number.isRequired, | ||
}).isRequired, | ||
onPress: PropTypes.func, | ||
offset: PropTypes.shape({ | ||
top: PropTypes.number, | ||
left: PropTypes.number, | ||
}), | ||
startSize: PropTypes.number, | ||
endSize: PropTypes.number, | ||
borderWidth: PropTypes.number, | ||
borderColor: PropTypes.string, | ||
backgroundColor: PropTypes.string, | ||
duration: PropTypes.number, | ||
stagger: PropTypes.number, | ||
}; | ||
static defaultProps = { | ||
onPress: null, | ||
offset: { | ||
top: 0, | ||
left: 0 | ||
}, | ||
startSize: 10, | ||
endSize: 80, | ||
borderWidth: 3, | ||
borderColor: "rgba(249, 161, 27, 0.4)", | ||
backgroundColor: "rgba(249, 161, 27, 0.6)", | ||
duration: 2000, | ||
stagger: 400 | ||
}; | ||
static defaultProps = { | ||
onPress: null, | ||
offset: { | ||
top: 0, | ||
left: 0, | ||
}, | ||
startSize: 10, | ||
endSize: 80, | ||
borderWidth: 3, | ||
borderColor: 'rgba(249, 161, 27, 0.4)', | ||
backgroundColor: 'rgba(249, 161, 27, 0.6)', | ||
duration: 2000, | ||
stagger: 400, | ||
}; | ||
constructor(props) { | ||
super(props); | ||
constructor(props) { | ||
super(props); | ||
this.animations = [ | ||
new Animated.Value(0), | ||
new Animated.Value(0), | ||
new Animated.Value(0) | ||
]; | ||
} | ||
this.animations = [new Animated.Value(0), new Animated.Value(0), new Animated.Value(0)]; | ||
} | ||
componentDidMount() { | ||
this.animate(); | ||
} | ||
componentDidMount() { | ||
this.animate(); | ||
} | ||
animate() { | ||
const { duration, stagger } = this.props; | ||
const animations = this.animations.map(item => { | ||
return Animated.timing(item, { | ||
toValue: 1, | ||
duration, | ||
easing: Easing.in | ||
}); | ||
}); | ||
Animated.loop(Animated.stagger(stagger, animations)).start(); | ||
} | ||
animate() { | ||
const { duration, stagger } = this.props; | ||
const animations = this.animations.map((item) => { | ||
return Animated.timing(item, { | ||
toValue: 1, | ||
duration, | ||
easing: Easing.in, | ||
}); | ||
}); | ||
Animated.loop(Animated.stagger(stagger, animations)).start(); | ||
} | ||
render() { | ||
const { | ||
dimensions, | ||
onPress, | ||
offset, | ||
startSize, | ||
endSize, | ||
borderWidth, | ||
borderColor, | ||
backgroundColor | ||
} = this.props; | ||
render() { | ||
const { | ||
dimensions, | ||
onPress, | ||
offset, | ||
startSize, | ||
endSize, | ||
borderWidth, | ||
borderColor, | ||
backgroundColor, | ||
} = this.props; | ||
const animations = this.animations.map((animation, idx) => { | ||
return ( | ||
<TouchableWithoutFeedback key={idx} onPress={() => {}}> | ||
<View | ||
style={[ | ||
defaultStyles.overlay, | ||
{ | ||
width: dimensions.width, | ||
height: dimensions.height, | ||
top: dimensions.pageY + offset.top, | ||
left: dimensions.pageX + offset.left, | ||
backgroundColor: "transparent", | ||
zIndex: 100 | ||
} | ||
]} | ||
> | ||
<TouchableWithoutFeedback | ||
onPress={() => { | ||
onPress(this.props.idx); | ||
}} | ||
testID="tappableArea" | ||
> | ||
<View | ||
style={[ | ||
defaultStyles.overlay, | ||
{ | ||
width: endSize, | ||
height: endSize, | ||
top: dimensions.height / 2 - endSize / 2 + offset.top, | ||
left: dimensions.width / 2 - endSize / 2 + offset.left, | ||
zIndex: 101 | ||
} | ||
]} | ||
testID="pulseOverlay" | ||
> | ||
<Animated.View | ||
style={[ | ||
{ | ||
borderWidth: borderWidth * StyleSheet.hairlineWidth, | ||
borderColor, | ||
backgroundColor, | ||
width: animation.interpolate({ | ||
inputRange: [0, 1], | ||
outputRange: [startSize, endSize] | ||
}), | ||
height: animation.interpolate({ | ||
inputRange: [0, 1], | ||
outputRange: [startSize, endSize] | ||
}), | ||
borderRadius: endSize / 2, | ||
opacity: animation.interpolate({ | ||
inputRange: [0, 1], | ||
outputRange: [1, 0] | ||
}) | ||
} | ||
]} | ||
/> | ||
</View> | ||
</TouchableWithoutFeedback> | ||
</View> | ||
</TouchableWithoutFeedback> | ||
); | ||
}); | ||
const animations = this.animations.map((animation, idx) => { | ||
return ( | ||
<TouchableWithoutFeedback key={idx} onPress={() => {}}> | ||
<View | ||
style={[ | ||
defaultStyles.overlay, | ||
{ | ||
width: dimensions.width, | ||
height: dimensions.height, | ||
top: dimensions.pageY + offset.top, | ||
left: dimensions.pageX + offset.left, | ||
backgroundColor: 'transparent', | ||
zIndex: 100, | ||
}, | ||
]} | ||
> | ||
<TouchableWithoutFeedback | ||
onPress={() => { | ||
onPress(this.props.idx); | ||
}} | ||
testID="tappableArea" | ||
> | ||
<View | ||
style={[ | ||
defaultStyles.overlay, | ||
{ | ||
width: endSize, | ||
height: endSize, | ||
top: dimensions.height / 2 - endSize / 2 + offset.top, | ||
left: dimensions.width / 2 - endSize / 2 + offset.left, | ||
zIndex: 101, | ||
}, | ||
]} | ||
testID="pulseOverlay" | ||
> | ||
<Animated.View | ||
style={[ | ||
{ | ||
borderWidth: borderWidth * StyleSheet.hairlineWidth, | ||
borderColor, | ||
backgroundColor, | ||
width: animation.interpolate({ | ||
inputRange: [0, 1], | ||
outputRange: [startSize, endSize], | ||
}), | ||
height: animation.interpolate({ | ||
inputRange: [0, 1], | ||
outputRange: [startSize, endSize], | ||
}), | ||
borderRadius: endSize / 2, | ||
opacity: animation.interpolate({ | ||
inputRange: [0, 1], | ||
outputRange: [1, 0], | ||
}), | ||
}, | ||
]} | ||
/> | ||
</View> | ||
</TouchableWithoutFeedback> | ||
</View> | ||
</TouchableWithoutFeedback> | ||
); | ||
}); | ||
return animations; | ||
} | ||
return animations; | ||
} | ||
} | ||
const defaultStyles = StyleSheet.create({ | ||
overlay: { | ||
justifyContent: "center", | ||
alignItems: "center", | ||
position: "absolute" | ||
} | ||
overlay: { | ||
justifyContent: 'center', | ||
alignItems: 'center', | ||
position: 'absolute', | ||
}, | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
225654
11
304
0
7