107 lines
2.2 KiB
TypeScript
107 lines
2.2 KiB
TypeScript
// frontend\src\components\ui\TextInput.tsx
|
|
|
|
'use client'
|
|
|
|
import {
|
|
useRef,
|
|
useCallback,
|
|
forwardRef,
|
|
type InputHTMLAttributes,
|
|
type Ref,
|
|
} from 'react'
|
|
|
|
type TextInputSize = 'sm' | 'md' | 'lg'
|
|
|
|
export type TextInputProps = Omit<
|
|
InputHTMLAttributes<HTMLInputElement>,
|
|
'size'
|
|
> & {
|
|
size?: TextInputSize
|
|
selectAllOnFocus?: boolean
|
|
selectAllOnMouseDown?: boolean
|
|
}
|
|
|
|
const baseClassName = `
|
|
block w-full min-w-0
|
|
bg-white text-gray-900 shadow-sm ring-1 ring-gray-200
|
|
focus:outline-none focus:ring-2 focus:ring-indigo-500
|
|
dark:bg-white/10 dark:text-white dark:ring-white/10 dark:[color-scheme:dark]
|
|
`
|
|
|
|
const sizeClassName: Record<TextInputSize, string> = {
|
|
sm: 'rounded-md px-3 py-2 text-sm',
|
|
md: 'rounded-lg px-3 py-2.5 text-sm',
|
|
lg: 'rounded-lg px-4 py-3 text-base',
|
|
}
|
|
|
|
function assignRef<T>(ref: Ref<T> | undefined, value: T | null) {
|
|
if (!ref) return
|
|
|
|
if (typeof ref === 'function') {
|
|
ref(value)
|
|
return
|
|
}
|
|
|
|
;(ref as { current: T | null }).current = value
|
|
}
|
|
|
|
const TextInput = forwardRef<HTMLInputElement, TextInputProps>(
|
|
(
|
|
{
|
|
className = '',
|
|
type = 'text',
|
|
size = 'md',
|
|
selectAllOnFocus = false,
|
|
selectAllOnMouseDown = false,
|
|
onFocus,
|
|
onMouseDown,
|
|
...props
|
|
},
|
|
forwardedRef
|
|
) => {
|
|
const innerRef = useRef<HTMLInputElement | null>(null)
|
|
|
|
const setRefs = useCallback(
|
|
(node: HTMLInputElement | null) => {
|
|
innerRef.current = node
|
|
assignRef(forwardedRef, node)
|
|
},
|
|
[forwardedRef]
|
|
)
|
|
|
|
const selectAll = useCallback(() => {
|
|
const el = innerRef.current
|
|
if (!el) return
|
|
el.focus()
|
|
requestAnimationFrame(() => el.select())
|
|
}, [])
|
|
|
|
return (
|
|
<input
|
|
{...props}
|
|
ref={setRefs}
|
|
type={type}
|
|
className={[baseClassName, sizeClassName[size], className].join(' ').trim()}
|
|
onFocus={(e) => {
|
|
onFocus?.(e)
|
|
if (selectAllOnFocus) {
|
|
selectAll()
|
|
}
|
|
}}
|
|
onMouseDown={(e) => {
|
|
onMouseDown?.(e)
|
|
|
|
if (!selectAllOnMouseDown) return
|
|
if (e.button !== 0) return
|
|
|
|
e.preventDefault()
|
|
selectAll()
|
|
}}
|
|
/>
|
|
)
|
|
}
|
|
)
|
|
|
|
TextInput.displayName = 'TextInput'
|
|
|
|
export default TextInput |