diff --git a/App.tsx b/App.tsx index 51c0c3b..6da9e98 100644 --- a/App.tsx +++ b/App.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import { NavigationContainer } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; @@ -7,6 +7,11 @@ import MainScreen from './src/screens/MainScreen'; import { Card } from './src/types'; import CardEditScreen from './src/screens/CardEditScreen'; import { QueryClient, QueryClientProvider } from 'react-query'; +import { StatusBar, useColorScheme } from 'react-native'; +import { ThemeProvider } from 'styled-components/native'; + +import LightTheme from './src/themes/lightTheme'; +import DarkTheme from './src/themes/darkTheme'; export type RootStackParams = { Main: undefined; @@ -18,39 +23,53 @@ const queryClient = new QueryClient(); const Stack = createNativeStackNavigator(); const App = () => { + const colorScheme = useColorScheme(); + const theme = useMemo( + () => (colorScheme === 'dark' ? DarkTheme : LightTheme), + [colorScheme], + ); + return ( - - - + + + - - - - + > + + + + + + ); }; diff --git a/package.json b/package.json index 4745093..1b4fabd 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "react-native-shadow-2": "^7.0.6", "react-native-svg": "^13.8.0", "react-native-vector-icons": "^9.2.0", - "react-query": "^3.39.3" + "react-query": "^3.39.3", + "styled-components": "^5.3.6" }, "devDependencies": { "@babel/core": "^7.20.0", @@ -33,6 +34,8 @@ "@types/react": "^18.0.24", "@types/react-native-vector-icons": "^6.4.13", "@types/react-test-renderer": "^18.0.0", + "@types/styled-components": "^5.1.26", + "@types/styled-components-react-native": "^5.2.1", "babel-jest": "^29.2.1", "eslint": "^8.19.0", "jest": "^29.2.1", diff --git a/src/components/Card.tsx b/src/components/Card.tsx index 41a1bbf..09e0575 100644 --- a/src/components/Card.tsx +++ b/src/components/Card.tsx @@ -1,9 +1,10 @@ import React, { useMemo, useCallback } from 'react'; -import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; +import { StyleSheet, TextProps, TouchableOpacity, View } from 'react-native'; import { useQuery } from 'react-query'; import { Shadow } from 'react-native-shadow-2'; import FontAwesome5 from 'react-native-vector-icons/FontAwesome5'; import Clipboard from '@react-native-clipboard/clipboard'; +import styled from 'styled-components/native'; import CardConv from '../modules/CardConv'; import { Card } from '../types'; @@ -19,6 +20,46 @@ interface CardViewProps { onDelete?: (index: number) => unknown; } +const Background = styled.View` + flex: 1; + border-radius: 8px; + justify-content: center; + background-color: ${props => props.theme.colors.primary}; +`; + +const CardName = styled.Text.attrs({ + numberOfLines: 1, + ellipsizeMode: 'tail', +})` + font-size: 16px; + font-weight: bold; + color: ${props => props.theme.colors.white}; +`; + +const CardNumber = styled.Text` + color: ${props => props.theme.colors.gray200}; + font-size: 14px; +`; + +const CopyIcon = styled(FontAwesome5).attrs({ name: 'copy' })` + color: ${props => props.theme.colors.gray200}; + font-size: 10px; + padding-left: 4px; +`; + +const ActivateButtonText = styled.Text` + text-align: center; + align-self: center; + font-size: 24px; + font-weight: 500; + color: ${props => props.theme.colors.white}; +`; + +const BottomButtonText = styled.Text` + font-size: 14px; + color: ${props => props.theme.colors.white}; +`; + const CardView = (props: CardViewProps) => { const { card, index, onPress: onPressFromProps, onEdit, onDelete } = props; @@ -61,7 +102,7 @@ const CardView = (props: CardViewProps) => { distance={4} offset={[0, 2]} > - + { > - - {card.name} - + {card.name} - {styledUid} - + {styledUid} + - {props.mainText} + {props.mainText} {props.hideBottomMenu !== true && ( @@ -94,18 +126,18 @@ const CardView = (props: CardViewProps) => { style={styles.bottomMenuButton} onPress={onPressEdit} > - 편집 + 편집 - 삭제 + 삭제 )} - + ); @@ -120,18 +152,11 @@ const styles = StyleSheet.create({ flex: 1, alignSelf: 'stretch', }, - background: { - flex: 1, - borderRadius: 8, - justifyContent: 'center', - resizeMode: 'contain', - backgroundColor: 'skyblue', - }, activateButton: { flex: 1, justifyContent: 'center', borderRadius: 8, - backgroundColor: 'rgba(0, 0, 0, 0.2)', + backgroundColor: 'rgba(0, 0, 0, 0.1)', }, topLeftArea: { position: 'absolute', @@ -141,33 +166,10 @@ const styles = StyleSheet.create({ flexDirection: 'row', justifyContent: 'space-between', }, - cardNameText: { - padding: 0, - fontSize: 16, - fontWeight: 'bold', - color: '#ffffff', - }, cardNumberContainer: { flexDirection: 'row', alignItems: 'center', }, - cardNumberText: { - alignSelf: 'center', - color: '#f1f1f1', - fontSize: 14, - }, - cardNumberCopyIcon: { - color: '#f1f1f1', - fontSize: 10, - paddingLeft: 4, - }, - enableText: { - textAlign: 'center', - alignSelf: 'center', - fontSize: 24, - color: '#FAFAFA', - fontWeight: '500', - }, bottomMenuContainer: { position: 'absolute', left: 0, @@ -181,10 +183,6 @@ const styles = StyleSheet.create({ justifyContent: 'center', alignItems: 'center', }, - bottomMenuButtonText: { - fontSize: 14, - color: '#FAFAFA', - }, }); export default CardView; diff --git a/src/screens/CardEditScreen.tsx b/src/screens/CardEditScreen.tsx index ed1197d..1a5dd7e 100644 --- a/src/screens/CardEditScreen.tsx +++ b/src/screens/CardEditScreen.tsx @@ -3,23 +3,22 @@ import { View, Text, ScrollView, - TouchableOpacity, StyleSheet, - KeyboardAvoidingView, - TextInput, TextInputProps, ViewStyle, TextInputFocusEventData, NativeSyntheticEvent, - TextStyle, + TouchableOpacityProps, } from 'react-native'; -import CardConv from '../modules/CardConv'; import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { RootStackParams } from '../../App'; import { Shadow } from 'react-native-shadow-2'; +import { useMutation, useQuery, useQueryClient } from 'react-query'; +import styled from 'styled-components/native'; + +import CardConv from '../modules/CardConv'; +import { RootStackParams } from '../../App'; import CardView from '../components/Card'; import { addCard, updateCard } from '../data/cards'; -import { useMutation, useQuery, useQueryClient } from 'react-query'; import { Card } from '../types'; type TextFieldProps = TextInputProps & { @@ -38,9 +37,69 @@ const generateRandomCardNumber = () => { return `02FE${getRandom4Byte()}${getRandom4Byte()}${getRandom4Byte()}`; }; -const FieldTitle = (props: { title: string; style?: TextStyle }) => { +const Container = styled.KeyboardAvoidingView` + flex: 1; + background-color: ${props => props.theme.colors.background}; +`; + +const FieldTitle = styled(Text)<{ focused: boolean }>` + font-size: 14px; + font-weight: bold; + color: ${props => + props.focused + ? props.theme.colors.primary + : props.theme.colors.placeholder}; +`; + +const FieldBottomBorder = styled.View<{ focused: boolean }>` + padding-top: 2px; + background-color: ${props => + props.focused + ? props.theme.colors.primary + : props.theme.colors.placeholder}; + height: ${props => (props.focused ? 2 : 1)}px; +`; + +const StyledTextInput = styled.TextInput` + font-size: 16px; + padding-top: 4px; + color: ${props => + props.editable !== false + ? props.theme.colors.text + : props.theme.colors.disabled}; +`; + +const ButtonContainer = styled.TouchableOpacity` + height: 48px; + background-color: ${props => props.theme.colors.primary}; + justify-content: center; + align-items: center; +`; + +const ButtonText = styled.Text` + font-size: 16px; + color: ${props => props.theme.colors.white}; +`; + +type ButtonProps = { + text: string; + containerStyle: ViewStyle; +} & TouchableOpacityProps; + +const Button = (props: ButtonProps) => { + const { text, containerStyle, ...touchableProps } = props; + return ( - {props.title} + + {/* shadow가 정상적으로 적용되지 않는 버그가 있어서 borderRadius 스타일을 분리 */} + + {text} + + ); }; @@ -66,25 +125,13 @@ const TextField = (props: TextFieldProps) => { return ( - - {title} + - + ); }; @@ -171,25 +218,23 @@ const CardEditScreen = (props: CardAddScreenProps | CardEditScreenProps) => { ]); return ( - + - - - + - + { - - - 카드 번호 변경 - - + onPress={changeCardNumber} + disabled={!uid.isSuccess} + text={'카드 번호 변경'} + /> - - - 저장 - - +