-
Notifications
You must be signed in to change notification settings - Fork 2
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
The head ref may contain hidden characters: "feat/fe/\uCE98\uB9B0\uB354-\uBC14-\uB4DC\uB798\uADF8"
Changes from 50 commits
c26c9d6
526e9b6
bf67149
463fcce
70c440a
c8c26c1
21889ee
7a92a9c
7005975
b098b85
461812b
ff09a1f
3f862db
dcb471e
a8e6e5a
5727b05
4b52e5b
488fbeb
2ed57cf
b532eaa
dc58954
f04465a
17eef10
d5a39c2
26b74c9
b8d7223
8fd537e
411cd4b
bcdbacf
c4d5c9c
bcc4831
a7a08da
c019bc1
4ec43c0
f9df389
43fb55d
6d31c42
83b7a6e
15c1b7a
fdf5c1d
421fdad
d099bd4
c431986
e15702f
58409c6
0be7107
4981756
ea37ebe
82d2416
09cf84b
e54a224
31dadee
6e5e258
28cb380
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 |
---|---|---|
@@ -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 { | ||
visible: boolean; | ||
initX: number; | ||
initY: number; | ||
calendarSize: CalendarSize; | ||
year: number; | ||
month: number; | ||
level: number; | ||
schedule: Schedule; | ||
onMouseUp: ( | ||
title: string, | ||
startDateTime: Schedule['startDateTime'], | ||
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. 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. 아니, 단순히 |
||
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; |
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.
...dragStaus로 보내고 있으면 dragStatus 객체로 받는게 더 깔끔하지 않나?
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.
나도 그렇게 생각하고, 반영해 둘게!