diff --git a/src-web/components/sidebar/Sidebar.tsx b/src-web/components/sidebar/Sidebar.tsx index f045f27e..a5038eda 100644 --- a/src-web/components/sidebar/Sidebar.tsx +++ b/src-web/components/sidebar/Sidebar.tsx @@ -9,6 +9,7 @@ import { getAnyModel, patchModelById } from '@yaakapp-internal/models'; import classNames from 'classnames'; import { useAtom, useAtomValue } from 'jotai'; import React, { useCallback, useRef, useState } from 'react'; +import { useDrop } from 'react-dnd'; import { useKey, useKeyPressEvent } from 'react-use'; import { activeRequestIdAtom } from '../../hooks/useActiveRequestId'; import { activeWorkspaceAtom } from '../../hooks/useActiveWorkspace'; @@ -22,6 +23,8 @@ import { router } from '../../lib/router'; import { setWorkspaceSearchParams } from '../../lib/setWorkspaceSearchParams'; import { ContextMenu } from '../core/Dropdown'; import { GitDropdown } from '../GitDropdown'; +import type { DragItem } from './dnd'; +import { ItemTypes } from './dnd'; import { sidebarSelectedIdAtom, sidebarTreeAtom } from './SidebarAtoms'; import type { SidebarItemProps } from './SidebarItem'; import { SidebarItems } from './SidebarItems'; @@ -204,15 +207,22 @@ export function Sidebar({ className }: Props) { [hasFocus, selectableRequests, selectedId, setSelectedId, setSelectedTree], ); + const handleMoveToSidebarEnd = useCallback(() => { + setHoveredTree(tree); + // Put at the end of the top tree + setHoveredIndex(tree?.children?.length ?? 0); + }, [tree]); + const handleMove = useCallback( - async (id, side) => { + (id, side) => { let hoveredTree = treeParentMap[id] ?? null; const dragIndex = hoveredTree?.children.findIndex((n) => n.id === id) ?? -99; const hoveredItem = hoveredTree?.children[dragIndex] ?? null; let hoveredIndex = dragIndex + (side === 'above' ? 0 : 1); - const isHoveredItemCollapsed = - hoveredItem != null ? getSidebarCollapsedMap()[hoveredItem.id] : false; + const collapsedMap = getSidebarCollapsedMap(); + const isHoveredItemCollapsed = hoveredItem != null ? collapsedMap[hoveredItem.id] : false; + if (hoveredItem?.model === 'folder' && side === 'below' && !isHoveredItemCollapsed) { // Move into the folder if it's open and we're moving below it hoveredTree = hoveredTree?.children.find((n) => n.id === id) ?? null; @@ -298,6 +308,20 @@ export function Sidebar({ className }: Props) { const mainContextMenuItems = useCreateDropdownItems({ folderId: null }); + const [, connectDrop] = useDrop( + { + accept: ItemTypes.REQUEST, + hover: (_, monitor) => { + if (sidebarRef.current == null) return; + if (!monitor.isOver({ shallow: true })) return; + handleMoveToSidebarEnd(); + }, + }, + [handleMoveToSidebarEnd], + ); + + connectDrop(sidebarRef); + // Not ready to render yet if (tree == null) { return null; diff --git a/src-web/components/sidebar/SidebarItem.tsx b/src-web/components/sidebar/SidebarItem.tsx index 8a31de37..79941119 100644 --- a/src-web/components/sidebar/SidebarItem.tsx +++ b/src-web/components/sidebar/SidebarItem.tsx @@ -20,15 +20,13 @@ import { HttpMethodTag } from '../core/HttpMethodTag'; import { HttpStatusTag } from '../core/HttpStatusTag'; import { Icon } from '../core/Icon'; import { LoadingIcon } from '../core/LoadingIcon'; +import type { DragItem} from './dnd'; +import { ItemTypes } from './dnd'; import type { SidebarTreeNode } from './Sidebar'; import { sidebarSelectedIdAtom } from './SidebarAtoms'; import { SidebarItemContextMenu } from './SidebarItemContextMenu'; import type { SidebarItemsProps } from './SidebarItems'; -enum ItemTypes { - REQUEST = 'request', -} - export type SidebarItemProps = { className?: string; itemId: string; @@ -44,11 +42,6 @@ export type SidebarItemProps = { latestWebsocketConnection: WebsocketConnection | null; } & Pick; -type DragItem = { - id: string; - itemName: string; -}; - export const SidebarItem = memo(function SidebarItem({ itemName, itemId, @@ -69,9 +62,10 @@ export const SidebarItem = memo(function SidebarItem({ const [, connectDrop] = useDrop( { - accept: ItemTypes.REQUEST, + accept: [ItemTypes.REQUEST, ItemTypes.SIDEBAR], hover: (_, monitor) => { if (!ref.current) return; + if (!monitor.isOver()) return; const hoverBoundingRect = ref.current?.getBoundingClientRect(); const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2; const clientOffset = monitor.getClientOffset(); diff --git a/src-web/components/sidebar/dnd.ts b/src-web/components/sidebar/dnd.ts new file mode 100644 index 00000000..1d302329 --- /dev/null +++ b/src-web/components/sidebar/dnd.ts @@ -0,0 +1,9 @@ +export enum ItemTypes { + REQUEST = 'request', + SIDEBAR = 'sidebar', +} + +export type DragItem = { + id: string; + itemName: string; +};