diff --git a/app/(app)/project/index.tsx b/app/(app)/project/index.tsx index df5043f..4515ac1 100644 --- a/app/(app)/project/index.tsx +++ b/app/(app)/project/index.tsx @@ -1,12 +1,39 @@ -import { View } from 'react-native'; +import { useRouter } from 'expo-router'; +import { useState } from 'react'; +import { SafeAreaView } from 'react-native'; +import SolidButton from '@/components/common/button/SolidButton'; import Typography from '@/components/common/typography'; +import ProjectInviteModal from '@/components/project/ProjectInviteModal'; function Project() { + const [visible, setVisible] = useState(false); + const router = useRouter(); + + const data = { + project_name: '위프로', + project_profile: 'https://picsum.photos/200', + member_length: 6, + }; + + const onRequestClose = () => { + setVisible(false); + router.replace('/project'); + }; + return ( - + + + setVisible(true)}>초대 링크 있음 Project - + ); } diff --git a/app/(beforeLogin)/onboarding.tsx b/app/(beforeLogin)/onboarding.tsx index 8135df9..ab327f5 100644 --- a/app/(beforeLogin)/onboarding.tsx +++ b/app/(beforeLogin)/onboarding.tsx @@ -68,7 +68,8 @@ function Onboarding() { 다음 diff --git a/src/components/common/button/Button.style.tsx b/src/components/common/button/Button.style.tsx index ffac483..5e42a32 100644 --- a/src/components/common/button/Button.style.tsx +++ b/src/components/common/button/Button.style.tsx @@ -2,9 +2,10 @@ import styled, { type ReactNativeStyle } from '@emotion/native'; import { flexDirectionRowItemsCenter, flexItemCenter } from '@/styles/common'; -export const Container = styled.View<{ $sizeStyle: ReactNativeStyle }>` - ${({ $sizeStyle }) => $sizeStyle} +export const Container = styled.View<{ $sizeStyle: ReactNativeStyle; $full: boolean }>` + ${({ $sizeStyle }) => $sizeStyle}; position: relative; + width: ${({ $full }) => ($full ? '100%' : 'fit-content')}; overflow: hidden; border-radius: 30px; `; diff --git a/src/components/common/button/OutLineButton.stories.tsx b/src/components/common/button/OutLineButton.stories.tsx index 50c134c..a2b2219 100644 --- a/src/components/common/button/OutLineButton.stories.tsx +++ b/src/components/common/button/OutLineButton.stories.tsx @@ -20,7 +20,7 @@ const OutLineButtonMeta: Meta = { description: '버튼의 사이즈를 결정합니다.', control: { type: 'select', - options: ['full', 'large', 'medium', 'small'], + options: ['large', 'medium', 'small'], }, }, disabled: { @@ -29,11 +29,17 @@ const OutLineButtonMeta: Meta = { type: 'boolean', }, }, + full: { + description: '버튼의 가로 길이 폭을 결정합니다.', + control: { + type: 'boolean', + }, + }, }, args: { children: '버튼', type: 'primary', - size: 'full', + size: 'large', disabled: false, }, }; diff --git a/src/components/common/button/OutlineButton.tsx b/src/components/common/button/OutlineButton.tsx index f9a7fdb..251c5d6 100644 --- a/src/components/common/button/OutlineButton.tsx +++ b/src/components/common/button/OutlineButton.tsx @@ -22,20 +22,13 @@ const typeStyle: Record = { }; const sizeStyle: Record = { - full: css` - width: 100%; - height: 48px; - `, large: css` - width: fit-content; height: 48px; `, medium: css` - width: fit-content; height: 40px; `, small: css` - width: fit-content; height: 32px; `, }; @@ -48,19 +41,22 @@ const disabledStyle = { }; function OutLineButton({ - size = 'full', + size = 'large', type = 'primary', disabled = false, LeftIcon, RightIcon, children, + full = false, ...rest }: PropsNeedChildren) { const { textSize, iconSize } = useButtonStyle(size); const { color } = useButtonTextColor(type, disabled); return ( - + = { description: '버튼의 사이즈를 결정합니다.', control: { type: 'select', - options: ['large', 'medium', 'small', 'full'], + options: ['large', 'medium', 'small'], }, }, disabled: { @@ -29,6 +29,12 @@ const PrimaryButtonMeta: Meta = { type: 'boolean', }, }, + full: { + description: '버튼의 가로 길이 폭을 결정합니다.', + control: { + type: 'boolean', + }, + }, }, args: { children: '버튼', diff --git a/src/components/common/button/SolidButton.tsx b/src/components/common/button/SolidButton.tsx index 8c5daad..6c2be3c 100644 --- a/src/components/common/button/SolidButton.tsx +++ b/src/components/common/button/SolidButton.tsx @@ -19,20 +19,13 @@ const typeStyle: Record = { }; const sizeStyle: Record = { - full: css` - width: 100%; - height: 48px; - `, large: css` - width: fit-content; height: 48px; `, medium: css` - width: fit-content; height: 40px; `, small: css` - width: fit-content; height: 32px; `, }; @@ -45,9 +38,10 @@ const disabledStyle = { }; function SolidButton({ - size = 'full', + size = 'large', type = 'primary', disabled = false, + full = false, LeftIcon, RightIcon, children, @@ -56,7 +50,9 @@ function SolidButton({ const { textSize, iconSize } = useButtonStyle(size); const color = disabled ? disabledStyle.color : '#FFFFFF'; return ( - + {type === 'primary' && !disabled && ( = { description: '버튼의 사이즈를 결정합니다.', control: { type: 'select', - options: ['full', 'large', 'medium', 'small'], + options: ['large', 'medium', 'small'], }, }, disabled: { @@ -33,7 +33,7 @@ const TextButtonMeta: Meta = { args: { children: '버튼', type: 'primary', - size: 'full', + size: 'large', disabled: false, }, }; diff --git a/src/components/common/button/TextButton.tsx b/src/components/common/button/TextButton.tsx index 30614a1..e46082c 100644 --- a/src/components/common/button/TextButton.tsx +++ b/src/components/common/button/TextButton.tsx @@ -8,14 +8,14 @@ import type { PropsNeedChildren } from '@/types'; import * as S from './Button.style'; function TextButton({ - size = 'full', + size = 'large', type = 'primary', disabled = false, LeftIcon, RightIcon, children, ...rest -}: PropsNeedChildren) { +}: PropsNeedChildren>) { const { textSize, iconSize } = useButtonStyle(size); const { color } = useButtonTextColor(type, disabled); diff --git a/src/components/common/button/button.type.ts b/src/components/common/button/button.type.ts index e74b278..c990c4b 100644 --- a/src/components/common/button/button.type.ts +++ b/src/components/common/button/button.type.ts @@ -5,7 +5,8 @@ import type { IconProps } from '@/types'; export type CustomButtonProps = { type: 'primary' | 'secondary'; - size: 'large' | 'medium' | 'small' | 'full'; + size: 'large' | 'medium' | 'small'; + full: boolean; disabled: boolean; }; diff --git a/src/components/common/typography/index.tsx b/src/components/common/typography/index.tsx index 656c93a..0b1306d 100644 --- a/src/components/common/typography/index.tsx +++ b/src/components/common/typography/index.tsx @@ -8,6 +8,7 @@ type Props = TextProps & { variant: keyof typeof TypographyStyle; fontWeight: keyof typeof FontWeightStyle; color: string; + breakWord?: boolean; }; const FontWeightStyle = { @@ -92,13 +93,19 @@ const TypographyStyle = { `, }; +const breakWordText = css` + width: 100%; + word-wrap: break-word; +`; + const CustomText = styled.Text` - ${({ variant }) => TypographyStyle[variant]} + ${({ variant }) => TypographyStyle[variant]}; ${({ fontWeight }) => fontWeight === 'semiBold' && !isMobile ? FontWeightStyle['normal'] - : FontWeightStyle[fontWeight]} - ${({ color }) => (color ? `color: ${color};` : '')} + : FontWeightStyle[fontWeight]}; + ${({ color }) => (color ? `color: ${color};` : '')}; + ${({ breakWord }) => breakWord && breakWordText} `; /** @@ -107,6 +114,7 @@ const CustomText = styled.Text` * @param variant 텍스트의 크기, 자간, 줄간 등의 스타일을 지정합니다. * @param fontWeight 글씨 굵기 속성을 지정합니다. * @param color 글씨의 색깔을 선택합니다. + * @param breakWord 글씨의 줄바꿈을 제거합니다. * @param rest 나머지 추가 속성들을 받아옵니다. * @constructor */ @@ -115,6 +123,7 @@ function Typography({ variant = 'Label1/Normal', fontWeight = 'normal', color = 'black', + breakWord = false, ...rest }: Partial) { return ( @@ -122,6 +131,7 @@ function Typography({ variant={variant} fontWeight={fontWeight} color={color} + breakWord={breakWord} {...rest}> {children} diff --git a/src/components/project/ProjectInviteModal.stories.tsx b/src/components/project/ProjectInviteModal.stories.tsx new file mode 100644 index 0000000..3a4f2da --- /dev/null +++ b/src/components/project/ProjectInviteModal.stories.tsx @@ -0,0 +1,40 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { useState } from 'react'; +import { View } from 'react-native'; + +import SolidButton from '@/components/common/button/SolidButton'; +import ProjectInviteModal from '@/components/project/ProjectInviteModal'; + +const ProjectInviteModalMeta: Meta = { + title: 'project/ProjectInviteModal', + component: ProjectInviteModal, + argTypes: {}, +}; + +export default ProjectInviteModalMeta; + +export const Primary: StoryObj = { + args: { + project_name: '위프로', + project_profile: 'https://picsum.photos/200', + member_length: 6, + }, + render: (args) => { + const [show, setShow] = useState(false); + + const onRequestClose = () => { + setShow(false); + }; + + return ( + + setShow(true)}>버튼 열기 + + + ); + }, +}; diff --git a/src/components/project/ProjectInviteModal.style.ts b/src/components/project/ProjectInviteModal.style.ts new file mode 100644 index 0000000..7bb6b78 --- /dev/null +++ b/src/components/project/ProjectInviteModal.style.ts @@ -0,0 +1,61 @@ +import styled from '@emotion/native'; + +import { flexDirectionColumn, flexDirectionColumnItemsCenter } from '@/styles/common'; +import { color } from '@/styles/theme'; +import { getSize } from '@/utils'; + +export const Container = styled.View` + ${flexDirectionColumnItemsCenter}; + flex: 1; + background: ${color.Background.Alternative}; +`; + +export const Contents = styled.View` + ${flexDirectionColumnItemsCenter}; + gap: 20px; + align-items: center; + width: ${getSize.screenWidth - 40 + 'px'}; + height: fit-content; + padding: 36px 32px; + background: #fff; + border-radius: 8px; +`; + +export const ProjectBox = styled.View` + ${flexDirectionColumnItemsCenter}; + gap: 12px; +`; + +export const ProjectImageBox = styled.View` + ${flexDirectionColumnItemsCenter}; + position: relative; + width: 48px; + height: 48px; + border-radius: 8px; +`; + +export const ProjectImageOutline = styled.View` + position: absolute; + width: 100%; + height: 100%; + border: 1px solid rgb(115 112 114); + border-radius: 8px; +`; + +export const ProjectImage = styled.Image` + position: relative; + width: 100%; + height: 100%; + border-radius: 8px; +`; + +export const TextBox = styled.View` + ${flexDirectionColumn}; + gap: 2px; +`; + +export const ButtonBox = styled.View` + ${flexDirectionColumnItemsCenter}; + gap: 12px; + width: 100%; +`; diff --git a/src/components/project/ProjectInviteModal.tsx b/src/components/project/ProjectInviteModal.tsx new file mode 100644 index 0000000..a125c5d --- /dev/null +++ b/src/components/project/ProjectInviteModal.tsx @@ -0,0 +1,72 @@ +import { Modal } from 'react-native'; + +import SolidButton from '@/components/common/button/SolidButton'; +import Typography from '@/components/common/typography'; +import { color } from '@/styles/theme'; + +import * as S from './ProjectInviteModal.style'; + +type ModalType = { + visible: boolean; + /** 사용자가 뒤로가기 버튼을 눌렀을 때 호출될 함수 */ + onRequestClose: () => void; +}; + +type ProjectType = { + project_name: string; + project_profile: string; + member_length: number; +} & ModalType; + +function ProjectInviteModal({ + project_name, + project_profile, + member_length, + visible, + onRequestClose, +}: ProjectType) { + return ( + + + + 프로젝트에 초대되었어요! + + + + {null} + + + + {project_name} + + + 멤버 {member_length}명 + + + + + 수락하기 + + 거절하기 + + + + + + ); +} + +export default ProjectInviteModal; diff --git a/src/styles/common.ts b/src/styles/common.ts index 76ef28a..d185cb7 100644 --- a/src/styles/common.ts +++ b/src/styles/common.ts @@ -11,6 +11,19 @@ export const flexDirectionColumn = css` flex-direction: column; `; +export const flexDirectionColumnCenter = css` + display: flex; + flex-direction: column; + justify-content: center; +`; + +export const flexDirectionColumnItemsCenter = css` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +`; + export const flexDirectionRow = css` display: flex; flex-direction: row;