-
Notifications
You must be signed in to change notification settings - Fork 2
[feat/MAT-253] Header 공통 Component 개발 #266
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
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| import React, { type FC, type ReactNode } from 'react'; | ||
| import { View, Text } from 'react-native'; | ||
| import { ChevronLeft, type LucideIcon } from 'lucide-react-native'; | ||
| import { useNavigation } from '@react-navigation/native'; | ||
|
|
||
| import { colors } from '@theme/tokens'; | ||
|
|
||
| import ContentInset from './ContentInset'; | ||
| import AnimatedPressable from './AnimatedPressable'; | ||
|
|
||
| type HeaderBadge = 'correct' | 'incorrect'; | ||
|
|
||
| type HeaderProps = { | ||
| title?: string; | ||
| subtitle?: string; | ||
| badge?: HeaderBadge; | ||
| showBackButton?: boolean; | ||
| onPressBack?: () => void; | ||
| right?: ReactNode; | ||
| }; | ||
|
|
||
| const badgeConfig = { | ||
| correct: { bg: 'bg-primary-200', text: 'text-primary-600', label: '정답' }, | ||
| incorrect: { bg: 'bg-red-100', text: 'text-red-500', label: '오답' }, | ||
| } as const; | ||
|
|
||
| const Badge = ({ variant }: { variant: HeaderBadge }) => { | ||
| const config = badgeConfig[variant]; | ||
| return ( | ||
| <View className={`rounded px-[6px] py-[2px] ${config.bg}`}> | ||
| <Text className={`typo-heading-2-semibold ${config.text}`}>{config.label}</Text> | ||
| </View> | ||
| ); | ||
| }; | ||
|
|
||
| const HeaderTextButton = ({ | ||
| children, | ||
| onPress, | ||
| color = colors['gray-700'], | ||
| }: { | ||
| children: ReactNode; | ||
| onPress?: () => void; | ||
| color?: string; | ||
| }) => ( | ||
| <AnimatedPressable className='h-[48px] items-center justify-center px-[12px]' onPress={onPress}> | ||
| <Text className='typo-heading-2-semibold' style={{ color }}> | ||
| {children} | ||
| </Text> | ||
| </AnimatedPressable> | ||
| ); | ||
|
|
||
| const HeaderIconButton = ({ | ||
| icon: Icon, | ||
| onPress, | ||
| color, | ||
| }: { | ||
| icon: LucideIcon; | ||
| onPress?: () => void; | ||
| color?: string; | ||
| }) => ( | ||
| <AnimatedPressable className='p-[12px]' onPress={onPress}> | ||
| <Icon size={24} {...(color && { color })} /> | ||
| </AnimatedPressable> | ||
| ); | ||
|
|
||
| const getRightGap = (children: ReactNode): number => { | ||
| let hasTextButton = false; | ||
| const check = (node: ReactNode) => { | ||
| React.Children.forEach(node, (child) => { | ||
| if (!React.isValidElement(child)) return; | ||
| if (child.type === HeaderTextButton) hasTextButton = true; | ||
| if (child.type === React.Fragment) check((child.props as { children?: ReactNode }).children); | ||
| }); | ||
| }; | ||
| check(children); | ||
| return hasTextButton ? 8 : 4; | ||
| }; | ||
|
|
||
| const HeaderRoot = ({ | ||
| title, | ||
| subtitle, | ||
| badge, | ||
| showBackButton, | ||
| onPressBack, | ||
| right, | ||
| }: HeaderProps) => { | ||
| const navigation = useNavigation(); | ||
|
|
||
| const handleBack = () => { | ||
| if (onPressBack) { | ||
| onPressBack(); | ||
| return; | ||
| } | ||
| if (navigation.canGoBack()) { | ||
| navigation.goBack(); | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <View className='h-[56px] w-full'> | ||
| <ContentInset className='h-full flex-row items-center justify-between'> | ||
| <View className='flex-row items-center gap-[4px]'> | ||
| {showBackButton && <HeaderIconButton icon={ChevronLeft} onPress={handleBack} />} | ||
| {title && ( | ||
| <View className='flex-row items-center gap-[12px]'> | ||
| <Text className='typo-title-1-bold text-gray-900'>{title}</Text> | ||
| {subtitle && <Text className='typo-title-1-semibold text-gray-700'>{subtitle}</Text>} | ||
| {badge && <Badge variant={badge} />} | ||
| </View> | ||
| )} | ||
| </View> | ||
| {right && ( | ||
| <View className='flex-row items-center' style={{ gap: getRightGap(right) }}> | ||
| {right} | ||
| </View> | ||
| )} | ||
| </ContentInset> | ||
| </View> | ||
| ); | ||
| }; | ||
|
|
||
| type HeaderComponent = FC<HeaderProps> & { | ||
| TextButton: typeof HeaderTextButton; | ||
| IconButton: typeof HeaderIconButton; | ||
| }; | ||
|
|
||
| const Header = HeaderRoot as HeaderComponent; | ||
| Header.TextButton = HeaderTextButton; | ||
| Header.IconButton = HeaderIconButton; | ||
|
|
||
| export default Header; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
56 changes: 28 additions & 28 deletions
56
apps/native/src/components/system/icons/AlertBellButtonIcon.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,36 +1,36 @@ | ||
| import React from 'react'; | ||
| import { Path, Circle, Svg } from 'react-native-svg'; | ||
| import { Path, Svg } from 'react-native-svg'; | ||
| import type { LucideIcon, LucideProps } from 'lucide-react-native'; | ||
|
|
||
| const AlertButtonIcon = React.forwardRef<React.ComponentRef<typeof Svg>, LucideProps>( | ||
| ({ color = 'black', size = 48, strokeWidth = 2, ...rest }, ref) => { | ||
| const resolvedStrokeWidth = Number(strokeWidth); | ||
|
|
||
| ({ size = 24, ...rest }, ref) => { | ||
| return ( | ||
| <Svg ref={ref} width={size} height={size} viewBox='0 0 48 48' fill='none' {...rest}> | ||
| <Path | ||
| d='M22.2681 33C22.4436 33.304 22.6961 33.5565 23.0001 33.732C23.3041 33.9075 23.649 33.9999 24.0001 33.9999C24.3511 33.9999 24.696 33.9075 25 33.732C25.3041 33.5565 25.5565 33.304 25.7321 33' | ||
| stroke={color} | ||
| strokeWidth={resolvedStrokeWidth} | ||
| strokeLinecap='round' | ||
| strokeLinejoin='round' | ||
| /> | ||
| <Path | ||
| d='M25.9161 14.3141C25.0136 14.01 24.0516 13.9251 23.1098 14.0664C22.168 14.2077 21.2733 14.5712 20.4998 15.1269C19.7263 15.6825 19.0961 16.4142 18.6614 17.2616C18.2268 18.1091 18.0001 19.0478 18.0001 20.0001C18.0001 24.4991 16.5891 25.9561 15.2601 27.3271C15.1297 27.4705 15.0437 27.6486 15.0127 27.8399C14.9816 28.0312 15.0068 28.2274 15.0852 28.4047C15.1636 28.5819 15.2918 28.7325 15.4542 28.8383C15.6167 28.944 15.8063 29.0002 16.0001 29.0001H32.0001C32.1939 29.0002 32.3836 28.944 32.546 28.8383C32.7084 28.7325 32.8366 28.5819 32.915 28.4047C32.9934 28.2274 33.0186 28.0312 32.9876 27.8399C32.9565 27.6486 32.8705 27.4705 32.7401 27.3271C32.5343 27.1152 32.3391 26.8933 32.1551 26.6621' | ||
| stroke={color} | ||
| strokeWidth={resolvedStrokeWidth} | ||
| strokeLinecap='round' | ||
| strokeLinejoin='round' | ||
| /> | ||
| <Circle | ||
| cx={30} | ||
| cy={20} | ||
| r={3} | ||
| fill='#E57A00' | ||
| stroke='#E57A00' | ||
| strokeWidth={resolvedStrokeWidth} | ||
| /> | ||
| </Svg> | ||
| <> | ||
| <Svg ref={ref} width={size} height={size} viewBox='0 0 24 24' fill='none' {...rest}> | ||
| <Path | ||
| d='M10.2681 21C10.4436 21.304 10.6961 21.5565 11.0001 21.732C11.3041 21.9075 11.649 21.9999 12.0001 21.9999C12.3511 21.9999 12.696 21.9075 13 21.732C13.3041 21.5565 13.5565 21.304 13.7321 21' | ||
| stroke='#1E1E21' | ||
| strokeWidth={2} | ||
|
Comment on lines
5
to
+13
|
||
| strokeLinecap='round' | ||
| strokeLinejoin='round' | ||
| /> | ||
| <Path | ||
| d='M13.9159 2.31415C13.0134 2.01002 12.0514 1.9251 11.1096 2.06642C10.1677 2.20774 9.27304 2.57124 8.49953 3.12685C7.72601 3.68246 7.09586 4.41423 6.6612 5.26164C6.22654 6.10905 5.99985 7.04776 5.99987 8.00015C5.99987 12.4991 4.58887 13.9561 3.25987 15.3271C3.12944 15.4705 3.04348 15.6486 3.01243 15.8399C2.98138 16.0312 3.00659 16.2274 3.08498 16.4047C3.16338 16.5819 3.29158 16.7325 3.454 16.8383C3.61643 16.944 3.80607 17.0002 3.99987 17.0001H19.9999C20.1937 17.0002 20.3833 16.944 20.5457 16.8383C20.7082 16.7325 20.8364 16.5819 20.9148 16.4047C20.9932 16.2274 21.0184 16.0312 20.9873 15.8399C20.9563 15.6486 20.8703 15.4705 20.7399 15.3271C20.5341 15.1152 20.3388 14.8933 20.1549 14.6621' | ||
| stroke='#1E1E21' | ||
| strokeWidth={2} | ||
| strokeLinecap='round' | ||
| strokeLinejoin='round' | ||
| /> | ||
| <Path | ||
| d='M18 11C19.6569 11 21 9.65685 21 8C21 6.34315 19.6569 5 18 5C16.3431 5 15 6.34315 15 8C15 9.65685 16.3431 11 18 11Z' | ||
| fill='#E57A00' | ||
| stroke='#E57A00' | ||
| strokeWidth={2} | ||
| strokeLinecap='round' | ||
| strokeLinejoin='round' | ||
| /> | ||
| </Svg> | ||
| </> | ||
| ); | ||
| } | ||
| ) as LucideIcon; | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Text에color-gray-900/color-gray-700클래스가 사용되고 있는데, 이 프로젝트의 다른 컴포넌트들은text-gray-900/text-gray-700형태로 색상을 지정하고 있습니다(예: student/problem/components/Header.tsx). 현재 클래스명은 Tailwind/NativeWind에서 매칭되지 않아 타이틀/서브타이틀 색상이 적용되지 않을 가능성이 큽니다.text-gray-*로 수정하거나, 토큰 색상을style={{ color: ... }}로 일관되게 적용해 주세요.