Skip to content

Commit 2929c75

Browse files
author
jsdario
committed
🎉 initial commit
0 parents  commit 2929c75

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+9541
-0
lines changed

.gitignore

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# OSX
2+
#
3+
.DS_Store
4+
5+
# Xcode
6+
#
7+
build/
8+
*.pbxuser
9+
!default.pbxuser
10+
*.mode1v3
11+
!default.mode1v3
12+
*.mode2v3
13+
!default.mode2v3
14+
*.perspectivev3
15+
!default.perspectivev3
16+
xcuserdata
17+
*.xccheckout
18+
*.moved-aside
19+
DerivedData
20+
*.hmap
21+
*.ipa
22+
*.xcuserstate
23+
project.xcworkspace
24+
25+
# Android/IntelliJ
26+
#
27+
build/
28+
.idea
29+
.gradle
30+
local.properties
31+
*.iml
32+
33+
# node.js
34+
#
35+
node_modules/
36+
npm-debug.log
37+
yarn-error.log
38+
39+
# BUCK
40+
buck-out/
41+
\.buckd/
42+
*.keystore
43+
44+
# fastlane
45+
#
46+
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
47+
# screenshots whenever they are needed.
48+
# For more information about the recommended setup visit:
49+
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
50+
51+
fastlane/report.xml
52+
fastlane/Preview.html
53+
fastlane/screenshots

.npmignore

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# OSX
2+
#
3+
.DS_Store
4+
5+
# Xcode
6+
#
7+
build/
8+
*.pbxuser
9+
!default.pbxuser
10+
*.mode1v3
11+
!default.mode1v3
12+
*.mode2v3
13+
!default.mode2v3
14+
*.perspectivev3
15+
!default.perspectivev3
16+
xcuserdata
17+
*.xccheckout
18+
*.moved-aside
19+
DerivedData
20+
*.hmap
21+
*.ipa
22+
*.xcuserstate
23+
project.xcworkspace
24+
25+
# Android/IntelliJ
26+
#
27+
build/
28+
.idea
29+
.gradle
30+
local.properties
31+
*.iml
32+
33+
# node.js
34+
#
35+
node_modules/
36+
npm-debug.log
37+
yarn-error.log
38+
39+
# BUCK
40+
buck-out/
41+
\.buckd/
42+
*.keystore
43+
44+
# fastlane
45+
#
46+
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
47+
# screenshots whenever they are needed.
48+
# For more information about the recommended setup visit:
49+
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
50+
51+
fastlane/report.xml
52+
fastlane/Preview.html
53+
fastlane/screenshots
54+
55+
56+
### Also ignore:
57+
example
58+
.vscode

Diagnose.js

+193
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import React, { Component } from 'react'
2+
import {
3+
Dimensions,
4+
PanResponder,
5+
StyleSheet,
6+
View,
7+
} from 'react-native'
8+
import { throttle } from 'lodash'
9+
10+
const GREY_LIGHT = '#eeeeee'
11+
12+
export class Dial extends Component {
13+
static defaultProps = {
14+
initialRadius: 1,
15+
initialAngle: 0,
16+
precision: 0,
17+
}
18+
19+
constructor(props) {
20+
super(props)
21+
this.state = {
22+
startingAngle: this.props.initialAngle,
23+
startingRadius: this.props.initialRadius,
24+
releaseAngle: this.props.initialAngle,
25+
releaseRadius: this.props.initialRadius,
26+
angle: this.props.initialAngle,
27+
radius: this.props.initialRadius,
28+
}
29+
this.offset = { x: 0, y: 0 }
30+
this.updateState = throttle(this.updateState.bind(this), 16)
31+
}
32+
33+
componentWillMount () {
34+
this._panResponder = PanResponder.create({
35+
onStartShouldSetPanResponder: (e, gestureState) => true,
36+
onStartShouldSetPanResponderCapture: (e, gestureState) => {
37+
this.measureOffset() // measure again
38+
const { deg, radius } = this.calcAngle(e.nativeEvent)
39+
this.setState({ startingAngle: deg, startingRadius: radius })
40+
return true
41+
},
42+
onMoveShouldSetPanResponder: (e, g) => true,
43+
onMoveShouldSetPanResponderCapture: (e, gestureState) => true,
44+
onPanResponderGrant: (e, gestureState) => true,
45+
onPanResponderMove: (e, gestureState) => {
46+
this.updateAngle(gestureState)
47+
},
48+
onPanResponderRelease: (e, gestureState) => {
49+
const {
50+
angle,
51+
radius,
52+
releaseAngle,
53+
releaseRadius,
54+
} = this.state
55+
56+
if (angle !== releaseAngle || radius !== releaseRadius) {
57+
this.setState({
58+
releaseAngle: angle,
59+
releaseRadius: radius,
60+
})
61+
}
62+
},
63+
})
64+
}
65+
66+
onLayout (nativeEvent) {
67+
/*
68+
* Multiple measures to avoid the gap between animated
69+
* and not animated views
70+
*/
71+
this.measureOffset()
72+
setTimeout(() => this.measureOffset(), 200)
73+
}
74+
75+
measureOffset () {
76+
/*
77+
* const {x, y, width, height} = nativeEvent.layout
78+
* onlayout values are different than measureInWindow
79+
* x and y are the distances to its previous element
80+
* but in measureInWindow they are relative to the window
81+
*/
82+
const { width: screenWidth } = Dimensions.get('window')
83+
84+
this.self.measureInWindow((x, y, width, height) => {
85+
this.offset = {
86+
x: x % screenWidth + width / 2,
87+
y: y + height / 2,
88+
}
89+
this.radius = width / 2
90+
})
91+
}
92+
93+
updateAngle (gestureState) {
94+
let { deg, radius } = this.calcAngle(gestureState)
95+
if (deg < 0) deg += 360
96+
if (Math.abs(this.state.angle - deg) > this.props.precision) {
97+
this.updateState({ deg, radius })
98+
}
99+
}
100+
101+
calcAngle (gestureState) {
102+
const { pageX, pageY, moveX, moveY } = gestureState
103+
const [x, y] = [pageX || moveX, pageY || moveY]
104+
const [dx, dy] = [x - this.offset.x, y - this.offset.y]
105+
return {
106+
deg: Math.atan2(dy, dx) * 180 / Math.PI + 120,
107+
radius: Math.sqrt(dy * dy + dx * dx) / this.radius, // pitagoras r^2 = x^2 + y^2 normalizado
108+
}
109+
}
110+
111+
updateState ({ deg, radius = this.state.radius }) {
112+
radius = this.state.releaseRadius + radius - this.state.startingRadius
113+
if (radius < this.props.radiusMin) radius = this.props.radiusMin
114+
else if (radius > this.props.radiusMax) radius = this.props.radiusMax
115+
116+
const angle = deg + this.state.releaseAngle - this.state.startingAngle
117+
if (deg < 0) deg += 360
118+
119+
if (angle !== this.state.angle || radius !== this.state.radius) {
120+
this.setState({ angle, radius })
121+
if (this.props.onValueChange) this.props.onValueChange(angle, radius)
122+
}
123+
}
124+
125+
render () {
126+
const rotate = this.props.fixed ? '0deg' : `${this.state.angle}deg`
127+
const scale = this.props.elastic ? this.state.radius : 1
128+
129+
return (
130+
<View
131+
onLayout={(nativeEvent) => this.onLayout(nativeEvent)}
132+
ref={(node) => { this.self = node }}
133+
style={[styles.coverResponder, this.props.responderStyle]}
134+
{...this._panResponder.panHandlers}
135+
>
136+
{this.props.children
137+
? <View style={[this.props.wrapperStyle, { transform: [{ rotate }, { scale }] }]}>
138+
{this.props.children}
139+
</View>
140+
: <DefaultDial style={this.props.style} rotate={rotate} scale={scale} />
141+
}
142+
</View>
143+
)
144+
}
145+
}
146+
147+
export const DefaultDial = ({ style = {}, rotate = '0rad', scale = 1 }) => (
148+
<View
149+
style={[styles.dial, style, {
150+
transform: [
151+
{ rotate }, { scale },
152+
]
153+
}]} >
154+
<View style={styles.innerDialDecorator}>
155+
<View style={styles.pointer} />
156+
</View>
157+
</View>
158+
)
159+
160+
const styles = StyleSheet.create({
161+
coverResponder: {
162+
padding: 20, // needs a minimum
163+
},
164+
dial: {
165+
width: 120,
166+
height: 120,
167+
backgroundColor: 'white',
168+
borderRadius: 60,
169+
elevation: 5,
170+
shadowColor: GREY_LIGHT,
171+
shadowOffset: { width: 1, height: 2 },
172+
shadowOpacity: 0.8,
173+
shadowRadius: 1,
174+
},
175+
innerDialDecorator: {
176+
top: 10,
177+
left: 10,
178+
width: 100,
179+
height: 100,
180+
borderRadius: 50,
181+
backgroundColor: 'white',
182+
elevation: 3,
183+
},
184+
pointer: {
185+
top: 20,
186+
left: 20,
187+
position: 'absolute',
188+
width: 10,
189+
height: 10,
190+
backgroundColor: 'rgb(221,223,226)',
191+
borderRadius: 5,
192+
},
193+
})

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2016 Netbeast
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# react-native-dial
2+
![npm version](https://badge.fury.io/js/react-native-dial.svg)
3+
4+
<a href="https://getyeti.co" target="_blank">
5+
<img alt="works with yeti" src="works-with-yeti.png" width="100" />
6+
</a>
7+
8+
> This package powers [Yeti Smart Home](https://getyeti.co) and is used in production.
9+
10+
A react native reusable and efficient dial knob element.
11+
12+
```javascript
13+
import { Dial } from 'react-native-dial'
14+
// ...
15+
<Dial
16+
initialRadius={brightness * DIF_RADIUS / 100 + MIN_RADIUS}
17+
radiusMax={MAX_RADIUS}
18+
radiusMin={MIN_RADIUS}
19+
onPress={() => this.toggle()}
20+
responderStyle={styles.responderStyle}
21+
wrapperStyle={styles.wheelWrapper}
22+
onValueChange={(a, r) => this.changeBrightness(r)} />
23+
```
24+
25+
<img alt="demo screenshot" src="screenshot.png" width="350" />
26+
27+
28+
Some properties:
29+
```
30+
<Dial
31+
fixed // disallows angle updates
32+
elastic // allows scaling the element
33+
initialAngle={Number}
34+
initialRadius={Number}
35+
radiusMax={Number}
36+
radiusMin={Number}
37+
responderStyle={ReactNative.Styles}
38+
wrapperStyle={ReactNative.Styles}
39+
>
40+
{/*
41+
Optionally you can pass children so it renders a different component of your choice as a Dial,
42+
that can change in scale and angle
43+
*/}
44+
<YourCustomDial />
45+
</Dial>
46+
47+
```
48+
49+
More documentation is incoming, in the meanwhile please read the source code. It is a single file!
50+
PRs and issues are more than welcome.
51+
52+
Follow us in Github or https://twitter.com/netbeast_co.

example/.babelrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"presets": ["react-native"]
3+
}

example/.buckconfig

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
[android]
3+
target = Google Inc.:Google APIs:23
4+
5+
[maven_repositories]
6+
central = https://repo1.maven.org/maven2

0 commit comments

Comments
 (0)