Skip to content

Commit

Permalink
[Feat] 타임블록 바 퍼블리싱 (#328)
Browse files Browse the repository at this point in the history
* feat: 기초 구조 잡기

* fix: command button secondary 스타일 추가

* docs: svg 파일 추가

* refactor: commandButton style 추가

* feat: timeblock 업로드된 파일 item 구현

* feat: TimeBlock 업로드된 파일 컴포넌트 구현

* feat: TimeBlock 태그된 인수인계 노트 item 구현

* feat: TimeBlock 태그된 인수인계 노트 컴포넌트 구현

* feat: TimeBlock info 구현

* feat: TimeBlockBar 컴포넌트 구현

* feat: TimeBlockDrawer 컴포넌트 구현

* feat: TimeBlockBar close 버튼 기능 구현

* feat: 수정하기 뷰 구현

* fix: textOverflow 에러 해결

* fix: button 공컴 disabled 시 cursor 이벤트 없애기

* style: cursor pointer

* refactor: 잔잔바리 수정

* fix: 버튼 공컴 정상화

* fix: 코드리뷰 반영
  • Loading branch information
rtttr1 authored Nov 20, 2024
1 parent ca7b688 commit 5b075d9
Show file tree
Hide file tree
Showing 26 changed files with 417 additions and 25 deletions.
4 changes: 2 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import { useQueryErrorResetBoundary } from '@tanstack/react-query';
import ErrorBoundary from '@/common/component/ErrorBoundary/ErrorBoundary';

import { HTTPError } from '@/shared/api/HTTPError';
import GlobalDrawer from '@/shared/component/GlobalDrawer/GlobalDrawer';
import Header from '@/shared/component/Header/Header';
import ModalContainer from '@/shared/component/Modal/ModalContainer';
import SideNavBar from '@/shared/component/SideNavBar/SideNavBar';
import TimeBlockDrawer from '@/shared/component/TimeBlockDrawer/TimeBlockDrawer';
import { HTTP_STATUS_CODE } from '@/shared/constant/api';
import { PATH } from '@/shared/constant/path';
import ErrorPage from '@/shared/page/errorPage/ErrorPage';
Expand Down Expand Up @@ -54,7 +54,7 @@ const App = () => {
<Header />
<Outlet />
</main>
<GlobalDrawer />
<TimeBlockDrawer />
</div>
</ErrorBoundary>
);
Expand Down
3 changes: 3 additions & 0 deletions src/common/asset/svg/ic_close.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/common/asset/svg/ic_cloud_upload.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/common/asset/svg/ic_group.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/common/asset/svg/ic_note_black.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/common/asset/svg/ic_paper.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/common/asset/svg/ic_subdirectory_arrow_right.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions src/common/component/Button/Button.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export const buttonStyle = css({
'&:disabled:not(:focus)': {
backgroundColor: theme.colors.gray_100,
color: theme.colors.gray_500,

cursor: 'default',
},

transition: 'all .2s ease-in',
Expand Down Expand Up @@ -53,6 +55,14 @@ export const variantStyle = (variant: Required<ButtonProps>['variant']) => {
backgroundColor: theme.colors.gray_200,
},
}),
fourth: css({
color: theme.colors.gray_800,
backgroundColor: theme.colors.white,

'&:hover': {
backgroundColor: theme.colors.gray_100,
},
}),
outline: css({
color: theme.colors.gray_800,
backgroundColor: theme.colors.white,
Expand Down
2 changes: 1 addition & 1 deletion src/common/component/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Size } from '@/common/type/design';
import { buttonStyle, sizeStyle, variantStyle } from './Button.style';

export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'tertiary' | 'outline' | 'text';
variant?: 'primary' | 'secondary' | 'tertiary' | 'fourth' | 'outline' | 'text';
size?: Extract<Size, 'xLarge' | 'large' | 'medium' | 'small' | 'xSmall'>;
}

Expand Down
19 changes: 18 additions & 1 deletion src/common/component/CommandButton/CommandButton.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ export const commonStyle = css({
});

export const keyStyle = (
variant: Extract<'primary' | 'tertiary' | 'outline', Omit<ButtonProps, 'underline' | 'secondary'>['variant']>
variant: Extract<
'primary' | 'tertiary' | 'fourth' | 'outline',
Omit<ButtonProps, 'underline' | 'secondary'>['variant']
>
) => {
const style = {
primary: css({
Expand All @@ -59,6 +62,14 @@ export const keyStyle = (
color: theme.colors.gray_500,
},
}),
fourth: css({
backgroundColor: theme.colors.gray_100,

'&:disabled:not(:focus)': {
backgroundColor: theme.colors.gray_100,
color: theme.colors.gray_500,
},
}),
outline: css({
backgroundColor: theme.colors.gray_100,

Expand Down Expand Up @@ -92,6 +103,12 @@ export const sizeStyle = (size: Required<CommandButtonProps>['size']) => {
small: css({
padding: '0.7rem 1rem 0.7rem 1.4rem',

...theme.text.body08,
}),
/** Button_24 */
xSmall: css({
padding: '0.6rem 1rem',

...theme.text.body08,
}),
};
Expand Down
4 changes: 2 additions & 2 deletions src/common/component/CommandButton/CommandButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import {
} from '@/common/component/CommandButton/CommandButton.style';

export interface CommandButtonProps extends ButtonProps {
variant?: Extract<ButtonProps['variant'], 'primary' | 'tertiary' | 'outline'>;
size?: Extract<ButtonProps['size'], 'large' | 'small'>;
variant?: Extract<ButtonProps['variant'], 'primary' | 'tertiary' | 'fourth' | 'outline'>;
size?: Extract<ButtonProps['size'], 'large' | 'small' | 'xSmall'>;
commandKey: string;
isCommand?: boolean;
isFrontIcon?: boolean;
Expand Down
8 changes: 6 additions & 2 deletions src/page/archiving/index/ArchivingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,12 @@ const ArchivingPage = () => {
/** TODO: 추후 block id에 따른 API 응답으로 데이터 넣기 */
openDrawer({
title: selectedBlockFromDashboard.name,
startDate: '2024-09-13',
endDate: '2024-09-24',
startDate: new Date('2024-09-13'),
endDate: new Date('2024-09-24'),
blockType: 'MEETING',
color: '#FFE6E8',
taggedMembers: [],
handoverNotes: [],
files: [],
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { css } from '@emotion/react';

import { theme } from '@/common/style/theme/theme';

export const periodStyle = css({
color: theme.colors.gray_800,
});

export const titleInputStyle = css({
width: '21.4rem',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import DatePicker from '@/common/component/DatePicker';
import Flex from '@/common/component/Flex/Flex';
import Heading from '@/common/component/Heading/Heading';
import Input from '@/common/component/Input/Input';
import Text from '@/common/component/Text/Text';

import { periodStyle, titleInputStyle } from '@/page/archiving/index/component/TimeBlockBar/BlockInfo/BlockInfo.style';
import { formattingDate } from '@/page/archiving/index/util/date';

interface BlockInfoProps {
title: string;
startDate: Date;
endDate: Date;
isEditable: boolean;
}

const BlockInfo = ({ title, startDate, endDate, isEditable }: BlockInfoProps) => {
return (
<Flex styles={{ direction: 'column', gap: '1rem', marginTop: '1.8rem', width: '100%' }}>
{isEditable ? <Input css={titleInputStyle} value={title} /> : <Heading tag="H6">{title}</Heading>}

{isEditable ? (
<DatePicker variant="range" triggerWidth="100%" />
) : (
<Text tag="body6" css={periodStyle}>
{formattingDate(startDate)} ~ {formattingDate(endDate)}
</Text>
)}
</Flex>
);
};

export default BlockInfo;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { css } from '@emotion/react';

export const handoverNoteItemStyle = css({
display: 'block',

maxWidth: '20.6rem',

whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import IcArrowRight from '@/common/asset/svg/ic_subdirectory_arrow_right.svg?react';
import Button from '@/common/component/Button/Button';
import Flex from '@/common/component/Flex/Flex';

import { handoverNoteItemStyle } from '@/page/archiving/index/component/TimeBlockBar/TaggedNotes/TaggedNoteItem/TaggedNoteItem.style';

interface TaggedNoteItemProps {
title: string;
isEditable: boolean;
}

const TaggedNoteItem = ({ title, isEditable }: TaggedNoteItemProps) => {
return (
<Flex tag="li" styles={{ gap: '0.8rem', width: '100%' }} style={{ overflow: 'hidden' }}>
<IcArrowRight width={16} height={16} />
<Button disabled={isEditable} variant="outline" size="xSmall" css={handoverNoteItemStyle}>
{title}
</Button>
</Flex>
);
};

export default TaggedNoteItem;
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import IcNote from '@/common/asset/svg/ic_note_black.svg?react';
import Flex from '@/common/component/Flex/Flex';
import Text from '@/common/component/Text/Text';

import TaggedNoteItem from '@/page/archiving/index/component/TimeBlockBar/TaggedNotes/TaggedNoteItem/TaggedNoteItem';
import { listHeaderStyle } from '@/page/archiving/index/component/TimeBlockBar/TimeBlockBar.style';

interface TaggedNotesProps {
isEditable: boolean;
}

// TODO: API 추가
const HANDOVER_NOTE_LIST = [
{ id: 1, title: '세상에서 제일 긴 제목을 지을거에요 나는 하하하하하하하ㅏ핳하' },
{ id: 2, title: 'OT 인수인계' },
{ id: 3, title: '엄마 나는 토스에 가고 싶어요' },
];

const TaggedNotes = ({ isEditable }: TaggedNotesProps) => {
return (
<Flex styles={{ direction: 'column', gap: '1.8rem' }}>
<Flex styles={{ gap: '0.8rem', align: 'center' }}>
<IcNote width={16} height={16} />
<Text tag="body6" css={listHeaderStyle}>
태그된 인수인계 노트
</Text>
</Flex>
<Flex tag="ul" styles={{ direction: 'column', gap: '0.8rem' }}>
{HANDOVER_NOTE_LIST.map((data) => (
<TaggedNoteItem key={data.id} title={data.title} isEditable={isEditable} />
))}
</Flex>
</Flex>
);
};

export default TaggedNotes;
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { css } from '@emotion/react';

import { theme } from '@/common/style/theme/theme';

export const closeBtnStyle = css({
position: 'absolute',

right: '2.2rem',
top: '3.2rem',

cursor: 'pointer',
});

export const circleStyle = (color: string) =>
css({
alignItems: 'center',
justifyContent: 'center',

padding: '0.6rem',

borderRadius: '18px',
backgroundColor: color,
});

export const listHeaderStyle = css({
color: theme.colors.gray_800,
});
56 changes: 56 additions & 0 deletions src/page/archiving/index/component/TimeBlockBar/TimeBlockBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { useState } from 'react';

import CloseButton from '@/common/asset/svg/ic_close.svg?react';
import CommandButton from '@/common/component/CommandButton/CommandButton';
import Flex from '@/common/component/Flex/Flex';

import BlockInfo from '@/page/archiving/index/component/TimeBlockBar/BlockInfo/BlockInfo';
import TaggedNotes from '@/page/archiving/index/component/TimeBlockBar/TaggedNotes/TaggedNotes';
import { circleStyle, closeBtnStyle } from '@/page/archiving/index/component/TimeBlockBar/TimeBlockBar.style';
import UploadedFiles from '@/page/archiving/index/component/TimeBlockBar/UploadedFiles/UploadedFiles';
import { BLOCK_ICON } from '@/page/archiving/index/constant/icon';

import { DrawerContent } from '@/shared/store/drawer';

interface TimeBlockBarProps {
content: DrawerContent;
onCloseDrawer: () => void;
}

const TimeBlockBar = ({ content, onCloseDrawer }: TimeBlockBarProps) => {
const [isEditable, setIsEditable] = useState(false);

const handleEditClick = () => {
setIsEditable((prevState) => !prevState);
};

return (
content && (
<section>
<CloseButton width={16} height={16} css={closeBtnStyle} onClick={onCloseDrawer} />

<Flex styles={{ justify: 'space-between', marginTop: '7.4rem' }}>
<Flex css={circleStyle(content.color)}>
{BLOCK_ICON.find((icon) => icon.name === content.blockType)?.icon(content.color)}
</Flex>
<CommandButton variant="fourth" commandKey={isEditable ? 'S' : 'E'} size="xSmall" onClick={handleEditClick}>
{isEditable ? '저장' : '수정하기'}
</CommandButton>
</Flex>

<Flex styles={{ direction: 'column', gap: '3.6rem' }}>
<BlockInfo
title={content.title}
startDate={content.startDate}
endDate={content.endDate}
isEditable={isEditable}
/>
<TaggedNotes isEditable={isEditable} />
<UploadedFiles isEditable={isEditable} />
</Flex>
</section>
)
);
};

export default TimeBlockBar;
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { css } from '@emotion/react';

import { theme } from '@/common/style/theme/theme';

export const containerStyle = css({
gap: '1.2rem',
alignItems: 'center',
justifyContent: 'space-between',

width: '100%',
padding: '0.6rem',

backgroundColor: theme.colors.gray_100,
borderRadius: '8px',

cursor: 'pointer',

'&:hover': {
backgroundColor: theme.colors.gray_200,
},
});

export const circleStyle = css({
justifyContent: 'center',
alignItems: 'center',

padding: '0.8rem',

backgroundColor: theme.colors.white,
borderRadius: '16px',
});

export const fileTitleStyle = css({
maxWidth: '15rem',

whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
});

export const fileCapacityStyle = css({
color: theme.colors.gray_800,
});

export const closeBtnStyle = css({
cursor: 'pointer',
});
Loading

0 comments on commit 5b075d9

Please sign in to comment.