mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Highlight dragged and target items when dragging in the file tree (#15384)
GitOrigin-RevId: 4eaace34434753f6674724adafcf3b0754365d15
This commit is contained in:
parent
e22c1d70f3
commit
949d4facc7
5 changed files with 76 additions and 46 deletions
|
@ -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 }) {
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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"
|
||||
/>
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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%;
|
||||
|
|
Loading…
Reference in a new issue