Просмотр исходного кода

Highlight dragged and target items when dragging in the file tree (#15384)

GitOrigin-RevId: 4eaace34434753f6674724adafcf3b0754365d15
Alf Eaton 3 лет назад
Родитель
Сommit
949d4facc7

+ 11 - 7
services/web/frontend/js/features/file-tree/components/file-tree-draggable-preview-layer.jsx

@@ -1,6 +1,5 @@
 import { useRef } from 'react'
 import PropTypes from 'prop-types'
-import { useDragLayer } from 'react-dnd'
 import classNames from 'classnames'
 
 // a custom component rendered on top of a draggable area that renders the
@@ -8,12 +7,12 @@ import classNames from 'classnames'
 // https://react-dnd.github.io/react-dnd/examples/drag-around/custom-drag-layer
 // for more details.
 // Also used to display a container border when hovered.
-function FileTreeDraggablePreviewLayer({ isOver }) {
-  const { isDragging, item, clientOffset } = useDragLayer(monitor => ({
-    isDragging: monitor.isDragging(),
-    item: monitor.getItem(),
-    clientOffset: monitor.getClientOffset(),
-  }))
+function FileTreeDraggablePreviewLayer({
+  isOver,
+  isDragging,
+  item,
+  clientOffset,
+}) {
   const ref = useRef()
 
   return (
@@ -39,6 +38,11 @@ function FileTreeDraggablePreviewLayer({ isOver }) {
 
 FileTreeDraggablePreviewLayer.propTypes = {
   isOver: PropTypes.bool.isRequired,
+  isDragging: PropTypes.bool.isRequired,
+  item: PropTypes.shape({
+    title: PropTypes.string.isRequired,
+  }),
+  clientOffset: PropTypes.number,
 }
 
 function DraggablePreviewItem({ title }) {

+ 5 - 2
services/web/frontend/js/features/file-tree/components/file-tree-item/file-tree-item-inner.jsx

@@ -11,6 +11,7 @@ import FileTreeItemName from './file-tree-item-name'
 import FileTreeItemMenu from './file-tree-item-menu'
 import { useFileTreeSelectable } from '../../contexts/file-tree-selectable'
 import { useFileTreeActionable } from '../../contexts/file-tree-actionable'
+import { useDragDropManager } from 'react-dnd'
 
 function FileTreeItemInner({ id, name, isSelected, icons }) {
   const { permissionsLevel } = useEditorContext(editorContextPropTypes)
@@ -24,7 +25,9 @@ function FileTreeItemInner({ id, name, isSelected, icons }) {
     isSelected &&
     selectedEntityIds.size === 1
 
-  const { isDragging, dragRef, setIsDraggable } = useDraggable(id)
+  const { dragRef, setIsDraggable } = useDraggable(id)
+
+  const dragDropItem = useDragDropManager().getMonitor().getItem()
 
   const itemRef = useRef()
 
@@ -58,7 +61,7 @@ function FileTreeItemInner({ id, name, isSelected, icons }) {
   return (
     <div
       className={classNames('entity', {
-        'dnd-draggable-dragging': isDragging,
+        'file-tree-entity-dragging': dragDropItem?.draggedEntityIds?.has(id),
       })}
       role="presentation"
       ref={dragRef}

+ 14 - 2
services/web/frontend/js/features/file-tree/components/file-tree-root.jsx

@@ -19,6 +19,8 @@ import { useDroppable } from '../contexts/file-tree-draggable'
 import { useFileTreeSocketListener } from '../hooks/file-tree-socket-listener'
 import FileTreeModalCreateFile from './modals/file-tree-modal-create-file'
 import FileTreeInner from './file-tree-inner'
+import { useDragLayer } from 'react-dnd'
+import classnames from 'classnames'
 
 const FileTreeRoot = React.memo(function FileTreeRoot({
   refProviders,
@@ -66,14 +68,24 @@ function FileTreeRootFolder() {
 
   const { isOver, dropRef } = useDroppable(fileTreeData._id)
 
+  const dragLayer = useDragLayer(monitor => ({
+    isDragging: monitor.isDragging(),
+    item: monitor.getItem(),
+    clientOffset: monitor.getClientOffset(),
+  }))
+
   return (
     <>
-      <FileTreeDraggablePreviewLayer isOver={isOver} />
+      <FileTreeDraggablePreviewLayer isOver={isOver} {...dragLayer} />
       <FileTreeFolderList
         folders={fileTreeData.folders}
         docs={fileTreeData.docs}
         files={fileTreeData.fileRefs}
-        classes={{ root: 'file-tree-list' }}
+        classes={{
+          root: classnames('file-tree-list', {
+            'file-tree-dragging': dragLayer.isDragging,
+          }),
+        }}
         dropRef={dropRef}
         dataTestId="file-tree-list-root"
       />

+ 3 - 1
services/web/frontend/js/features/file-tree/contexts/file-tree-draggable.jsx

@@ -88,7 +88,7 @@ export function useDraggable(draggedEntityId) {
   const [isDraggable, setIsDraggable] = useState(true)
 
   const item = { type: DRAGGABLE_TYPE }
-  const [{ isDragging }, dragRef, preview] = useDrag({
+  const [{ isDragging, draggedEntityIds }, dragRef, preview] = useDrag({
     item, // required, but overwritten by the return value of `begin`
     begin: () => {
       const draggedEntityIds = getDraggedEntityIds(
@@ -104,6 +104,7 @@ export function useDraggable(draggedEntityId) {
       isDragging: !!monitor.isDragging(),
     }),
     canDrag: () => permissionsLevel !== 'readOnly' && isDraggable,
+    end: () => item,
   })
 
   // remove the automatic preview as we're using a custom preview via
@@ -116,6 +117,7 @@ export function useDraggable(draggedEntityId) {
     dragRef,
     isDragging,
     setIsDraggable,
+    draggedEntityIds,
   }
 }
 

+ 43 - 34
services/web/frontend/stylesheets/app/editor/file-tree.less

@@ -67,22 +67,6 @@
     }
   }
 
-  li.dnd-droppable-hover .entity-name,
-  li .entity-name.droppable-hover {
-    font-weight: bold;
-    background-color: @file-tree-item-hover-bg;
-    .fake-full-width-bg(@file-tree-item-hover-bg);
-  }
-
-  ul.droppable-hover li div.entity-name:hover {
-    background-color: transparent;
-    .fake-full-width-bg(transparent);
-    &.droppable-hover {
-      background-color: @file-tree-item-hover-bg;
-      .fake-full-width-bg(@file-tree-item-hover-bg);
-    }
-  }
-
   ul.file-tree-list {
     margin: 0;
     overflow-x: hidden;
@@ -144,10 +128,10 @@
         &:focus {
           outline: none;
         }
+        background-color: transparent;
         &:hover {
           background-color: @file-tree-item-hover-bg;
-        }
-        &:hover {
+
           // When the entity is a subfolder, the DOM element is "indented" via margin-left. This makes the
           // element not fill the entire file-tree width (as it's spaced from the left-hand side via margin)
           // and, in consequence, the background gets clipped. The ::before pseudo-selector is used to fill
@@ -163,13 +147,6 @@
         }
       }
 
-      .entity.dnd-draggable-dragging {
-        .entity-name:hover {
-          background-color: transparent;
-          .fake-full-width-bg(transparent);
-        }
-      }
-
       i.fa {
         color: @file-tree-item-icon-color;
         font-size: 14px;
@@ -309,15 +286,18 @@
       > .entity {
         > .entity-name {
           color: @file-tree-item-selected-color;
+
           > div > i.fa,
           > button > i.fa,
           > i.fa,
           .entity-menu-toggle i.fa {
             color: @file-tree-item-selected-color;
           }
+
           > i.linked-file-highlight {
             color: @blue;
           }
+
           background-color: @file-tree-item-selected-bg;
           font-weight: bold;
           padding-right: 32px;
@@ -332,13 +312,43 @@
         }
       }
     }
-    ul.file-tree-list li.selected.dnd-droppable-hover {
-      > .entity {
-        > .entity-name {
-          background-color: @file-tree-item-hover-bg;
-          .fake-full-width-bg(@file-tree-item-hover-bg);
-        }
-      }
+  }
+
+  // while dragging, the previously selected item gets no highlight
+  ul.file-tree-list.file-tree-dragging li.selected .entity .entity-name {
+    font-weight: normal;
+
+    background-color: transparent;
+    .fake-full-width-bg(transparent);
+    color: @file-tree-item-color;
+    i.fa {
+      color: @file-tree-item-icon-color !important;
+    }
+  }
+
+  // the items being dragged get the full "hover" colour
+  ul.file-tree-list.file-tree-dragging
+    li
+    .entity.file-tree-entity-dragging
+    .entity-name {
+    background-color: fade(@file-tree-item-hover-bg, 90%);
+    .fake-full-width-bg(fade(@file-tree-item-hover-bg, 90%));
+    color: @file-tree-item-color;
+    i.fa {
+      color: @file-tree-item-icon-color !important;
+    }
+  }
+
+  // the drop target gets the "selected" colour
+  ul.file-tree-list.file-tree-dragging
+    li.dnd-droppable-hover
+    .entity
+    .entity-name {
+    background-color: @file-tree-item-selected-bg;
+    .fake-full-width-bg(@file-tree-item-selected-bg);
+    color: @file-tree-item-selected-color;
+    i.fa {
+      color: @file-tree-item-selected-color !important;
     }
   }
 
@@ -355,8 +365,7 @@
     }
   }
 
-  .dnd-draggable-preview-item,
-  .ui-draggable-dragging {
+  .dnd-draggable-preview-item {
     background-color: fade(@file-tree-item-selected-bg, 60%);
     color: @file-tree-item-selected-color;
     width: 75%;