1
1
import './component.css'
2
- import { Bookmark , BookmarkFolder , FolderCollapse , filterBookmarks } from './model'
3
- import React , { FC , PropsWithChildren , useState } from 'react'
4
- import { useBookmarkFolders , useFolderCollapse } from './repository'
2
+ import { Bookmark , BookmarkFolder , FolderCollapse , Position , filterBookmarks } from './model'
3
+ import { BookmarkWithDragProps , Drag , reorderBookmarks } from './viewmodel'
4
+ import React , { Dispatch , FC , PropsWithChildren , useState } from 'react'
5
+ import { moveBookmark , useBookmarkFolders , useFolderCollapse } from './repository'
5
6
import BookmarkEditorComponent from '../BookmarkEditor/component'
6
7
import { EditingBookmark } from '../BookmarkEditor/model'
7
8
import Link from '../Link/component'
@@ -37,12 +38,13 @@ type BookmarkFoldersComponentProps = {
37
38
const BookmarkFoldersComponent : FC < BookmarkFoldersComponentProps > = ( { bookmarkFolders, shortcutMap, search } ) => {
38
39
const [ toggles ] = useToggles ( )
39
40
const [ folderCollapse , setFolderCollapse ] = useFolderCollapse ( )
41
+ const [ drag , setDrag ] = useState < Drag > ( )
40
42
return (
41
43
< div className = "BookmarkFolders" >
42
44
{ bookmarkFolders . map ( ( f , i ) => (
43
45
< BookmarkFolderIndent key = { i } depth = { toggles . indent ? f . depth : 0 } >
44
46
< BookmarkFolderCollapse folder = { f } folderCollapse = { folderCollapse } setFolderCollapse = { setFolderCollapse } >
45
- < BookmarkFolderItems folder = { f } shortcutMap = { shortcutMap } search = { search } />
47
+ < BookmarkFolderItems folder = { f } shortcutMap = { shortcutMap } search = { search } drag = { drag } setDrag = { setDrag } />
46
48
</ BookmarkFolderCollapse >
47
49
</ BookmarkFolderIndent >
48
50
) ) }
@@ -111,31 +113,109 @@ type BookmarkFolderItemsProps = {
111
113
folder : BookmarkFolder
112
114
shortcutMap : ShortcutMap
113
115
search : string
116
+ drag : Drag | undefined
117
+ setDrag : Dispatch < Drag | undefined >
114
118
}
115
119
116
- const BookmarkFolderItems : FC < BookmarkFolderItemsProps > = ( { folder, shortcutMap, search } ) => {
117
- const bookmarks = filterBookmarks ( folder . bookmarks , search )
120
+ const BookmarkFolderItems : FC < BookmarkFolderItemsProps > = ( { folder, shortcutMap, search, drag , setDrag } ) => {
121
+ const bookmarks = reorderBookmarks ( drag , folder . id , filterBookmarks ( folder . bookmarks , search ) )
118
122
return (
119
123
< >
120
- { bookmarks . map ( ( b ) => (
121
- < BookmarkComponent key = { b . id } bookmark = { b } shortcutMap = { shortcutMap } />
124
+ { bookmarks . map ( ( b , index ) => (
125
+ < BookmarkDragDrop
126
+ key = { b . id }
127
+ bookmark = { b }
128
+ position = { { folderID : folder . id , index } }
129
+ drag = { drag }
130
+ setDrag = { setDrag }
131
+ >
132
+ < BookmarkComponent bookmark = { b } shortcutMap = { shortcutMap } dragActive = { drag ? true : undefined } />
133
+ </ BookmarkDragDrop >
122
134
) ) }
123
135
</ >
124
136
)
125
137
}
126
138
139
+ const classNameOfMap = ( classNameMap : { [ className : string ] : boolean | undefined } ) =>
140
+ Object . entries ( classNameMap )
141
+ . filter ( ( [ , enabled ] ) => enabled === true )
142
+ . map ( ( [ className ] ) => className )
143
+ . join ( ' ' )
144
+
145
+ type BookmarkDragDropProps = {
146
+ bookmark : BookmarkWithDragProps
147
+ position : Position
148
+ drag : Drag | undefined
149
+ setDrag : Dispatch < Drag | undefined >
150
+ } & PropsWithChildren
151
+
152
+ const BookmarkDragDrop : FC < BookmarkDragDropProps > = ( { bookmark, position, drag, setDrag, children } ) => {
153
+ return (
154
+ < div
155
+ className = { classNameOfMap ( {
156
+ Bookmark__DragDrop__From : bookmark . dragFrom ,
157
+ Bookmark__DragDrop__To : bookmark . dragTo ,
158
+ Bookmark__DragDrop__Hover : bookmark . hover ,
159
+ } ) }
160
+ onDragStart = { ( e ) => {
161
+ setDrag ( Drag . start ( bookmark , position ) )
162
+ e . dataTransfer . effectAllowed = 'move'
163
+ e . dataTransfer . setData ( 'text/plain' , bookmark . url )
164
+ } }
165
+ onDragOver = { ( e ) => {
166
+ if ( drag ) {
167
+ e . preventDefault ( )
168
+ }
169
+ } }
170
+ onDragEnter = { ( e ) => {
171
+ if ( drag ) {
172
+ e . preventDefault ( )
173
+ setDrag ( drag . enterTo ( position ) )
174
+ }
175
+ } }
176
+ onDragLeave = { ( e ) => {
177
+ // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/relatedTarget
178
+ const exitedFrom = e . target
179
+ const enteredTo = e . relatedTarget
180
+ if (
181
+ drag &&
182
+ exitedFrom instanceof HTMLElement &&
183
+ exitedFrom . classList . contains ( 'BookmarkButton' ) &&
184
+ enteredTo instanceof HTMLElement &&
185
+ enteredTo . classList . contains ( 'BookmarkFolder' )
186
+ ) {
187
+ setDrag ( drag . leave ( ) )
188
+ }
189
+ } }
190
+ onDragEnd = { ( ) => {
191
+ setDrag ( undefined )
192
+ } }
193
+ onDrop = { ( e ) => {
194
+ e . preventDefault ( )
195
+ if ( drag ) {
196
+ moveBookmark ( drag . bookmark , drag . calculateDestination ( ) ) . catch ( console . error )
197
+ setDrag ( undefined )
198
+ }
199
+ } }
200
+ >
201
+ { children }
202
+ </ div >
203
+ )
204
+ }
205
+
127
206
type BookmarkComponentProps = {
128
207
bookmark : Bookmark
129
208
shortcutMap : ShortcutMap
209
+ dragActive : true | undefined
130
210
}
131
211
132
- const BookmarkComponent : FC < BookmarkComponentProps > = ( { bookmark, shortcutMap } ) => {
212
+ const BookmarkComponent : FC < BookmarkComponentProps > = ( { bookmark, shortcutMap, dragActive } ) => {
133
213
const [ editingBookmark , setEditingBookmark ] = useState < EditingBookmark > ( )
134
214
const shortcutKey = shortcutMap . getByBookmarkID ( bookmark . id )
135
215
return (
136
216
< div className = "Bookmark" >
137
217
< Link href = { bookmark . url } >
138
- < div className = "BookmarkButton" >
218
+ < div className = "BookmarkButton" data-drag-active = { dragActive } draggable >
139
219
< div className = "BookmarkButton__Title" > { bookmark . title } </ div >
140
220
< img className = "BookmarkButton__Icon" alt = "" src = { faviconImage ( bookmark . url ) } />
141
221
{ shortcutKey ? < div className = "BookmarkButton__Badge" > { shortcutKey } </ div > : null }
@@ -144,6 +224,7 @@ const BookmarkComponent: FC<BookmarkComponentProps> = ({ bookmark, shortcutMap }
144
224
< a
145
225
href = "#Edit"
146
226
className = "BookmarkEditButton"
227
+ data-drag-active = { dragActive }
147
228
onClick = { ( e ) => {
148
229
setEditingBookmark ( new EditingBookmark ( bookmark , shortcutKey ) )
149
230
e . preventDefault ( )
0 commit comments