Removed the boilerplate's counter, started a drawer menu, added a toolbar to access the menu, created the calculator form and state.master
parent
ff17b2a0d3
commit
7f6fc32d74
28 changed files with 607 additions and 94 deletions
@ -1,3 +1,6 @@ |
||||
rootProject.name = 'Counter' |
||||
|
||||
include ':app' |
||||
|
||||
include ':react-native-vector-icons' |
||||
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android') |
||||
|
@ -1,2 +0,0 @@ |
||||
export const INCREMENT = 'INCREMENT'; |
||||
export const DECREMENT = 'DECREMENT'; |
@ -1,13 +0,0 @@ |
||||
import * as types from './actionTypes'; |
||||
|
||||
export function increment() { |
||||
return { |
||||
type: types.INCREMENT |
||||
}; |
||||
} |
||||
|
||||
export function decrement() { |
||||
return { |
||||
type: types.DECREMENT |
||||
}; |
||||
} |
@ -1,36 +0,0 @@ |
||||
import React, {Component} from 'react'; |
||||
import {StyleSheet, View, Text, TouchableOpacity} from 'react-native'; |
||||
|
||||
const styles = StyleSheet.create({ |
||||
button: { |
||||
width: 100, |
||||
height: 30, |
||||
padding: 10, |
||||
backgroundColor: 'lightgray', |
||||
alignItems: 'center', |
||||
justifyContent: 'center', |
||||
margin: 3 |
||||
} |
||||
}); |
||||
|
||||
export default class Counter extends Component { |
||||
constructor(props) { |
||||
super(props); |
||||
} |
||||
|
||||
render() { |
||||
const { counter, increment, decrement } = this.props; |
||||
|
||||
return ( |
||||
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center' }}> |
||||
<Text>{counter}</Text> |
||||
<TouchableOpacity onPress={increment} style={styles.button}> |
||||
<Text>up</Text> |
||||
</TouchableOpacity> |
||||
<TouchableOpacity onPress={decrement} style={styles.button}> |
||||
<Text>down</Text> |
||||
</TouchableOpacity> |
||||
</View> |
||||
); |
||||
} |
||||
} |
@ -1,22 +0,0 @@ |
||||
import * as types from '../actions/actionTypes'; |
||||
|
||||
const initialState = { |
||||
count: 0 |
||||
}; |
||||
|
||||
export default function counter(state = initialState, action = {}) { |
||||
switch (action.type) { |
||||
case types.INCREMENT: |
||||
return { |
||||
...state, |
||||
count: state.count + 1 |
||||
}; |
||||
case types.DECREMENT: |
||||
return { |
||||
...state, |
||||
count: state.count - 1 |
||||
}; |
||||
default: |
||||
return state; |
||||
} |
||||
} |
@ -1,5 +0,0 @@ |
||||
import counter from './counter'; |
||||
|
||||
export { |
||||
counter |
||||
}; |
@ -1,6 +1,6 @@ |
||||
'use strict'; |
||||
|
||||
import React, { AppRegistry } from 'react-native'; |
||||
import App from './app/containers/app'; |
||||
import App from './src/containers/app'; |
||||
|
||||
AppRegistry.registerComponent('Counter', () => App); |
||||
|
@ -1,6 +1,6 @@ |
||||
'use strict'; |
||||
|
||||
import React, { AppRegistry } from 'react-native'; |
||||
import App from './app/containers/app'; |
||||
import App from './src/containers/app'; |
||||
|
||||
AppRegistry.registerComponent('Counter', () => App); |
||||
|
@ -0,0 +1,7 @@ |
||||
export const UPDATEVALUE = 'UPDATEVALUE'; |
||||
export const UPDATEMULT = 'UPDATEMULT'; |
||||
export const MENUOPEN = 'MENUOPEN'; |
||||
export const MENUCLOSE = 'MENUCLOSE'; |
||||
export const MAIN = 'MAIN'; |
||||
export const HELP = 'HELP'; |
||||
export const SETREF = 'SETREF'; |
@ -0,0 +1,19 @@ |
||||
import * as types from './actionTypes'; |
||||
|
||||
export function updatevalue(form, index, value) { |
||||
return { |
||||
type: types.UPDATEVALUE, |
||||
form, |
||||
index, |
||||
value, |
||||
}; |
||||
} |
||||
|
||||
export function updatemult(form, index, mult) { |
||||
return { |
||||
type: types.UPDATEMULT, |
||||
form, |
||||
index, |
||||
mult, |
||||
}; |
||||
} |
@ -0,0 +1,25 @@ |
||||
import * as types from './actionTypes'; |
||||
|
||||
export function menuopen() { |
||||
return { |
||||
type: types.MENUOPEN |
||||
}; |
||||
} |
||||
|
||||
export function menuclose() { |
||||
return { |
||||
type: types.MENUCLOSE |
||||
}; |
||||
} |
||||
|
||||
export function main() { |
||||
return { |
||||
type: types.MAIN |
||||
}; |
||||
} |
||||
|
||||
export function help() { |
||||
return { |
||||
type: types.HELP |
||||
}; |
||||
} |
@ -0,0 +1,67 @@ |
||||
import React, {Component} from 'react'; |
||||
import {ScrollView, StyleSheet, Text, TouchableOpacity, View} from 'react-native'; |
||||
|
||||
import Resistor from './resistor'; |
||||
import Section from './section'; |
||||
|
||||
const styles = StyleSheet.create({ |
||||
button: { |
||||
width: 100, |
||||
height: 30, |
||||
padding: 10, |
||||
backgroundColor: 'lightgray', |
||||
alignItems: 'center', |
||||
justifyContent: 'center', |
||||
margin: 3 |
||||
} |
||||
}); |
||||
|
||||
export default class Calc extends Component { |
||||
constructor(props) { |
||||
super(props); |
||||
} |
||||
|
||||
render() { |
||||
const {state} = this.props; |
||||
|
||||
const allValid = state.have.every((x) => x.valid); |
||||
const nonEmpty = state.have.some((x) => x.value); |
||||
|
||||
const reciprocalSum = state.have |
||||
.filter((x) => x.value) |
||||
.reduce((a,b) => a + 1.0/(b.value * b.mult), 0.0); |
||||
|
||||
return ( |
||||
<ScrollView> |
||||
<View style={{padding: 20}}> |
||||
<Section> |
||||
<Text> |
||||
Enter the exact resistance target: |
||||
</Text> |
||||
<Resistor form="target" /> |
||||
</Section> |
||||
<Section> |
||||
<Text> |
||||
Overall parallel resistor value: |
||||
{allValid && nonEmpty ? ' ' + 1/reciprocalSum : ' ???'} |
||||
</Text> |
||||
<Text> |
||||
Error between this and the target: {0}% |
||||
</Text> |
||||
<Text> |
||||
Next recommended resistor value: {0} |
||||
</Text> |
||||
</Section> |
||||
<Section> |
||||
<Text> |
||||
Enter the resistors you have in parallel: |
||||
</Text> |
||||
{state.have.map((x,n) =>
|
||||
<Resistor form="have" index={n} key={n} /> |
||||
)} |
||||
</Section> |
||||
</View> |
||||
</ScrollView> |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,24 @@ |
||||
import React, {Component} from 'react'; |
||||
import {StyleSheet, View, Text, TouchableOpacity} from 'react-native'; |
||||
|
||||
const styles = StyleSheet.create({ |
||||
}); |
||||
|
||||
export default class Help extends Component { |
||||
constructor(props) { |
||||
super(props); |
||||
} |
||||
|
||||
render() { |
||||
//const { counter } = this.props;
|
||||
|
||||
return ( |
||||
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center' }}> |
||||
<Text>This is a sample counter app.</Text> |
||||
<Text>Press Increase to add 1 to the value.</Text> |
||||
<Text>Press Decrease to subtract 1 from the value.</Text> |
||||
</View> |
||||
); |
||||
} |
||||
} |
||||
|
@ -0,0 +1,29 @@ |
||||
import React, {Component} from 'react'; |
||||
import {StyleSheet, View, Text, TouchableOpacity} from 'react-native'; |
||||
|
||||
const styles = StyleSheet.create({ |
||||
main: { |
||||
backgroundColor: '#ffffff', |
||||
flex: 1, |
||||
alignItems: 'center', |
||||
justifyContent: 'center', |
||||
}, |
||||
}); |
||||
|
||||
export default class Menu extends Component { |
||||
constructor(props) { |
||||
super(props); |
||||
} |
||||
|
||||
render() { |
||||
const { main, help } = this.props; |
||||
|
||||
return ( |
||||
<View style={styles.main}> |
||||
<Text onPress={main}>Calculator</Text> |
||||
<Text onPress={help}>Help</Text> |
||||
<Text>Link 4</Text> |
||||
</View> |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,124 @@ |
||||
import React, {Component} from 'react'; |
||||
import {bindActionCreators} from 'redux'; |
||||
import {connect} from 'react-redux'; |
||||
import { |
||||
Alert, |
||||
Picker, |
||||
StyleSheet, |
||||
View, |
||||
Text, |
||||
TextInput, |
||||
TouchableOpacity, |
||||
} from 'react-native'; |
||||
|
||||
import * as calcActions from '../actions/calcActions'; |
||||
|
||||
const styles = { |
||||
resistorinput: { |
||||
flex: 1, |
||||
height: 40, |
||||
maxWidth: 80, |
||||
borderWidth: 1, |
||||
textAlign: 'right', |
||||
}, |
||||
clearbutton: { |
||||
height: 30, |
||||
padding: 10, |
||||
backgroundColor: 'lightgray', |
||||
alignItems: 'center', |
||||
justifyContent: 'center', |
||||
margin: 3, |
||||
}, |
||||
}; |
||||
|
||||
class Resistor extends Component { |
||||
constructor(props) { |
||||
super(props); |
||||
} |
||||
|
||||
render() { |
||||
const {data, actions} = this.props; |
||||
|
||||
// Curry the update functions to make them simpler
|
||||
const updatevalue = (x) => actions.updatevalue( |
||||
this.props.form, this.props.index, x |
||||
); |
||||
const updatemult = (x) => actions.updatemult( |
||||
this.props.form, this.props.index, x |
||||
); |
||||
|
||||
const clear = () => { |
||||
updatevalue(''); |
||||
updatemult('1'); |
||||
} |
||||
|
||||
console.log(this); |
||||
|
||||
return ( |
||||
<View style={{flex: 1, flexDirection: 'row', justifyContent: 'center', margin: 3}}> |
||||
<TextInput |
||||
value={data.value} |
||||
onChangeText={(x) => updatevalue(x)} |
||||
style={Object.assign({}, styles.resistorinput, { |
||||
borderColor: data.valid ? 'grey' : 'red', |
||||
})} |
||||
underlineColorAndroid={'transparent'} |
||||
autoCorrect={false} |
||||
keyboardType={'numeric'} |
||||
/> |
||||
<Picker
|
||||
ref={(x) => this._picker = x} |
||||
selectedValue={data.mult} |
||||
onValueChange={(x) => updatemult(x)} |
||||
style={{height: 40, width: 100}} |
||||
mode={'dropdown'} |
||||
> |
||||
<Picker.Item label=" MΩ" value="1000000" /> |
||||
<Picker.Item label=" kΩ" value="1000" /> |
||||
<Picker.Item label=" Ω" value="1" /> |
||||
<Picker.Item label=" mΩ" value="0.001" /> |
||||
</Picker> |
||||
<TouchableOpacity onPress={() => {
|
||||
console.log(this); |
||||
Alert.alert( |
||||
'Clear resistor value?', |
||||
data.value +
|
||||
// I created this kludge for fun (so don't hate on me):
|
||||
this._picker.props.children |
||||
.map((x) => x.props) |
||||
.filter((x) => x.value == data.mult)[0] |
||||
.label, |
||||
[ |
||||
{text: 'CANCEL'}, |
||||
{text: 'OK', onPress: clear}, |
||||
] |
||||
) |
||||
}} style={styles.clearbutton} activeOpacity={1} |
||||
> |
||||
<Text>Clear</Text> |
||||
</TouchableOpacity> |
||||
</View> |
||||
); |
||||
} |
||||
} |
||||
|
||||
//Redux stuff
|
||||
function mapStateToProps(state, ownProps) { |
||||
if (ownProps.form == "target") { |
||||
return { |
||||
data: state.calc.target, |
||||
}; |
||||
} else if (ownProps.form == "have") { |
||||
return { |
||||
data: state.calc.have[ownProps.index], |
||||
}; |
||||
} else { |
||||
return {}; |
||||
} |
||||
} |
||||
|
||||
export default connect(mapStateToProps, |
||||
(dispatch) => ({ |
||||
actions: bindActionCreators(calcActions, dispatch) |
||||
}) |
||||
)(Resistor); |
@ -0,0 +1,18 @@ |
||||
import React, {Component} from 'react'; |
||||
import {View} from 'react-native'; |
||||
|
||||
export default class Section extends Component { |
||||
constructor(props) { |
||||
super(props); |
||||
} |
||||
|
||||
render() { |
||||
return ( |
||||
<View style={{marginBottom: 15}}> |
||||
{this.props.children} |
||||
</View> |
||||
); |
||||
} |
||||
} |
||||
|
||||
|
@ -0,0 +1,24 @@ |
||||
'use strict'; |
||||
|
||||
import React, {Component} from 'react'; |
||||
import Calc from '../components/calc'; |
||||
import {connect} from 'react-redux'; |
||||
|
||||
class CalcApp extends Component { |
||||
constructor(props) { |
||||
super(props); |
||||
} |
||||
|
||||
render() { |
||||
const {state} = this.props; |
||||
console.log(this); |
||||
|
||||
return ( |
||||
<Calc state={state}/> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export default connect(state => ({ |
||||
state: state.calc |
||||
}))(CalcApp); |
@ -0,0 +1,30 @@ |
||||
'use strict'; |
||||
|
||||
import React, {Component} from 'react'; |
||||
import {bindActionCreators} from 'redux'; |
||||
import Menu from '../components/menu'; |
||||
import * as menuActions from '../actions/menuActions'; |
||||
import { connect } from 'react-redux'; |
||||
|
||||
class MenuApp extends Component { |
||||
constructor(props) { |
||||
super(props); |
||||
} |
||||
|
||||
render() { |
||||
const { state, actions } = this.props; |
||||
return ( |
||||
<Menu {...actions} /> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export default connect(state => ({ |
||||
state: state |
||||
}), |
||||
(dispatch) => ({ |
||||
actions: bindActionCreators(menuActions, dispatch) |
||||
}) |
||||
)(MenuApp); |
||||
|
||||
|
@ -0,0 +1,70 @@ |
||||
'use strict';
|
||||
import React, {Component} from 'react'; |
||||
import {View, StyleSheet} from 'react-native'; |
||||
import {bindActionCreators} from 'redux'; |
||||
import {connect} from 'react-redux'; |
||||
import Ionicons from 'react-native-vector-icons/Ionicons'; |
||||
import Drawer from 'react-native-drawer'; |
||||
|
||||
import * as menuActions from '../actions/menuActions'; |
||||
import MenuApp from '../containers/menuApp'; |
||||
|
||||
class Nav extends Component { |
||||
constructor(props) { |
||||
super(props); |
||||
} |
||||
|
||||
render() { |
||||
const { state, actions } = this.props; |
||||
|
||||
const drawerStyles = { |
||||
drawer: { shadowColor: '#000000', shadowOpacity: 0.8, shadowRadius: 3}, |
||||
main: {paddingLeft: 3}, |
||||
} |
||||
const styles = StyleSheet.create({ |
||||
toolbar: { |
||||
backgroundColor: '#e9eaed', |
||||
height: 56, |
||||
}, |
||||
});
|
||||
|
||||
return ( |
||||
<Drawer |
||||
open={state.isOpen} |
||||
type="overlay" |
||||
content={<MenuApp />} |
||||
tapToClose={true} |
||||
openDrawerOffset={0.3} |
||||
captureGestures={false} |
||||
panCloseMask={0.3} |
||||
closedDrawerOffset={-3} |
||||
styles={drawerStyles} |
||||
tweenHandler={(ratio) => ({ |
||||
main: { opacity: 1, }, |
||||
mainOverlay: { opacity: ratio/2, backgroundColor: 'black', }, |
||||
})} |
||||
> |
||||
<Ionicons.ToolbarAndroid |
||||
navIconName={"md-menu"} |
||||
onIconClicked={actions.menuopen} |
||||
style={styles.toolbar} |
||||
subtitle={state.subtitle} |
||||
title="Exact Resistor Calculator" |
||||
/> |
||||
<View style={{flex: 1}}> |
||||
{state.page} |
||||
</View> |
||||
</Drawer> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export default connect(state => ({ |
||||
state: state.menu |
||||
}), |
||||
(dispatch) => ({ |
||||
actions: bindActionCreators(menuActions, dispatch) |
||||
}) |
||||
)(Nav); |
||||
|
||||
|
@ -0,0 +1,97 @@ |
||||
import * as types from '../actions/actionTypes'; |
||||
|
||||
const initialState = { |
||||
target: { |
||||
value: '', |
||||
mult: '1', |
||||
valid: true, |
||||
}, |
||||
have: [ |
||||
{ |
||||
value: '', |
||||
mult: '1', |
||||
valid: true, |
||||
}, |
||||
{ |
||||
value: '', |
||||
mult: '1', |
||||
valid: true, |
||||
}, |
||||
{ |
||||
value: '', |
||||
mult: '1', |
||||
valid: true, |
||||
}, |
||||
], |
||||
}; |
||||
|
||||
function isPosReal(n) { |
||||
return n == '' || // Allowed to be blank
|
||||
!isNaN(parseFloat(n)) && // Has to be real
|
||||
isFinite(n) // Can't be infinite
|
||||
&& parseFloat(n) > 0; // Must be positive, non-zero
|
||||
} |
||||
|
||||
export default function calc(state = initialState, action = {}) { |
||||
const {type, value, form, index, mult} = action; |
||||
|
||||
switch (type) { |
||||
case types.UPDATEVALUE: |
||||
const valid = isPosReal(value); |
||||
|
||||
if (form == "target") { |
||||
return { |
||||
...state, |
||||
target: { |
||||
...state.target, |
||||
value: value, |
||||
valid: valid, |
||||
} |
||||
}; |
||||
} else if (form == "have") { |
||||
const newState = { |
||||
...state, |
||||
have: state.have.map((x,n) => |
||||
n == index ? { |
||||
...x, |
||||
value: value, |
||||
valid: valid, |
||||
} : x |
||||
), |
||||
}; |
||||
|
||||
// If all values are full, add another resistor
|
||||
if (newState.have.every((x) => x.value)) { |
||||
newState.have.push({ |
||||
value: '', |
||||
mult: '1', |
||||
valid: true, |
||||
}); |
||||
} |
||||
|
||||
return newState; |
||||
} |
||||
case types.UPDATEMULT: |
||||
if (form == "target") { |
||||
return { |
||||
...state, |
||||
target: { |
||||
...state.target, |
||||
mult: mult, |
||||
} |
||||
}; |
||||
} else if (form == "have") { |
||||
return { |
||||
...state, |
||||
have: state.have.map((x,n) => |
||||
n == index ? { |
||||
...x, |
||||
mult: mult, |
||||
} : x |
||||
), |
||||
}; |
||||
} |
||||
default: |
||||
return state; |
||||
} |
||||
} |
@ -0,0 +1,7 @@ |
||||
import calc from './calc'; |
||||
import menu from './menu'; |
||||
|
||||
export { |
||||
calc, |
||||
menu, |
||||
}; |
@ -0,0 +1,43 @@ |
||||
import React, {Component} from 'react'; |
||||
import * as types from '../actions/actionTypes'; |
||||
|
||||
import CalcApp from '../containers/calcApp'; |
||||
import HelpApp from '../containers/helpApp'; |
||||
|
||||
const initialState = { |
||||
isOpen: false, |
||||
refdrawer: null, |
||||
page: <CalcApp />, |
||||
subtitle: 'Calculator Page', |
||||
}; |
||||
|
||||
export default function menu(state = initialState, action = {}) { |
||||
switch (action.type) { |
||||
case types.MENUOPEN: |
||||
return { |
||||
...state, |
||||
isOpen: true, |
||||
}; |
||||
case types.MENUCLOSE: |
||||
return { |
||||
...state, |
||||
isOpen: false, |
||||
}; |
||||
case types.MAIN: |
||||
return { |
||||
...state, |
||||
isOpen: false, |
||||
page: <CalcApp />, |
||||
subtitle: 'Calculator Page', |
||||
}; |
||||
case types.HELP: |
||||
return { |
||||
...state, |
||||
isOpen: false, |
||||
page: <HelpApp />, |
||||
subtitle: 'Help', |
||||
}; |
||||
default: |
||||
return state; |
||||
} |
||||
} |
Loading…
Reference in new issue