73 lines
1.9 KiB
TypeScript
73 lines
1.9 KiB
TypeScript
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
import { Folder } from '@phosphor-icons/react'
|
|
import { Input } from '@/components/ui/input'
|
|
|
|
interface FolderNameInputProps {
|
|
ariaLabel: string
|
|
initialValue: string
|
|
placeholder: string
|
|
selectTextOnFocus?: boolean
|
|
submitOnBlur?: boolean
|
|
testId: string
|
|
onCancel: () => void
|
|
onSubmit: (value: string) => Promise<boolean> | boolean
|
|
}
|
|
|
|
export function FolderNameInput({
|
|
ariaLabel,
|
|
initialValue,
|
|
placeholder,
|
|
selectTextOnFocus = false,
|
|
submitOnBlur = false,
|
|
testId,
|
|
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])
|
|
|
|
return (
|
|
<div className="flex items-center gap-2" style={{ padding: '4px 8px' }}>
|
|
<Folder size={18} className="shrink-0 text-muted-foreground" />
|
|
<Input
|
|
ref={inputRef}
|
|
aria-label={ariaLabel}
|
|
className="h-7 flex-1 rounded-sm px-2 text-[13px]"
|
|
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()
|
|
}
|
|
}}
|
|
placeholder={placeholder}
|
|
data-testid={testId}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|