-
Notifications
You must be signed in to change notification settings - Fork 1
Feature: map detail 페이지 도입 #48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
61e35ee
9bfe2e4
a343abd
87e3b10
11e19a7
6bb0fc2
5f7a0f9
f852faa
f87bc64
49a04fa
5f270e4
3d63a63
aada0f1
8043018
2d63309
4d2ce12
135e3bb
43520c7
6907c9a
79f697a
79338ba
6f996b7
6de1cf0
2762037
ddfa5cb
d51d4ab
328fc6d
3c38d36
72f025e
249229d
87cfe76
ea1d043
f4beefa
ad265da
06ab1c0
a29e436
7413ab5
996f747
dc3b876
6a62317
7cb4784
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ interface CardProps { | |
| onPress?: () => void; | ||
| style?: ViewStyle; | ||
| fullWidth?: boolean; | ||
| variant?: 'default' | 'light'; | ||
| } | ||
|
|
||
| export default function Card({ | ||
|
|
@@ -31,6 +32,7 @@ export default function Card({ | |
| onPress, | ||
| style, | ||
| fullWidth = false, | ||
| variant = 'default', | ||
| }: CardProps) { | ||
| const { width: screenWidth } = Dimensions.get('window'); | ||
| const [imageError, setImageError] = useState(false); | ||
|
|
@@ -52,6 +54,7 @@ export default function Card({ | |
| <CardContainer | ||
| fullWidth={fullWidth} | ||
| mode={finalMode} | ||
| variant={variant} | ||
| onPress={onPress} | ||
| style={style} | ||
| activeOpacity={0.8} | ||
|
|
@@ -77,7 +80,7 @@ export default function Card({ | |
| </LoadingContainer> | ||
| )} | ||
| {content?.hasStar && ( | ||
| <StarIcon size={20} color="#FFD700" fill="#FFD700" /> | ||
| <StarIcon size={20} color="#2d2d2d" fill="#2d2d2d" /> | ||
| )} | ||
| </ImageContainer> | ||
| </CardContainer> | ||
|
|
@@ -86,16 +89,19 @@ export default function Card({ | |
|
|
||
| if (finalMode === 'image-with-text') { | ||
| const contentWidth = screenWidth - 64; | ||
| const imageSize = contentWidth * 0.45; | ||
| const imageSize = contentWidth * 0.36; // 0.45에서 0.36으로 변경 (약 20% 감소) | ||
| return ( | ||
| <CardContainer | ||
| fullWidth={fullWidth} | ||
| mode={finalMode} | ||
| variant={variant} | ||
| onPress={onPress} | ||
| style={style} | ||
| activeOpacity={0.8} | ||
| > | ||
| {content?.cardTitle && <CardTitle>{content.cardTitle}</CardTitle>} | ||
| {content?.cardTitle && ( | ||
| <CardTitle variant={variant}>{content.cardTitle}</CardTitle> | ||
| )} | ||
| <ContentRow> | ||
| {imageSource && ( | ||
| <CardImage | ||
|
|
@@ -107,14 +113,18 @@ export default function Card({ | |
| )} | ||
| {content && ( | ||
| <CardContentContainer> | ||
| {content.title && <Title>{content.title}</Title>} | ||
| {content.subtitle && <Subtitle>{content.subtitle}</Subtitle>} | ||
| {content.title && ( | ||
| <Title variant={variant}>{content.title}</Title> | ||
| )} | ||
| {content.subtitle && ( | ||
| <Subtitle variant={variant}>{content.subtitle}</Subtitle> | ||
| )} | ||
| {content.stats && ( | ||
| <StatsContainer> | ||
| {content.stats.map((stat, index) => ( | ||
| <StatItem key={index}> | ||
| <StatLabel>{stat.label}</StatLabel> | ||
| <StatValue>{stat.value}</StatValue> | ||
| <StatLabel variant={variant}>{stat.label}</StatLabel> | ||
| <StatValue variant={variant}>{stat.value}</StatValue> | ||
| </StatItem> | ||
| ))} | ||
| </StatsContainer> | ||
|
|
@@ -130,6 +140,7 @@ export default function Card({ | |
| <CardContainer | ||
| fullWidth={fullWidth} | ||
| mode={finalMode} | ||
| variant={variant} | ||
| onPress={onPress} | ||
| style={style} | ||
| activeOpacity={0.8} | ||
|
|
@@ -142,9 +153,11 @@ export default function Card({ | |
| const CardContainer = styled(TouchableOpacity)<{ | ||
| fullWidth: boolean; | ||
| mode: CardMode; | ||
| }>(({ fullWidth, mode }) => ({ | ||
| backgroundColor: 'rgba(255, 255, 255, 0.1)', | ||
| borderColor: 'rgba(255, 255, 255, 0.2)', | ||
| variant: 'default' | 'light'; | ||
| }>(({ fullWidth, mode, variant }) => ({ | ||
| backgroundColor: variant === 'light' ? '#ffffff' : 'rgba(255, 255, 255, 0.1)', | ||
| borderColor: | ||
| variant === 'light' ? 'rgba(0, 0, 0, 0.1)' : 'rgba(255, 255, 255, 0.2)', | ||
|
Comment on lines
+158
to
+160
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 배경색과 테두리 색상이 하드코딩되어 있습니다. 스타일 가이드에 따라 재사용 가능한 색상은 테마 또는 상수 파일에서 관리해야 합니다.1 Style Guide ReferencesFootnotes
|
||
| borderWidth: 1, | ||
| borderRadius: 12, | ||
| padding: mode === 'only-image' ? 0 : 16, | ||
|
|
@@ -153,12 +166,14 @@ const CardContainer = styled(TouchableOpacity)<{ | |
| aspectRatio: fullWidth ? undefined : 1, | ||
| })); | ||
|
|
||
| const CardTitle = styled.Text({ | ||
| color: '#ffffff', | ||
| fontSize: 16, | ||
| fontWeight: '600', | ||
| marginBottom: 8, | ||
| }); | ||
| const CardTitle = styled.Text<{ variant?: 'default' | 'light' }>( | ||
| ({ variant }) => ({ | ||
| color: variant === 'light' ? '#000000' : '#ffffff', | ||
| fontSize: 16, | ||
| fontWeight: '600', | ||
| marginBottom: 8, | ||
| }), | ||
| ); | ||
|
|
||
| const ContentRow = styled.View({ | ||
| flexDirection: 'row', | ||
|
|
@@ -185,18 +200,21 @@ const CardContentContainer = styled.View({ | |
| flex: 1, | ||
| }); | ||
|
|
||
| const Title = styled.Text({ | ||
| color: '#ffffff', | ||
| const Title = styled.Text<{ variant?: 'default' | 'light' }>(({ variant }) => ({ | ||
| color: variant === 'light' ? '#000000' : '#ffffff', | ||
| fontSize: 18, | ||
| fontWeight: 'bold', | ||
| marginBottom: 4, | ||
| }); | ||
| })); | ||
|
|
||
| const Subtitle = styled.Text({ | ||
| color: 'rgba(255, 255, 255, 0.7)', | ||
| fontSize: 14, | ||
| marginBottom: 8, | ||
| }); | ||
| const Subtitle = styled.Text<{ variant?: 'default' | 'light' }>( | ||
| ({ variant }) => ({ | ||
| color: | ||
| variant === 'light' ? 'rgba(0, 0, 0, 0.7)' : 'rgba(255, 255, 255, 0.7)', | ||
| fontSize: 14, | ||
| marginBottom: 8, | ||
| }), | ||
| ); | ||
|
|
||
| const StatsContainer = styled.View({ | ||
| flexDirection: 'row', | ||
|
|
@@ -207,17 +225,23 @@ const StatItem = styled.View({ | |
| alignItems: 'center', | ||
| }); | ||
|
|
||
| const StatLabel = styled.Text({ | ||
| color: 'rgba(255, 255, 255, 0.8)', | ||
| fontSize: 12, | ||
| marginBottom: 2, | ||
| }); | ||
| const StatLabel = styled.Text<{ variant?: 'default' | 'light' }>( | ||
| ({ variant }) => ({ | ||
| color: | ||
| variant === 'light' ? 'rgba(0, 0, 0, 0.8)' : 'rgba(255, 255, 255, 0.8)', | ||
| fontSize: 12, | ||
| marginBottom: 2, | ||
| }), | ||
| ); | ||
|
|
||
| const StatValue = styled.Text({ | ||
| color: 'rgba(255, 255, 255, 0.8)', | ||
| fontSize: 14, | ||
| fontWeight: '600', | ||
| }); | ||
| const StatValue = styled.Text<{ variant?: 'default' | 'light' }>( | ||
| ({ variant }) => ({ | ||
| color: | ||
| variant === 'light' ? 'rgba(0, 0, 0, 0.8)' : 'rgba(255, 255, 255, 0.8)', | ||
| fontSize: 14, | ||
| fontWeight: '600', | ||
| }), | ||
| ); | ||
|
|
||
| const StarIcon = styled(Star)({ | ||
| position: 'absolute', | ||
|
|
@@ -254,7 +278,7 @@ const LoadingContainer = styled.View({ | |
| }); | ||
|
|
||
| const LoadingText = styled.Text({ | ||
| color: '#007AFF', | ||
| color: '#2d2d2d', | ||
| fontSize: 12, | ||
| fontWeight: '500', | ||
| }); | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -7,8 +7,9 @@ import { INITIAL_ZOOM_LEVEL } from '@/constants/location'; | |||||
| interface MapProps { | ||||||
| mapRef: RefObject<Mapbox.MapView | null>; | ||||||
| cameraRef: RefObject<Mapbox.Camera | null>; | ||||||
| initialLocation: Position; | ||||||
| initialLocation: Position | null; | ||||||
| onUserLocationUpdate?: (location: Mapbox.Location) => void; | ||||||
| onMapReady?: () => void; | ||||||
| children?: React.ReactNode; | ||||||
| showUserLocation?: boolean; | ||||||
| } | ||||||
|
|
@@ -18,15 +19,23 @@ export default function Map({ | |||||
| cameraRef, | ||||||
| initialLocation, | ||||||
| onUserLocationUpdate, | ||||||
| onMapReady, | ||||||
| children, | ||||||
| showUserLocation = true, | ||||||
| }: MapProps) { | ||||||
| // initialLocation이 없으면 기본 서울 좌표 사용 | ||||||
| const safeInitialLocation = initialLocation || [127.0276, 37.4979]; | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 기본 위치 좌표가 하드코딩되어 있습니다. 이와 같은 값은 매직 넘버로 간주될 수 있으며,
Suggested change
Style Guide ReferencesFootnotes
|
||||||
|
|
||||||
| return ( | ||||||
| <StyledMapView ref={mapRef} styleURL={Mapbox.StyleURL.Street}> | ||||||
| <StyledMapView | ||||||
| ref={mapRef} | ||||||
| styleURL={Mapbox.StyleURL.Street} | ||||||
| onDidFinishLoadingMap={onMapReady} | ||||||
| > | ||||||
| <Mapbox.Camera | ||||||
| ref={cameraRef} | ||||||
| defaultSettings={{ | ||||||
| centerCoordinate: initialLocation, | ||||||
| centerCoordinate: safeInitialLocation, | ||||||
| zoomLevel: INITIAL_ZOOM_LEVEL, | ||||||
| }} | ||||||
| /> | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이미지 크기 계산에 사용된
0.36은 매직 넘버입니다. 코드의 가독성과 유지보수성을 위해 의미 있는 이름의 상수로 정의하여 사용하는 것이 좋습니다.1Style Guide References
Footnotes
매직 넘버(의미를 알 수 없는 숫자)는 코드 가독성을 해치므로, 이름 있는 상수로 정의하여 사용해야 합니다. ↩