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:
Thomas Schleicher 2024-01-03 17:31:59 +00:00
commit 0ea9acde38
10 changed files with 297 additions and 21 deletions

View file

@ -1,16 +1,10 @@
import { Stack } from "expo-router"; import { Stack } from "expo-router";
import { useTheme } from "../../contexts/ThemeContext";
export default function _Layout() { export default function _Layout() {
const { colors } = useTheme();
return ( return (
<Stack initialRouteName="index" screenOptions={{ <Stack initialRouteName="index" screenOptions={{headerShown: false}}>
headerShown: false
}}>
<Stack.Screen name="index"/> <Stack.Screen name="index"/>
<Stack.Screen <Stack.Screen name="addCategory" options={{presentation: "modal"}}/>
name="addCategory"
options={{presentation: "modal"}}/>
</Stack> </Stack>
); );
} }

View file

@ -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 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 ( 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,
}
});

View file

@ -4,8 +4,7 @@ import { useEffect, useState } from 'react';
import { StyleSheet, View } from 'react-native'; import { StyleSheet, View } from 'react-native';
import { FlatList } from 'react-native-gesture-handler'; import { FlatList } from 'react-native-gesture-handler';
import { SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaView } from 'react-native-safe-area-context';
import { BudgetHeader, LoadingSymbol, Plus } from '../../../components'; import { BudgetHeader, CategoryItem, LoadingSymbol, Plus } from '../../../components';
import CategoryItem from '../../../components/budget/categoryItem';
import useFetch from '../../../hooks/useFetch'; import useFetch from '../../../hooks/useFetch';
import { useTheme } from '../../contexts/ThemeContext'; import { useTheme } from '../../contexts/ThemeContext';
@ -43,7 +42,7 @@ export default function Page() {
<BudgetHeader selectedPage={selectedPage} handlePageSelection={handlePageSelection}/> <BudgetHeader selectedPage={selectedPage} handlePageSelection={handlePageSelection}/>
<Plus onPress={() => { <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/>) : ( {isLoading ? (<LoadingSymbol/>) : (

View file

@ -4,6 +4,7 @@ module.exports = function (api) {
presets: ['babel-preset-expo'], presets: ['babel-preset-expo'],
plugins: [ plugins: [
'expo-router/babel', 'expo-router/babel',
'react-native-reanimated/plugin',
], ],
}; };
}; };

View 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,
}
});

View 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,
}
});

View 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,
}
});

View file

@ -4,19 +4,24 @@ import Welcome from "./home/Welcome"
import ExpenseItem from "./home/expenseItem" import ExpenseItem from "./home/expenseItem"
//common //common
import CustomCard from "./common/CustomCard"
import SearchBar from "./common/SearchBar"
import NavigationButton from "./common/button"
import LoadingSymbol from "./common/loadingSymbol" import LoadingSymbol from "./common/loadingSymbol"
import Plus from "./common/plus" 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 //login
import BudgetHeader from "./budget/budgetHeader"
import Input from "./login/input" import Input from "./login/input"
export { export {
BudgetHeader, ButtonSetting, CustomCard, ExpenseItem, Input, BudgetHeader, ButtonSetting, CategoryItem, CustomCard, CustomColorPicker, ExpenseItem, Input,
LoadingSymbol, Plus, LoadingSymbol, NavigationButton, Plus,
SearchBar, ToggleSetting, Welcome SearchBar, ToggleSetting, TypeSelectorSwitch, Welcome
} }

56
package-lock.json generated
View file

@ -26,10 +26,12 @@
"react-native": "0.72.6", "react-native": "0.72.6",
"react-native-calendars": "^1.1303.0", "react-native-calendars": "^1.1303.0",
"react-native-gesture-handler": "~2.12.0", "react-native-gesture-handler": "~2.12.0",
"react-native-reanimated": "^3.3.0",
"react-native-safe-area-context": "4.6.3", "react-native-safe-area-context": "4.6.3",
"react-native-screens": "~3.22.0", "react-native-screens": "~3.22.0",
"react-native-uuid": "^2.0.1", "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": { "devDependencies": {
"@babel/core": "^7.20.0", "@babel/core": "^7.20.0",
@ -1415,6 +1417,20 @@
"@babel/core": "^7.0.0-0" "@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": { "node_modules/@babel/plugin-transform-object-rest-spread": {
"version": "7.23.4", "version": "7.23.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz", "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": "*" "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": { "node_modules/react-native-safe-area-context": {
"version": "4.6.3", "version": "4.6.3",
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.6.3.tgz", "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", "resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz",
"integrity": "sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg==" "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": { "node_modules/recast": {
"version": "0.21.5", "version": "0.21.5",
"resolved": "https://registry.npmjs.org/recast/-/recast-0.21.5.tgz", "resolved": "https://registry.npmjs.org/recast/-/recast-0.21.5.tgz",

View file

@ -36,10 +36,12 @@
"react-native": "0.72.6", "react-native": "0.72.6",
"react-native-calendars": "^1.1303.0", "react-native-calendars": "^1.1303.0",
"react-native-gesture-handler": "~2.12.0", "react-native-gesture-handler": "~2.12.0",
"react-native-reanimated": "^3.3.0",
"react-native-safe-area-context": "4.6.3", "react-native-safe-area-context": "4.6.3",
"react-native-screens": "~3.22.0", "react-native-screens": "~3.22.0",
"react-native-uuid": "^2.0.1", "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": { "devDependencies": {
"@babel/core": "^7.20.0", "@babel/core": "^7.20.0",