@@ -5,58 +5,132 @@ import { PlusIcon } from '@boolti/icon';
5
5
import ShowCastInfoFormDialogContent , {
6
6
TempShowCastInfoFormInput ,
7
7
} from '../ShowCastInfoFormDialogContent' ;
8
+ import { DndContext , DragOverEvent , KeyboardSensor , MouseSensor , TouchSensor , closestCenter , useSensor , useSensors } from '@dnd-kit/core' ;
9
+ import { restrictToVerticalAxis } from '@dnd-kit/modifiers' ;
10
+ import { SortableContext , arrayMove , sortableKeyboardCoordinates , verticalListSortingStrategy } from '@dnd-kit/sortable' ;
11
+ import ShowCastInfo from '~/components/ShowCastInfo' ;
12
+ import { useCallback , useRef , useState } from 'react' ;
13
+ import { useEffect } from 'react' ;
8
14
9
- interface Props {
10
- onSave : ( value : TempShowCastInfoFormInput ) => Promise < void > ;
15
+ interface ShowCastInfoFormContentProps {
16
+ initialCastTeamList ?: TempShowCastInfoFormInput [ ] ;
17
+ onChange : ( value : TempShowCastInfoFormInput [ ] ) => void ;
11
18
}
12
19
13
- const ShowCastInfoFormContent = ( { onSave } : Props ) => {
20
+ const ShowCastInfoFormContent = ( { initialCastTeamList , onChange } : ShowCastInfoFormContentProps ) => {
14
21
const dialog = useDialog ( ) ;
15
22
16
- const onClick = ( ) => {
23
+ const [ castTeamList , setCastTeamList ] = useState < TempShowCastInfoFormInput [ ] > ( initialCastTeamList ?? [ ] )
24
+ const prevCastTeamList = useRef < string > ( JSON . stringify ( castTeamList ) )
25
+
26
+ const sensors = useSensors (
27
+ useSensor ( MouseSensor , {
28
+ activationConstraint : {
29
+ distance : 10 ,
30
+ } ,
31
+ } ) ,
32
+ useSensor ( TouchSensor , {
33
+ activationConstraint : {
34
+ delay : 0 ,
35
+ tolerance : 5 ,
36
+ } ,
37
+ } ) ,
38
+ useSensor ( KeyboardSensor , {
39
+ coordinateGetter : sortableKeyboardCoordinates ,
40
+ } )
41
+ ) ;
42
+
43
+ const castTeamDragEndHandler = useCallback ( ( event : DragOverEvent ) => {
44
+ const { active, over } = event ;
45
+
46
+ if ( active && over && over . id !== active . id ) {
47
+ setCastTeamList ( ( prev ) => {
48
+ const oldIndex = prev . findIndex ( ( { id } ) => id === active . id ) ;
49
+ const newIndex = prev . findIndex ( ( { id } ) => id === over . id ) ;
50
+
51
+ return arrayMove ( prev , oldIndex , newIndex ) ;
52
+ } ) ;
53
+ }
54
+ } , [ ] ) ;
55
+
56
+
57
+ const castAddButtonClickHandler = ( ) => {
17
58
dialog . open ( {
18
59
isAuto : true ,
19
60
title : '출연진 정보 등록' ,
20
61
content : (
21
62
< ShowCastInfoFormDialogContent
22
- onSave = { async ( value ) => {
23
- try {
24
- await onSave ( value ) ;
25
- dialog . close ( ) ;
26
- } catch {
27
- return new Promise ( ( _ , reject ) => reject ( '저장 중 오류가 발생하였습니다.' ) ) ;
28
- }
63
+ onSave = { ( castInfo ) => {
64
+ console . log ( castInfo )
65
+ setCastTeamList ( ( prev ) => [
66
+ ... prev ,
67
+ castInfo
68
+ ] )
69
+ dialog . close ( ) ;
29
70
} }
30
71
/>
31
72
) ,
32
73
} ) ;
33
74
} ;
34
75
76
+ useEffect ( ( ) => {
77
+ const stringifiedCastTeamList = JSON . stringify ( castTeamList )
78
+
79
+ if ( prevCastTeamList . current !== stringifiedCastTeamList ) {
80
+ prevCastTeamList . current = stringifiedCastTeamList
81
+ onChange ?.( castTeamList )
82
+ }
83
+ } , [ castTeamList , onChange ] )
84
+
35
85
return (
36
86
< Styled . ShowInfoFormGroup >
37
87
< Styled . ShowInfoFormGroupHeader >
38
- < Styled . ShowInfoFormGroupInfo >
39
- < Styled . ShowInfoFormTitle >
88
+ < Styled . ShowInfoFormGroupInfo style = { { marginBottom : 0 } } >
89
+ < Styled . ShowInfoFormLabel style = { { justifyContent : 'space-between' } } >
40
90
출연진 정보
41
- < Styled . MobileCastInfoRegisterButton type = "button" onClick = { onClick } >
91
+ < Styled . MobileCastInfoRegisterButton type = "button" onClick = { castAddButtonClickHandler } >
42
92
< PlusIcon />
43
93
등록하기
44
94
</ Styled . MobileCastInfoRegisterButton >
45
- </ Styled . ShowInfoFormTitle >
46
- < Styled . ShowInfoFormSubtitle >
95
+ </ Styled . ShowInfoFormLabel >
96
+ < Styled . ShowInfoFormDescription >
47
97
출연진 정보를 팀 단위로 등록해 주세요.
48
- </ Styled . ShowInfoFormSubtitle >
98
+ </ Styled . ShowInfoFormDescription >
49
99
</ Styled . ShowInfoFormGroupInfo >
50
100
< Styled . DesktopCastInfoRegisterButton
51
101
type = "button"
52
102
colorTheme = "netural"
53
- size = "bold "
103
+ size = "small "
54
104
icon = { < PlusIcon /> }
55
- onClick = { onClick }
105
+ onClick = { castAddButtonClickHandler }
56
106
>
57
107
등록하기
58
108
</ Styled . DesktopCastInfoRegisterButton >
59
109
</ Styled . ShowInfoFormGroupHeader >
110
+ < DndContext sensors = { sensors } modifiers = { [ restrictToVerticalAxis ] } collisionDetection = { closestCenter } onDragEnd = { castTeamDragEndHandler } >
111
+ < SortableContext items = { castTeamList . map ( ( info ) => info . id ) } strategy = { verticalListSortingStrategy } >
112
+ { castTeamList . map ( ( info ) => (
113
+ < ShowCastInfo
114
+ key = { info . id }
115
+ showCastInfo = { info }
116
+ onSave = { ( showCastInfoFormInput : TempShowCastInfoFormInput ) => {
117
+ setCastTeamList ( ( prev ) =>
118
+ prev . map ( ( item ) =>
119
+ item . id === info . id ? showCastInfoFormInput : item ,
120
+ )
121
+ ) ;
122
+ return new Promise ( ( resolve ) => resolve ( ) ) ;
123
+ } }
124
+ onDelete = { ( ) => {
125
+ setCastTeamList ( ( prev ) =>
126
+ prev . filter ( ( item ) => item . id !== info . id )
127
+ ) ;
128
+ return new Promise ( ( resolve ) => resolve ( ) ) ;
129
+ } }
130
+ />
131
+ ) ) }
132
+ </ SortableContext >
133
+ </ DndContext >
60
134
</ Styled . ShowInfoFormGroup >
61
135
) ;
62
136
} ;
0 commit comments