refactor: consolidate sidebar item interactions

This commit is contained in:
lucaronin
2026-05-01 06:34:25 +02:00
parent 09759d204c
commit 4acbe17e1e
11 changed files with 263 additions and 150 deletions

View File

@@ -1,6 +1,6 @@
import { useCallback, useEffect, useRef, useState } from 'react'
import { Folder } from '@phosphor-icons/react'
import { Input } from '@/components/ui/input'
import { useSidebarInlineRenameInput } from '../sidebar/sidebarHooks'
interface FolderNameInputProps {
ariaLabel: string
@@ -25,26 +25,18 @@ export function FolderNameInput({
onCancel,
onSubmit,
}: FolderNameInputProps) {
const [value, setValue] = useState(initialValue)
const inputRef = useRef<HTMLInputElement>(null)
const submittingRef = useRef(false)
useEffect(() => {
const input = inputRef.current
if (!input) return
input.focus()
if (selectTextOnFocus) input.select()
}, [selectTextOnFocus])
const handleSubmit = useCallback(async () => {
if (submittingRef.current) return false
submittingRef.current = true
try {
return await onSubmit(value)
} finally {
submittingRef.current = false
}
}, [onSubmit, value])
const {
handleKeyDown,
inputRef,
setValue,
submitValue,
value,
} = useSidebarInlineRenameInput({
initialValue,
onCancel,
onSubmit,
selectTextOnFocus,
})
return (
<div className="flex items-center gap-2 rounded" style={{ paddingTop: 6, paddingBottom: 6, paddingRight: 16, paddingLeft: leftInset, borderRadius: 4 }}>
@@ -55,17 +47,8 @@ export function FolderNameInput({
className="h-auto min-h-0 flex-1 rounded-sm px-2 py-[3px] text-[13px] font-medium"
value={value}
onChange={(event) => setValue(event.target.value)}
onBlur={submitOnBlur ? () => { void handleSubmit() } : undefined}
onKeyDown={(event) => {
if (event.key === 'Enter') {
event.preventDefault()
void handleSubmit()
}
if (event.key === 'Escape') {
event.preventDefault()
onCancel()
}
}}
onBlur={submitOnBlur ? () => { void submitValue() } : undefined}
onKeyDown={handleKeyDown}
placeholder={placeholder}
data-testid={testId}
/>

View File

@@ -1,7 +1,7 @@
import { useCallback, useEffect, useRef, useState, type MouseEvent as ReactMouseEvent, type RefObject } from 'react'
import { useCallback, type MouseEvent as ReactMouseEvent } from 'react'
import type { FolderNode } from '../../types'
import type { FolderFileActions } from '../../hooks/useFileActions'
import type { FolderContextMenuState } from './FolderContextMenu'
import { useSidebarContextMenu } from '../sidebar/sidebarHooks'
interface UseFolderContextMenuInput {
onDeleteFolder?: (folderPath: string) => void
@@ -9,50 +9,21 @@ interface UseFolderContextMenuInput {
onStartRenameFolder?: (folderPath: string) => void
}
function useContextMenuDismiss(
contextMenu: FolderContextMenuState | null,
menuRef: RefObject<HTMLDivElement | null>,
closeContextMenu: () => void,
) {
useEffect(() => {
if (!contextMenu) return
const handleOutsideClick = (event: MouseEvent) => {
if (menuRef.current && !menuRef.current.contains(event.target as Node)) closeContextMenu()
}
const handleEscape = (event: KeyboardEvent) => {
if (event.key === 'Escape') closeContextMenu()
}
document.addEventListener('mousedown', handleOutsideClick)
document.addEventListener('keydown', handleEscape)
return () => {
document.removeEventListener('mousedown', handleOutsideClick)
document.removeEventListener('keydown', handleEscape)
}
}, [closeContextMenu, contextMenu, menuRef])
}
export function useFolderContextMenu({
onDeleteFolder,
folderFileActions,
onStartRenameFolder,
}: UseFolderContextMenuInput) {
const [contextMenu, setContextMenu] = useState<FolderContextMenuState | null>(null)
const menuRef = useRef<HTMLDivElement>(null)
const closeContextMenu = useCallback(() => setContextMenu(null), [])
useContextMenuDismiss(contextMenu, menuRef, closeContextMenu)
const {
closeContextMenu,
contextMenu,
contextMenuRef,
openContextMenuFromPointer,
} = useSidebarContextMenu<string>()
const handleOpenMenu = useCallback((node: FolderNode, event: ReactMouseEvent<HTMLDivElement>) => {
event.preventDefault()
event.stopPropagation()
setContextMenu({
path: node.path,
x: event.clientX,
y: event.clientY,
})
}, [])
openContextMenuFromPointer(node.path, event)
}, [openContextMenuFromPointer])
const handleRenameFromMenu = useCallback((folderPath: string) => {
closeContextMenu()
@@ -73,15 +44,20 @@ export function useFolderContextMenu({
closeContextMenu()
folderFileActions?.copyFolderPath(folderPath)
}, [closeContextMenu, folderFileActions])
const menu = contextMenu ? {
path: contextMenu.target,
x: contextMenu.pos.x,
y: contextMenu.pos.y,
} : null
return {
closeContextMenu,
contextMenu,
contextMenu: menu,
handleCopyPathFromMenu,
handleDeleteFromMenu,
handleOpenMenu,
handleRevealFromMenu,
handleRenameFromMenu,
menuRef,
menuRef: contextMenuRef,
}
}