@@ -2,80 +2,94 @@ package org.scastie.client.components.fileHierarchy
22
33import org .scastie .api ._
44import japgolly .scalajs .react ._
5- import japgolly .scalajs .react .vdom . all . cls
5+ import japgolly .scalajs .react .hooks . Hooks . UseState
66import japgolly .scalajs .react .vdom .html_<^ ._
77
8- case class DragInfo (start : Boolean , end : Boolean , f : FileOrFolder )
9-
10- final case class FileOrFolderNode (file : FileOrFolder , selectedFile : String , depth : Int , selectFile : File => Callback , dragStartOrEnd : DragInfo => Callback ) {
11-
12- @ inline def render : VdomElement = FileOrFolderNode .component((file, selectedFile, depth, selectFile, dragStartOrEnd))
8+ /**
9+ * Provides information about drag over or end.
10+ *
11+ * @param end true if drag ended
12+ * @param fileOrFolder file or folder that is being dragged (end is true) or dragged over (end is false)
13+ */
14+ case class DragInfo (end : Boolean , fileOrFolder : FileOrFolder )
15+
16+ /**
17+ * Node in the file hierarchy view that represents a file or a folder.
18+ * In case of a folder we can collapse or expand the children.
19+ * It can be dragged around and dropped on another folder.
20+ *
21+ * @param fileOrFolder node that we display
22+ * @param selectedFile selected file's path in the view hierarchy
23+ * @param depth depth of the node in the hierarchy from root (used to shift the node to the right)
24+ * @param selectFile callback when user clicks on a file
25+ * @param dragOverOrEnd callback when user drags over a folder or ends dragging a file or folder
26+ */
27+ final case class FileOrFolderNode (fileOrFolder : FileOrFolder , selectedFile : String , depth : Int , selectFile : File => Callback , dragOverOrEnd : DragInfo => Callback ) {
28+
29+ @ inline def render : VdomElement = FileOrFolderNode .component((fileOrFolder, selectedFile, depth, selectFile, dragOverOrEnd))
1330}
1431
1532object FileOrFolderNode {
1633
17-
1834 val component = ScalaFnComponent .withHooks[(FileOrFolder , String , Int , File => Callback , DragInfo => Callback )]
1935
2036 .useState(true ) // isExpanded
2137 .useState(false ) // isMouseOver
2238
23- .render((props, isExpanded, isMouseOver) => {
24- val (file , s, depth, selectFile, dragStartOrEnd ) = props
39+ .render((props, isExpanded : UseState [ Boolean ] , isMouseOver : UseState [ Boolean ] ) => {
40+ val (fileOrFolder , s, depth, selectFile, dragOverOrEnd ) = props
2541
26- val icon = file match {
42+ val icon = fileOrFolder match {
43+ case _ : File => " file-o"
2744 case _ : Folder if isExpanded.value => " folder-open"
2845 case _ : Folder => " folder"
29- case _ : File => " file-o"
3046 }
3147
3248 val handleClick = (e : ReactMouseEvent ) => {
33- e.stopPropagationCB >> (file match {
34- case _ : Folder => isExpanded.modState(x => ! x)
35- case f : File => selectFile(f)
36- })
37- }
38-
39- val onDragStart = (e : ReactDragEvent ) => {
40- dragStartOrEnd(DragInfo (start = true , end = false , file))
49+ e.stopPropagation()
50+ fileOrFolder match {
51+ case f : File => selectFile(f)
52+ case _ : Folder => isExpanded.modState(x => ! x)
53+ }
4154 }
4255
4356 val onDragOver = (e : ReactDragEvent ) => {
44- isMouseOver.setState(true ).when(file.isFolder) >>
45- dragStartOrEnd(DragInfo (start = false , end = false , file))
57+ if (fileOrFolder.isFolder) {
58+ isMouseOver.setState(true ) >>
59+ dragOverOrEnd(DragInfo (end = false , fileOrFolder))
60+ } else Callback .empty
4661 }
4762
4863 val onDragEnd = (e : ReactDragEvent ) => {
49- dragStartOrEnd (DragInfo (start = false , end = true , file ))
64+ dragOverOrEnd (DragInfo (end = true , fileOrFolder ))
5065 }
5166
5267 < .div(
5368 < .div(
5469 ^ .cls := s " hierarchy-list-row " ,
55- ^ .cls := s " ${if (file .path.equals(s)) " file-selected" else " " }" ,
70+ ^ .cls := s " ${if (fileOrFolder .path.equals(s)) " file-selected" else " " }" ,
5671 ^ .cls := s " ${if (isMouseOver.value) " file-mouse-over" else " " }" ,
5772 ^ .onClick ==> handleClick,
5873 ^ .draggable := true ,
59- ^ .onDragStart ==> onDragStart,
6074 ^ .onDragEnd ==> onDragEnd,
6175 ^ .onDragOver ==> onDragOver,
6276 ^ .onDragLeave --> isMouseOver.setState(false ),
6377 ^ .onMouseOver --> isMouseOver.setState(true ),
6478 ^ .onMouseLeave --> isMouseOver.setState(false ),
65- ^ .key := file .path,
79+ ^ .key := fileOrFolder .path,
6680 < .div(
6781 ^ .paddingLeft := s " ${16 * depth}px " ,
6882 < .i(^ .className := s " fa fa- ${icon}" ),
69- file .name
83+ fileOrFolder .name
7084 )
7185 ),
7286
7387 < .div(
7488 if (isExpanded.value) {
75- file match {
89+ fileOrFolder match {
7690 case folder : Folder =>
7791 folder.children.map {
78- f : FileOrFolder => FileOrFolderNode (f, s, depth + 1 , selectFile, dragStartOrEnd ).render
92+ f : FileOrFolder => FileOrFolderNode (f, s, depth + 1 , selectFile, dragOverOrEnd ).render
7993 }.toVdomArray
8094 case _ : File => EmptyVdom
8195 }
@@ -84,6 +98,5 @@ object FileOrFolderNode {
8498 }
8599 )
86100 )
87-
88101 })
89102}
0 commit comments