Skip to content
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

[FE] 스케줄 바를 드래그하여 수정할 수 있는 기능 구현 #857

Merged
merged 54 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
c26c9d6
feat: 캘린더 드래그가 진행될 가짜 드래그 화면 컴포넌트 생성, backdrop 화면이 나타나도록 구현
wzrabbit Nov 6, 2023
526e9b6
feat: CalendarDragScreen을 캘린더 컴포넌트에 부착
wzrabbit Nov 6, 2023
bf67149
feat: 캘린더 컴포넌트에서 컨트롤할 수 있도록 스케줄 바 드래그 현황을 관리하는 커스텀 훅 추가
wzrabbit Nov 6, 2023
463fcce
refactor: 스케줄 바 컴포넌트 드래그 시작 시 이벤트 객체를 함께 넘기도록 변경
wzrabbit Nov 6, 2023
70c440a
feat: 좌표를 의미하는 Point 타입 추가
wzrabbit Nov 6, 2023
c8c26c1
feat: 움직이는 스케줄 바 컴포넌트 구현
wzrabbit Nov 6, 2023
21889ee
test: 움직이는 스케줄 바 컴포넌트에 대한 스토리북 작성
wzrabbit Nov 6, 2023
7a92a9c
feat: useCalendarDragScreen 커스텀 훅에 상대좌표 업데이트 기능 및 스케줄 바로의 변환 기능 추가
wzrabbit Nov 6, 2023
7005975
feat: 가짜 스크롤 기능 구현을 위해 커스텀 훅과 캘린더 컴포넌트의 로직을 결합
wzrabbit Nov 6, 2023
b098b85
feat: 캘린더의 크기에 따라 CalendarDragScreen에 표시되는 스케줄 바의 크기가 반영되도록 개선
wzrabbit Nov 6, 2023
461812b
feat: 초기 좌표를 CalendarDragScreen 자체에서 관리하고, 커스텀 훅 내부에서 절대 좌표를 사용할 수 있도…
wzrabbit Nov 7, 2023
ff09a1f
feat: 비활성 상태일 경우에도 unmount 되지 않도록 변경
wzrabbit Nov 8, 2023
3f862db
feat: 캘린더 크기와 상대좌표를 이용해 캘린더 칸 이동 수치를 구하는 유틸 함수 작성
wzrabbit Nov 8, 2023
dcb471e
feat: 날짜 포맷을 받아 n일 지난 날짜 포맷을 리턴해 주는 유틸 함수 작성
wzrabbit Nov 8, 2023
a8e6e5a
feat: 움직이는 스케줄 바에 대한 정보를 생성하는 유틸 함수 작성
wzrabbit Nov 8, 2023
5727b05
feat: 절대 좌표 방식에 의존하지 않도록 개선
wzrabbit Nov 8, 2023
4b52e5b
feat: ScheduleBar에서 초기 좌표를 전달하는 방식으로 재변경
wzrabbit Nov 8, 2023
488fbeb
feat: ScheduleBar에 mode 타입 추가
wzrabbit Nov 8, 2023
2ed57cf
feat: ScheduleBar 컴포넌트에 mode 프로퍼티를 추가, no-interaction 모드에 대한 기능을 구현
wzrabbit Nov 8, 2023
b532eaa
test: ScheduleBar 스토리북에 no-interaction인 경우에 해당하는 스토리를 추가
wzrabbit Nov 8, 2023
dc58954
feat: MovingScheduleBar의 스케줄 바는 상호작용이 불가능하도록 유틸함수 개선
wzrabbit Nov 8, 2023
f04465a
feat: ScheduleBar에서 indicator 모드에 대한 기능을 구현
wzrabbit Nov 8, 2023
17eef10
test: ScheduleBar 스토리북에 indicator인 경우에 해당하는 스토리를 추가
wzrabbit Nov 8, 2023
d5a39c2
feat: ScheduleIndicator 컴포넌트 구현
wzrabbit Nov 8, 2023
26b74c9
test: ScheduleIndicator 컴포넌트에 해당하는 스토리 작성
wzrabbit Nov 8, 2023
b8d7223
feat: 두 종류의 가짜 스케줄 바 컴포넌트를 하나의 컴포넌트로 통합
wzrabbit Nov 9, 2023
8fd537e
feat: CalendarDragScreen에 통합된 가짜 스케줄 바 컴포넌트를 부착
wzrabbit Nov 9, 2023
411cd4b
feat: 스케줄 바의 변수명 변경, 인디케이터 모드일 경우 화살표를 보여주지 않도록 수정
wzrabbit Nov 9, 2023
bcdbacf
feat: 가짜 스케줄바를 반환하는 유틸함수명 변경, 세로로 스케줄바를 이동시켜도 자연스럽게 작동하도록 개선
wzrabbit Nov 9, 2023
c4d5c9c
feat: 가짜 캘린더 바를 이동시킬 때 세로 방향으로도 이동이 자연스럽도록 개선, 전반적인 이동성을 개선
wzrabbit Nov 9, 2023
bcc4831
feat: 스크롤을 끝내면 변경된 스케줄 정보를 post형태로 요청하도록 기능 구현
wzrabbit Nov 9, 2023
a7a08da
feat: 드래그한 이후 일정이 변하지 않았을 경우, API 요청을 보내지 않도록 로직 개선
wzrabbit Nov 9, 2023
c019bc1
refactor: FakeScheduleBarScreen 컴포넌트에서 자주 바뀌는 스타일을 attrs로 받도록 변경
wzrabbit Nov 13, 2023
4ec43c0
chore: 불필요한 Point 타입 제거
wzrabbit Nov 13, 2023
f9df389
Merge branch 'develop' into feat/fe/캘린더-바-드래그
wzrabbit Nov 13, 2023
43fb55d
chore: 유틸 함수, 커스텀 훅의 파일 위치 변경
wzrabbit Nov 13, 2023
6d31c42
refactor: 캘린더 날짜의 변화값을 구하는 유틸함수의 이름명 변경, 내부 변수 상수화
wzrabbit Nov 13, 2023
83b7a6e
fix: 스케줄 바가 범위 밖으로 이동하여 수정이 되었을 때 오류가 생기는 경우를 해결
wzrabbit Nov 13, 2023
15c1b7a
refactor: 꼭 필요한 경우에만 함수가 다시 생성되도록 useCallback 사용, 불필요한 useMemo 제거
wzrabbit Nov 13, 2023
fdf5c1d
fix: 캘린더 바를 드래그할 때 아주 잠깐 잘못된 좌표로 이동하며 버벅거리는 문제 해결
wzrabbit Nov 13, 2023
421fdad
chore: 스케줄 바 생성 유틸함수의 import 경로를 절대경로로 변경
wzrabbit Nov 13, 2023
d099bd4
refactor: 스케줄 바 타입에 calendarSize 프로퍼티 추가
wzrabbit Nov 13, 2023
c431986
test: 상대좌표 기반 스케줄 바 유틸함수에 대한 좌표 대응 테스트 작성
wzrabbit Nov 13, 2023
e15702f
test: 상대좌표 기반 스케줄 바 유틸함수에 대한 캘린더 크기 대응 테스트 작성
wzrabbit Nov 14, 2023
58409c6
test: 상대좌표 기반 스케줄 바 유틸함수에 대한 부가 기능 테스트 작성
wzrabbit Nov 14, 2023
0be7107
chore: 상대좌표 기반 스케줄 바 유틸함수에 JSDoc 주석 추가
wzrabbit Nov 14, 2023
4981756
chore: useScheduleBarDragStatus 커스텀 훅을 hooks 폴더로 이동
wzrabbit Nov 14, 2023
ea37ebe
refactor: 불필요 파라미터 제거, onMouseUp의 타입 명시
wzrabbit Nov 14, 2023
82d2416
refactor: as를 사용하는 대신 빈 스케줄 데이터를 선언해 스케줄이 비었을 경우 사용하도록 변경
wzrabbit Nov 14, 2023
09cf84b
refactor: CalendarDragScreen의 backdrop 색상을 이미 선언된 색상으로 변경
wzrabbit Nov 14, 2023
e54a224
refactor: 날짜 포맷 props의 타입으로 YYYYMMDDHHMM을 사용하도록 변경
wzrabbit Dec 6, 2023
31dadee
refactor: 일정 변경 성공 시 드래그 상태에 저장되어 있는 스케줄을 빈 스케줄로 변경
wzrabbit Dec 7, 2023
6e5e258
refactor: dragStatus 타입 분리 및 드래그 스크린에서 값을 dragStatus로 받도록 변경
wzrabbit Dec 7, 2023
28cb380
refactor: 더미 스케줄 바 정보를 사용하는 대신, null 타입으로 빈 스케줄을 관리하도록 변경
wzrabbit Dec 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { styled } from 'styled-components';

export const Container = styled.div<{ $visible: boolean }>`
${({ $visible }) => !$visible && 'display: none'};
position: absolute;
overflow: hidden;
left: 0;
top: 0;

width: 100%;
height: 100%;

background-color: ${({ theme }) => theme.color.WHITE_BLUR};

cursor: all-scroll;
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import * as S from './CalendarDragScreen.styled';
import { useRef } from 'react';
import FakeScheduleBarsScreen from '~/components/team_calendar/FakeScheduleBarsScreen/FakeScheduleBarsScreen';
import type { Schedule } from '~/types/schedule';
import type { CalendarSize } from '~/types/size';
import { useCalendarDragScreen } from '~/hooks/schedule/useCalendarDragScreen';

interface CalendarDragScreenProps {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...dragStaus로 보내고 있으면 dragStatus 객체로 받는게 더 깔끔하지 않나?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나도 그렇게 생각하고, 반영해 둘게!

visible: boolean;
initX: number;
initY: number;
calendarSize: CalendarSize;
year: number;
month: number;
level: number;
schedule: Schedule;
onMouseUp: (
title: string,
startDateTime: Schedule['startDateTime'],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image
일케 안한 이유가 있어??

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아니, 단순히 YYYYMMDDHHMM 이 타입을 잊고 있었어...
이게 의미도 더 명확하게 전달되는 것 같아 보이니, 이 타입을 사용하도록 코드 수정할게!

endDateTime: Schedule['endDateTime'],
shouldUpdate: boolean,
) => void;
}

const CalendarDragScreen = (props: CalendarDragScreenProps) => {
const {
visible,
initX,
initY,
calendarSize,
year,
month,
level,
schedule,
onMouseUp,
} = props;
const calendarRef = useRef<HTMLDivElement>(null);
const { scheduleBars, relativeX, relativeY } = useCalendarDragScreen({
visible,
initX,
initY,
calendarRef,
calendarSize,
onMouseUp,
year,
month,
level,
schedule,
});

return (
<S.Container $visible={visible} ref={calendarRef}>
<FakeScheduleBarsScreen mode="indicator" scheduleBars={scheduleBars} />
<FakeScheduleBarsScreen
mode="schedule"
scheduleBars={scheduleBars}
relativeX={relativeX}
relativeY={relativeY}
/>
</S.Container>
);
};

export default CalendarDragScreen;
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import type { Meta, StoryObj } from '@storybook/react';
import type { ComponentType } from 'react';
import FakeScheduleBarsScreen from '~/components/team_calendar/FakeScheduleBarsScreen/FakeScheduleBarsScreen';
import type { GeneratedScheduleBar } from '~/types/schedule';

/**
* `FakeScheduleBarsScreen` 는 캘린더 바의 드래그 기능을 구현하기 위해 사용자에게 보여주는 가짜 캘린더 바로 구성된, 시각적인 컴포넌트입니다.
*
* `mode = schedule`일 경우, 마우스 조작을 통해 x, y 값을 계속해서 업데이트하면 마우스를 따라다니듯이 작동하도록 만들 수 있습니다. x, y 값을 변경하면서 컴포넌트의 변화를 테스트하세요.
*/
const meta = {
title: 'Schedule/FakeScheduleBarsScreen',
component: FakeScheduleBarsScreen,
tags: ['autodocs'],
decorators: [
(Story: ComponentType) => (
<div
style={{
position: 'relative',
overflow: 'hidden',
width: '600px',
height: '450px',
border: '3px solid red',
}}
>
<Story />
</div>
),
],
argTypes: {
mode: {
description:
'이 컴포넌트의 모드를 의미합니다. 사용 목적에 따라 `schedule`과 `indicator` 중 하나를 명시해 주세요.',
},
scheduleBars: {
description: '렌더링할 스케줄 바들의 정보를 의미합니다.',
},
relativeX: {
description:
'기존 좌표에서 좌우로 얼마나 이동한 위치에 렌더링 시킬 것인지를 의미합니다. 이 값이 양수이면 기존 좌표에서 수치만큼 오른쪽으로 이동하여 렌더링되고, 음수일 경우 왼쪽으로 이동하여 렌더링됩니다. 단위는 픽셀(px)입니다. **이 프로퍼티는 `mode = schedule`일 때만 사용할 수 있습니다.**',
},
relativeY: {
description:
'기존 좌표에서 상하로 얼마나 이동한 위치에 렌더링 시킬 것인지를 의미합니다. 이 값이 양수이면 기존 좌표에서 수치만큼 아래쪽으로 이동하여 렌더링되고, 음수일 경우 위쪽으로 이동하여 렌더링됩니다. 단위는 픽셀(px)입니다. **이 프로퍼티는 `mode = schedule`일 때만 사용할 수 있습니다.**',
},
},
} satisfies Meta<typeof FakeScheduleBarsScreen>;

export default meta;

type Story = StoryObj<typeof meta>;

const scheduleBars: GeneratedScheduleBar[] = [
{
id: '1',
scheduleId: 1105,
title: '바쁜 필립의 3주짜리 일정',
row: 0,
column: 1,
duration: 6,
level: 0,
roundedStart: true,
roundedEnd: false,
schedule: {
id: 1105,
title: '바쁜 필립의 3주짜리 일정',
startDateTime: '2023-06-26 00:00',
endDateTime: '2023-07-12 23:59',
},
},
{
id: '2',
scheduleId: 1105,
title: '바쁜 필립의 3주짜리 일정',
row: 1,
column: 0,
duration: 7,
level: 0,
roundedStart: false,
roundedEnd: false,
schedule: {
id: 1105,
title: '바쁜 필립의 3주짜리 일정',
startDateTime: '2023-06-26 00:00',
endDateTime: '2023-07-12 23:59',
},
},
{
id: '3',
scheduleId: 1105,
title: '바쁜 필립의 3주짜리 일정',
row: 2,
column: 0,
duration: 4,
level: 0,
roundedStart: false,
roundedEnd: true,
schedule: {
id: 1105,
title: '바쁜 필립의 3주짜리 일정',
startDateTime: '2023-06-26 00:00',
endDateTime: '2023-07-12 23:59',
},
},
];

/**
* 이 모드는 가짜 스케줄 바를 보여줘야 할 경우에 사용합니다.
*/
export const ScheduleMode: Story = {
args: {
mode: 'schedule',
scheduleBars,
relativeX: 0,
relativeY: 0,
},
};

/**
* 이 모드는 스케줄 바가 놓일 위치를 시각적으로 보여줘야 할 경우에 사용합니다.
*/
export const IndicatorMode: Story = {
args: {
mode: 'indicator',
scheduleBars,
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { styled } from 'styled-components';

export const Container = styled.div.attrs<{
$relativeX: number;
$relativeY: number;
}>(({ $relativeX, $relativeY }) => ({
style: {
transform: `translate(${$relativeX}px, ${$relativeY}px)`,
},
}))`
display: flex;
flex-direction: column;
position: absolute;

width: 100%;
height: 100%;
`;

export const CalendarRow = styled.div`
position: relative;
flex-grow: 1;
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import * as S from './FakeScheduleBarsScreen.styled';
import ScheduleBar from '~/components/team_calendar/ScheduleBar/ScheduleBar';
import { arrayOf } from '~/utils/arrayOf';
import type { GeneratedScheduleBar } from '~/types/schedule';

interface ScheduleModeProps {
mode: 'schedule';
scheduleBars: GeneratedScheduleBar[];
relativeX: number;
relativeY: number;
}

interface IndicatorModeProps {
mode: 'indicator';
scheduleBars: GeneratedScheduleBar[];
}

type FakeScheduleBarsScreenProps = ScheduleModeProps | IndicatorModeProps;

const FakeScheduleBarsScreen = (props: FakeScheduleBarsScreenProps) => {
const { mode, scheduleBars } = props;

return (
<S.Container
$relativeX={mode === 'schedule' ? props.relativeX : 0}
$relativeY={mode === 'schedule' ? props.relativeY : 0}
>
{arrayOf(6).map((_, rowIndex) => (
<S.CalendarRow key={rowIndex}>
{scheduleBars.map((scheduleBar) => {
return scheduleBar.row === rowIndex ? (
<ScheduleBar
key={scheduleBar.id}
{...scheduleBar}
mode={mode === 'schedule' ? 'no-interaction' : 'indicator'}
/>
) : null;
})}
</S.CalendarRow>
))}
</S.Container>
);
};

export default FakeScheduleBarsScreen;
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,51 @@ export const LongTitle: Story = {
onClick: () => alert('clicked!'),
},
};

/**
* `mode` 값이 `no-interaction`일 경우, 해당 캘린더 바는 오로지 장식 용도가 되며 **상호작용이 불가능**하게 됩니다. 가짜 스케줄 바 드래그 화면 등 시각적인 효과를 위해 사용할 수 있습니다.
*/
export const NoInteraction: Story = {
args: {
id: '1',
scheduleId: 1,
schedule: {
id: 1,
title: 'No Interaction',
startDateTime: '2023-07-07 05:00',
endDateTime: '2023-07-09 10:00',
},
title: 'No Interaction',
row: 1,
column: 2,
duration: 3,
level: 0,
roundedStart: true,
roundedEnd: true,
mode: 'no-interaction',
},
};

/**
* `mode` 값이 `indicator`일 경우, 해당 캘린더 바는 **상호작용이 불가능하고 캘린더 바의 윤곽만 드러내는** 시각적 요소가 됩니다. 캘린더 바가 놓일 위치를 시각적으로 표시하는 데에 사용합니다.
*/
export const Indicator: Story = {
args: {
id: '1',
scheduleId: 1,
schedule: {
id: 1,
title: 'This should not shown',
startDateTime: '2023-07-07 05:00',
endDateTime: '2023-07-09 10:00',
},
title: 'This should not shown',
row: 1,
column: 2,
duration: 3,
level: 0,
roundedStart: true,
roundedEnd: true,
mode: 'indicator',
},
};
Loading