62 lines
1.4 KiB
TypeScript
62 lines
1.4 KiB
TypeScript
import React, { useEffect, useRef, ChangeEvent } from 'react';
|
|
|
|
interface Props {
|
|
value: string;
|
|
onChange: (value: string) => void;
|
|
className?: string;
|
|
placeholder?: string;
|
|
}
|
|
|
|
const AutoExpandingTextInput: React.FC<Props> = ({
|
|
value,
|
|
className,
|
|
onChange,
|
|
placeholder
|
|
}) => {
|
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
const measureRef = useRef<HTMLSpanElement>(null);
|
|
|
|
const adjustWidth = () => {
|
|
const input = inputRef.current;
|
|
const measure = measureRef.current;
|
|
if (input && measure) {
|
|
measure.textContent = input.value || placeholder || '';
|
|
input.style.width = `${measure.offsetWidth + 10}px`;
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
adjustWidth();
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [value, placeholder]);
|
|
|
|
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
onChange(e.target.value);
|
|
adjustWidth();
|
|
};
|
|
|
|
return (
|
|
<div className="relative inline-block">
|
|
<input
|
|
ref={inputRef}
|
|
type="text"
|
|
value={value}
|
|
onChange={handleChange}
|
|
className={className}
|
|
placeholder={placeholder}
|
|
style={{ minWidth: '50px' }}
|
|
/>
|
|
<span
|
|
ref={measureRef}
|
|
className="absolute invisible whitespace-pre"
|
|
style={{
|
|
font: 'inherit',
|
|
padding: '0 4px',
|
|
border: '2px solid transparent',
|
|
}}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default AutoExpandingTextInput; |