import classnames from 'classnames'; import type { MouseEvent as ReactMouseEvent } from 'react'; import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { DndProvider, useDrag, useDrop } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import { useActiveRequest } from '../hooks/useActiveRequest'; import { useCreateRequest } from '../hooks/useCreateRequest'; import { useDeleteRequest } from '../hooks/useDeleteRequest'; import { useKeyValue } from '../hooks/useKeyValue'; import { useRequests } from '../hooks/useRequests'; import { useUpdateRequest } from '../hooks/useUpdateRequest'; import { clamp } from '../lib/clamp'; import type { HttpRequest } from '../lib/models'; import { Button } from './core/Button'; import { Dropdown, DropdownMenuTrigger } from './core/Dropdown'; import { Icon } from './core/Icon'; import { IconButton } from './core/IconButton'; import { HStack, VStack } from './core/Stacks'; import { WindowDragRegion } from './core/WindowDragRegion'; import { ToggleThemeButton } from './ToggleThemeButton'; interface Props { className?: string; } const MIN_WIDTH = 110; const INITIAL_WIDTH = 200; const MAX_WIDTH = 500; enum ItemTypes { REQUEST = 'request', } export function Sidebar({ className }: Props) { return ( ); } export function Container({ className }: Props) { const [isResizing, setIsRisizing] = useState(false); const width = useKeyValue({ key: 'sidebar_width', initialValue: INITIAL_WIDTH }); const sidebarRef = useRef(null); const requests = useRequests(); const activeRequest = useActiveRequest(); const createRequest = useCreateRequest({ navigateAfter: true }); const moveState = useRef<{ move: (e: MouseEvent) => void; up: () => void } | null>(null); const unsub = () => { if (moveState.current !== null) { document.documentElement.removeEventListener('mousemove', moveState.current.move); document.documentElement.removeEventListener('mouseup', moveState.current.up); } }; const handleResizeReset = useCallback(() => { width.set(INITIAL_WIDTH); }, []); const handleResizeStart = useCallback((e: ReactMouseEvent) => { unsub(); const mouseStartX = e.clientX; const startWidth = width.value; moveState.current = { move: (e: MouseEvent) => { const newWidth = clamp(startWidth + (e.clientX - mouseStartX), MIN_WIDTH, MAX_WIDTH); width.set(newWidth); }, up: () => { unsub(); setIsRisizing(false); }, }; document.documentElement.addEventListener('mousemove', moveState.current.move); document.documentElement.addEventListener('mouseup', moveState.current.up); setIsRisizing(true); }, []); const sidebarStyles = useMemo(() => ({ width: width.value }), [width.value]); const sidebarWidth = width.value - 1; // Minus 1 for the border return (
{/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}
{ await createRequest.mutate({ name: 'Test Request' }); }} />
); } function SidebarItems({ requests, activeRequestId, sidebarWidth, }: { requests: HttpRequest[]; activeRequestId?: string; sidebarWidth: number; }) { const [items, setItems] = useState(requests.map((r) => ({ request: r, left: 0, top: 0 }))); useEffect(() => { setItems(requests.map((r) => ({ request: r, left: 0, top: 0 }))); }, [requests]); const handleMove = useCallback((id: string, hoverId: string) => { setItems((oldItems) => { const dragIndex = oldItems.findIndex((i) => i.request.id === id); const index = oldItems.findIndex((i) => i.request.id === hoverId); const newItems = [...oldItems]; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const b = newItems[index]!; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion newItems[index] = newItems[dragIndex]!; newItems[dragIndex] = b; return newItems; }); }, []); return ( <> {items.map(({ request }) => ( ))} ); } type SidebarItemProps = { requestId: string; requestName: string; workspaceId: string; sidebarWidth: number; active?: boolean; }; const SidebarItem = memo(function SidebarItem({ requestName, requestId, workspaceId, active, sidebarWidth, }: SidebarItemProps) { const deleteRequest = useDeleteRequest(requestId); const updateRequest = useUpdateRequest(requestId); const [editing, setEditing] = useState(false); const handleSubmitNameEdit = useCallback(async (el: HTMLInputElement) => { await updateRequest.mutate({ name: el.value }); setEditing(false); }, []); const handleFocus = useCallback((el: HTMLInputElement | null) => { el?.focus(); el?.select(); }, []); const itemStyles = useMemo(() => ({ width: sidebarWidth }), [sidebarWidth]); if (workspaceId === null) return null; return (
  • [ { label: 'Delete Request', onSelect: deleteRequest.mutate, leftSlot: , }, ], [], )} >
  • ); }); type DraggableSidebarItemProps = SidebarItemProps & { onMove: (id: string, hoverId: string) => void; }; type DragItem = { id: string; }; const DraggableSidebarItem = memo(function DraggableSidebarItem({ requestName, requestId, workspaceId, active, sidebarWidth, onMove, }: DraggableSidebarItemProps) { const ref = useRef(null); const [, connectDrop] = useDrop({ accept: ItemTypes.REQUEST, collect: (m) => ({ handlerId: m.getHandlerId(), isOver: m.isOver() }), hover: (item) => { if (item.id !== requestId) { onMove(requestId, item.id); } }, }); const [{ isDragging }, connectDrag] = useDrag(() => ({ type: ItemTypes.REQUEST, item: () => ({ id: requestId }), collect: (m) => ({ isDragging: m.isDragging() }), })); connectDrag(ref); connectDrop(ref); return (
    ); });