Merge branch '52-add-category-modal' into 'main'
Resolve "Add Category Modal" Closes #52 See merge request thschleicher/interaktive-systeme!38
This commit is contained in:
commit
0ea9acde38
10 changed files with 297 additions and 21 deletions
|
|
@ -1,16 +1,10 @@
|
|||
import { Stack } from "expo-router";
|
||||
import { useTheme } from "../../contexts/ThemeContext";
|
||||
|
||||
export default function _Layout() {
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<Stack initialRouteName="index" screenOptions={{
|
||||
headerShown: false
|
||||
}}>
|
||||
<Stack initialRouteName="index" screenOptions={{headerShown: false}}>
|
||||
<Stack.Screen name="index"/>
|
||||
<Stack.Screen
|
||||
name="addCategory"
|
||||
options={{presentation: "modal"}}/>
|
||||
<Stack.Screen name="addCategory" options={{presentation: "modal"}}/>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,8 +1,86 @@
|
|||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { SafeAreaView, StyleSheet, Text, TextInput, View } from "react-native";
|
||||
import { CustomColorPicker, NavigationButton, TypeSelectorSwitch } from "../../../components";
|
||||
import { useTheme } from "../../contexts/ThemeContext";
|
||||
|
||||
const addCategory = () => {
|
||||
const {colors} = useTheme();
|
||||
|
||||
const parameters = useLocalSearchParams();
|
||||
|
||||
const [categoryName, setCartegoryName] = useState<string>("Enter Category Name...");
|
||||
const [categoryColor, setCartegoryColor] = useState<null|string>(null);
|
||||
const [selectedType, setSelectedType] = useState<string>("expense");
|
||||
|
||||
return (
|
||||
<></>
|
||||
<SafeAreaView style={[styles.safeAreaViewStyle, {backgroundColor: colors.backgroundColor}]}>
|
||||
<Text style={[styles.headingTextStyle, {color: colors.primaryText}]}>Category Editor</Text>
|
||||
|
||||
<View style={[styles.containerStyle, {backgroundColor: colors.containerColor}]}>
|
||||
<View style={[styles.textInputViewStyle, {backgroundColor: colors.elementDefaultColor}]}>
|
||||
<TextInput placeholder={categoryName} placeholderTextColor={colors.secondaryText} style={[styles.textInputStyle, {color: colors.primaryText}]} onChangeText={(newName: string) => {
|
||||
setCartegoryName(newName);
|
||||
}}/>
|
||||
</View>
|
||||
|
||||
<TypeSelectorSwitch
|
||||
currentSelected={selectedType}
|
||||
handleButtonPress={(type) => {
|
||||
setSelectedType(type);
|
||||
}}
|
||||
/>
|
||||
|
||||
<View>
|
||||
<CustomColorPicker currentColor={categoryColor} handleColorChange={(color) => {
|
||||
setCartegoryColor(color);
|
||||
}}/>
|
||||
</View>
|
||||
|
||||
<View style={styles.navigationButtonViewStyle}>
|
||||
<NavigationButton text="Back" onPress={() => {
|
||||
router.back();
|
||||
}}/>
|
||||
<NavigationButton text="Save" onPress={() => {
|
||||
console.log("Implement Saving here!");
|
||||
router.back();
|
||||
}}/>
|
||||
</View>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
export default addCategory;
|
||||
export default addCategory;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
containerStyle: {
|
||||
flex: 1,
|
||||
margin: 10,
|
||||
borderRadius: 10,
|
||||
},
|
||||
safeAreaViewStyle: {
|
||||
flex: 1,
|
||||
flexDirection: "column"
|
||||
},
|
||||
headingTextStyle: {
|
||||
fontSize: 40,
|
||||
fontWeight: "bold",
|
||||
alignSelf: "center",
|
||||
marginVertical: 10,
|
||||
},
|
||||
navigationButtonViewStyle: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
marginBottom: 10,
|
||||
},
|
||||
textInputViewStyle: {
|
||||
borderRadius: 10,
|
||||
paddingVertical: 10,
|
||||
margin: 10,
|
||||
},
|
||||
textInputStyle: {
|
||||
paddingHorizontal: 10,
|
||||
fontSize: 25,
|
||||
}
|
||||
});
|
||||
|
|
@ -4,8 +4,7 @@ import { useEffect, useState } from 'react';
|
|||
import { StyleSheet, View } from 'react-native';
|
||||
import { FlatList } from 'react-native-gesture-handler';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { BudgetHeader, LoadingSymbol, Plus } from '../../../components';
|
||||
import CategoryItem from '../../../components/budget/categoryItem';
|
||||
import { BudgetHeader, CategoryItem, LoadingSymbol, Plus } from '../../../components';
|
||||
import useFetch from '../../../hooks/useFetch';
|
||||
import { useTheme } from '../../contexts/ThemeContext';
|
||||
|
||||
|
|
@ -43,7 +42,7 @@ export default function Page() {
|
|||
<BudgetHeader selectedPage={selectedPage} handlePageSelection={handlePageSelection}/>
|
||||
|
||||
<Plus onPress={() => {
|
||||
router.push("/(tabs)/budget/addCategory")
|
||||
router.push({pathname: '/(tabs)/budget/addCategory', params: { guid: '123'}}); //This needs to be changed to a regular push, without parameters
|
||||
}}/>
|
||||
|
||||
{isLoading ? (<LoadingSymbol/>) : (
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ module.exports = function (api) {
|
|||
presets: ['babel-preset-expo'],
|
||||
plugins: [
|
||||
'expo-router/babel',
|
||||
'react-native-reanimated/plugin',
|
||||
],
|
||||
};
|
||||
};
|
||||
|
|
|
|||
51
components/budget/customColorPicker.tsx
Normal file
51
components/budget/customColorPicker.tsx
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import { StyleSheet } from "react-native";
|
||||
import ColorPicker, { BrightnessSlider, HueSlider, Preview, SaturationSlider, } from "reanimated-color-picker";
|
||||
|
||||
export type CustomColorPickerProperties = {
|
||||
currentColor?: string | null,
|
||||
handleColorChange: (color: string) => void | undefined,
|
||||
}
|
||||
|
||||
const CustomColorPicker = (properties: CustomColorPickerProperties) => {
|
||||
return (
|
||||
<ColorPicker
|
||||
value={properties.currentColor ?? generateRandomColor()}
|
||||
onChange={(color) => {
|
||||
properties.handleColorChange(color["hex"])
|
||||
}}
|
||||
style={styles.colorPickerStyle}
|
||||
sliderThickness={30}
|
||||
thumbSize={40}
|
||||
thumbShape= "circle">
|
||||
<Preview
|
||||
style={[styles.previewStyle]}
|
||||
textStyle={{ fontSize: 18 }}
|
||||
colorFormat="hex"
|
||||
hideInitialColor/>
|
||||
|
||||
<HueSlider style={[styles.sliderStyle]}/>
|
||||
<BrightnessSlider style={[styles.sliderStyle]}/>
|
||||
<SaturationSlider style={[styles.sliderStyle]}/>
|
||||
</ColorPicker>
|
||||
);
|
||||
}
|
||||
|
||||
const generateRandomColor = (): string => {
|
||||
return '#' + Math.floor(Math.random()*16777215).toString(16);
|
||||
}
|
||||
|
||||
export default CustomColorPicker;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colorPickerStyle: {
|
||||
justifyContent: 'center',
|
||||
},
|
||||
sliderStyle: {
|
||||
margin: 10,
|
||||
},
|
||||
previewStyle: {
|
||||
margin: 10,
|
||||
height: 50,
|
||||
borderRadius: 10,
|
||||
}
|
||||
});
|
||||
53
components/budget/typeSelectorSwitch.tsx
Normal file
53
components/budget/typeSelectorSwitch.tsx
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
||||
import { useTheme } from "../../app/contexts/ThemeContext";
|
||||
|
||||
export type TypeSelectorSwitchProperties = {
|
||||
handleButtonPress: (type: string) => void,
|
||||
currentSelected: string,
|
||||
}
|
||||
|
||||
const TypeSelectorSwitch = (properties: TypeSelectorSwitchProperties) => {
|
||||
const {colors} = useTheme();
|
||||
|
||||
return (
|
||||
<View style={styles.containerStyle}>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
properties.handleButtonPress("expense");
|
||||
}}
|
||||
style={[styles.touchableOpacityStyle, properties.currentSelected == "expense" ? {backgroundColor: colors.accentColor} : {backgroundColor: colors.elementDefaultColor}]
|
||||
}>
|
||||
<Text style={[styles.textStyle, {color: colors.primaryText}]}>Expenses</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
properties.handleButtonPress("saving");
|
||||
}}
|
||||
style={[styles.touchableOpacityStyle, properties.currentSelected == "saving" ? {backgroundColor: colors.accentColor} : {backgroundColor: colors.elementDefaultColor}]
|
||||
}>
|
||||
<Text style={[styles.textStyle, {color: colors.primaryText}]}>Savings</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default TypeSelectorSwitch;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
containerStyle: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
},
|
||||
touchableOpacityStyle: {
|
||||
flex: 1,
|
||||
marginHorizontal: 10,
|
||||
borderRadius: 10,
|
||||
},
|
||||
textStyle: {
|
||||
paddingHorizontal: 10,
|
||||
fontSize: 25,
|
||||
textAlign: "center",
|
||||
paddingVertical: 5,
|
||||
}
|
||||
});
|
||||
39
components/common/button.tsx
Normal file
39
components/common/button.tsx
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
import { StyleSheet, Text, TouchableHighlight } from "react-native";
|
||||
import { useTheme } from "../../app/contexts/ThemeContext";
|
||||
|
||||
export type NavigationButtonProperties = {
|
||||
onPress?: () => void | undefined,
|
||||
text: string,
|
||||
}
|
||||
|
||||
const NavigationButton = (properties: NavigationButtonProperties) => {
|
||||
|
||||
const {colors} = useTheme();
|
||||
|
||||
return (
|
||||
<TouchableHighlight
|
||||
onPress={properties.onPress}
|
||||
underlayColor={colors.elementSelectedColor}
|
||||
style={[styles.touchableHighlightStyle, {backgroundColor: colors.elementDefaultColor}]}>
|
||||
|
||||
<Text style={[styles.buttonTextStyle, {color: colors.primaryText}]}>{properties.text}</Text>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
|
||||
export default NavigationButton;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
touchableHighlightStyle: {
|
||||
borderRadius: 10,
|
||||
marginVertical: 10,
|
||||
marginHorizontal: 15,
|
||||
paddingHorizontal: 20,
|
||||
paddingVertical: 5,
|
||||
},
|
||||
buttonTextStyle: {
|
||||
textAlign: "center",
|
||||
fontSize: 30,
|
||||
}
|
||||
});
|
||||
|
|
@ -4,19 +4,24 @@ import Welcome from "./home/Welcome"
|
|||
import ExpenseItem from "./home/expenseItem"
|
||||
|
||||
//common
|
||||
import CustomCard from "./common/CustomCard"
|
||||
import SearchBar from "./common/SearchBar"
|
||||
import NavigationButton from "./common/button"
|
||||
import LoadingSymbol from "./common/loadingSymbol"
|
||||
import Plus from "./common/plus"
|
||||
import SearchBar from "./common/SearchBar"
|
||||
import CustomCard from "./common/CustomCard"
|
||||
|
||||
//budget
|
||||
import BudgetHeader from "./budget/budgetHeader"
|
||||
import CategoryItem from "./budget/categoryItem"
|
||||
import CustomColorPicker from "./budget/customColorPicker"
|
||||
import TypeSelectorSwitch from "./budget/typeSelectorSwitch"
|
||||
|
||||
//login
|
||||
import BudgetHeader from "./budget/budgetHeader"
|
||||
import Input from "./login/input"
|
||||
|
||||
export {
|
||||
BudgetHeader, ButtonSetting, CustomCard, ExpenseItem, Input,
|
||||
LoadingSymbol, Plus,
|
||||
SearchBar, ToggleSetting, Welcome
|
||||
BudgetHeader, ButtonSetting, CategoryItem, CustomCard, CustomColorPicker, ExpenseItem, Input,
|
||||
LoadingSymbol, NavigationButton, Plus,
|
||||
SearchBar, ToggleSetting, TypeSelectorSwitch, Welcome
|
||||
}
|
||||
|
||||
|
|
|
|||
56
package-lock.json
generated
56
package-lock.json
generated
|
|
@ -26,10 +26,12 @@
|
|||
"react-native": "0.72.6",
|
||||
"react-native-calendars": "^1.1303.0",
|
||||
"react-native-gesture-handler": "~2.12.0",
|
||||
"react-native-reanimated": "^3.3.0",
|
||||
"react-native-safe-area-context": "4.6.3",
|
||||
"react-native-screens": "~3.22.0",
|
||||
"react-native-uuid": "^2.0.1",
|
||||
"react-native-web": "~0.19.6"
|
||||
"react-native-web": "~0.19.6",
|
||||
"reanimated-color-picker": "^2.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.0",
|
||||
|
|
@ -1415,6 +1417,20 @@
|
|||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-object-assign": {
|
||||
"version": "7.23.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.23.3.tgz",
|
||||
"integrity": "sha512-TPJ6O7gVC2rlQH2hvQGRH273G1xdoloCj9Pc07Q7JbIZYDi+Sv5gaE2fu+r5E7qK4zyt6vj0FbZaZTRU5C3OMA==",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-object-rest-spread": {
|
||||
"version": "7.23.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz",
|
||||
|
|
@ -15761,6 +15777,27 @@
|
|||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-reanimated": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.3.0.tgz",
|
||||
"integrity": "sha512-LzfpPZ1qXBGy5BcUHqw3pBC0qSd22qXS3t8hWSbozXNrBkzMhhOrcILE/nEg/PHpNNp1xvGOW8NwpAMF006roQ==",
|
||||
"dependencies": {
|
||||
"@babel/plugin-transform-object-assign": "^7.16.7",
|
||||
"@babel/preset-typescript": "^7.16.7",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"invariant": "^2.2.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0-0",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0-0",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.0.0-0",
|
||||
"@babel/plugin-transform-arrow-functions": "^7.0.0-0",
|
||||
"@babel/plugin-transform-shorthand-properties": "^7.0.0-0",
|
||||
"@babel/plugin-transform-template-literals": "^7.0.0-0",
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-safe-area-context": {
|
||||
"version": "4.6.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.6.3.tgz",
|
||||
|
|
@ -15909,6 +15946,23 @@
|
|||
"resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz",
|
||||
"integrity": "sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg=="
|
||||
},
|
||||
"node_modules/reanimated-color-picker": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/reanimated-color-picker/-/reanimated-color-picker-2.4.2.tgz",
|
||||
"integrity": "sha512-UNw1jEEOjY2m6F/sI7/nZQwT+yl7kSxao9wJtdkHUsrmm66QRqWKku76/hZGXyl+wzdKk4yLmXMPaEaLq69GBg==",
|
||||
"peerDependencies": {
|
||||
"expo": ">=44.0.0",
|
||||
"react": "*",
|
||||
"react-native": "*",
|
||||
"react-native-gesture-handler": ">=2.0.0",
|
||||
"react-native-reanimated": "^2.0.0 || ^3.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"expo": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/recast": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/recast/-/recast-0.21.5.tgz",
|
||||
|
|
|
|||
|
|
@ -36,10 +36,12 @@
|
|||
"react-native": "0.72.6",
|
||||
"react-native-calendars": "^1.1303.0",
|
||||
"react-native-gesture-handler": "~2.12.0",
|
||||
"react-native-reanimated": "^3.3.0",
|
||||
"react-native-safe-area-context": "4.6.3",
|
||||
"react-native-screens": "~3.22.0",
|
||||
"react-native-uuid": "^2.0.1",
|
||||
"react-native-web": "~0.19.6"
|
||||
"react-native-web": "~0.19.6",
|
||||
"reanimated-color-picker": "^2.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.0",
|
||||
|
|
|
|||
Reference in a new issue