Allow moving requests and folders to end of list

This commit is contained in:
Gregory Schier
2025-06-29 08:40:14 -07:00
parent 99975c3223
commit fa62f88fa4
3 changed files with 40 additions and 13 deletions

View File

@@ -9,6 +9,7 @@ import { getAnyModel, patchModelById } from '@yaakapp-internal/models';
import classNames from 'classnames'; import classNames from 'classnames';
import { useAtom, useAtomValue } from 'jotai'; import { useAtom, useAtomValue } from 'jotai';
import React, { useCallback, useRef, useState } from 'react'; import React, { useCallback, useRef, useState } from 'react';
import { useDrop } from 'react-dnd';
import { useKey, useKeyPressEvent } from 'react-use'; import { useKey, useKeyPressEvent } from 'react-use';
import { activeRequestIdAtom } from '../../hooks/useActiveRequestId'; import { activeRequestIdAtom } from '../../hooks/useActiveRequestId';
import { activeWorkspaceAtom } from '../../hooks/useActiveWorkspace'; import { activeWorkspaceAtom } from '../../hooks/useActiveWorkspace';
@@ -22,6 +23,8 @@ import { router } from '../../lib/router';
import { setWorkspaceSearchParams } from '../../lib/setWorkspaceSearchParams'; import { setWorkspaceSearchParams } from '../../lib/setWorkspaceSearchParams';
import { ContextMenu } from '../core/Dropdown'; import { ContextMenu } from '../core/Dropdown';
import { GitDropdown } from '../GitDropdown'; import { GitDropdown } from '../GitDropdown';
import type { DragItem } from './dnd';
import { ItemTypes } from './dnd';
import { sidebarSelectedIdAtom, sidebarTreeAtom } from './SidebarAtoms'; import { sidebarSelectedIdAtom, sidebarTreeAtom } from './SidebarAtoms';
import type { SidebarItemProps } from './SidebarItem'; import type { SidebarItemProps } from './SidebarItem';
import { SidebarItems } from './SidebarItems'; import { SidebarItems } from './SidebarItems';
@@ -204,15 +207,22 @@ export function Sidebar({ className }: Props) {
[hasFocus, selectableRequests, selectedId, setSelectedId, setSelectedTree], [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<SidebarItemProps['onMove']>( const handleMove = useCallback<SidebarItemProps['onMove']>(
async (id, side) => { (id, side) => {
let hoveredTree = treeParentMap[id] ?? null; let hoveredTree = treeParentMap[id] ?? null;
const dragIndex = hoveredTree?.children.findIndex((n) => n.id === id) ?? -99; const dragIndex = hoveredTree?.children.findIndex((n) => n.id === id) ?? -99;
const hoveredItem = hoveredTree?.children[dragIndex] ?? null; const hoveredItem = hoveredTree?.children[dragIndex] ?? null;
let hoveredIndex = dragIndex + (side === 'above' ? 0 : 1); let hoveredIndex = dragIndex + (side === 'above' ? 0 : 1);
const isHoveredItemCollapsed = const collapsedMap = getSidebarCollapsedMap();
hoveredItem != null ? getSidebarCollapsedMap()[hoveredItem.id] : false; const isHoveredItemCollapsed = hoveredItem != null ? collapsedMap[hoveredItem.id] : false;
if (hoveredItem?.model === 'folder' && side === 'below' && !isHoveredItemCollapsed) { if (hoveredItem?.model === 'folder' && side === 'below' && !isHoveredItemCollapsed) {
// Move into the folder if it's open and we're moving below it // Move into the folder if it's open and we're moving below it
hoveredTree = hoveredTree?.children.find((n) => n.id === id) ?? null; hoveredTree = hoveredTree?.children.find((n) => n.id === id) ?? null;
@@ -298,6 +308,20 @@ export function Sidebar({ className }: Props) {
const mainContextMenuItems = useCreateDropdownItems({ folderId: null }); const mainContextMenuItems = useCreateDropdownItems({ folderId: null });
const [, connectDrop] = useDrop<DragItem, void>(
{
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 // Not ready to render yet
if (tree == null) { if (tree == null) {
return null; return null;

View File

@@ -20,15 +20,13 @@ import { HttpMethodTag } from '../core/HttpMethodTag';
import { HttpStatusTag } from '../core/HttpStatusTag'; import { HttpStatusTag } from '../core/HttpStatusTag';
import { Icon } from '../core/Icon'; import { Icon } from '../core/Icon';
import { LoadingIcon } from '../core/LoadingIcon'; import { LoadingIcon } from '../core/LoadingIcon';
import type { DragItem} from './dnd';
import { ItemTypes } from './dnd';
import type { SidebarTreeNode } from './Sidebar'; import type { SidebarTreeNode } from './Sidebar';
import { sidebarSelectedIdAtom } from './SidebarAtoms'; import { sidebarSelectedIdAtom } from './SidebarAtoms';
import { SidebarItemContextMenu } from './SidebarItemContextMenu'; import { SidebarItemContextMenu } from './SidebarItemContextMenu';
import type { SidebarItemsProps } from './SidebarItems'; import type { SidebarItemsProps } from './SidebarItems';
enum ItemTypes {
REQUEST = 'request',
}
export type SidebarItemProps = { export type SidebarItemProps = {
className?: string; className?: string;
itemId: string; itemId: string;
@@ -44,11 +42,6 @@ export type SidebarItemProps = {
latestWebsocketConnection: WebsocketConnection | null; latestWebsocketConnection: WebsocketConnection | null;
} & Pick<SidebarItemsProps, 'onSelect'>; } & Pick<SidebarItemsProps, 'onSelect'>;
type DragItem = {
id: string;
itemName: string;
};
export const SidebarItem = memo(function SidebarItem({ export const SidebarItem = memo(function SidebarItem({
itemName, itemName,
itemId, itemId,
@@ -69,9 +62,10 @@ export const SidebarItem = memo(function SidebarItem({
const [, connectDrop] = useDrop<DragItem, void>( const [, connectDrop] = useDrop<DragItem, void>(
{ {
accept: ItemTypes.REQUEST, accept: [ItemTypes.REQUEST, ItemTypes.SIDEBAR],
hover: (_, monitor) => { hover: (_, monitor) => {
if (!ref.current) return; if (!ref.current) return;
if (!monitor.isOver()) return;
const hoverBoundingRect = ref.current?.getBoundingClientRect(); const hoverBoundingRect = ref.current?.getBoundingClientRect();
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2; const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
const clientOffset = monitor.getClientOffset(); const clientOffset = monitor.getClientOffset();

View File

@@ -0,0 +1,9 @@
export enum ItemTypes {
REQUEST = 'request',
SIDEBAR = 'sidebar',
}
export type DragItem = {
id: string;
itemName: string;
};