69 lines
2.1 KiB
TypeScript
69 lines
2.1 KiB
TypeScript
import React, { CSSProperties, useEffect, useMemo, useRef, useState } from 'react'
|
|
import { isMobile } from 'renderer/viewer/lib/simpleUtils'
|
|
import styles from './input.module.css'
|
|
|
|
interface Props extends Omit<React.ComponentProps<'input'>, 'width'> {
|
|
rootStyles?: React.CSSProperties
|
|
autoFocus?: boolean
|
|
inputRef?: React.RefObject<HTMLInputElement>
|
|
validateInput?: (value: string) => CSSProperties | undefined
|
|
width?: number
|
|
}
|
|
|
|
const Input = ({ autoFocus, rootStyles, inputRef, validateInput, defaultValue, width, ...inputProps }: Props) => {
|
|
if (width) rootStyles = { ...rootStyles, width }
|
|
|
|
const ref = useRef<HTMLInputElement>(null!)
|
|
const [validationStyle, setValidationStyle] = useState<CSSProperties>({})
|
|
const [value, setValue] = useState(defaultValue ?? '')
|
|
|
|
useEffect(() => {
|
|
setValue(inputProps.value === '' || inputProps.value ? inputProps.value : value)
|
|
}, [inputProps.value])
|
|
|
|
useEffect(() => {
|
|
if (inputRef) (inputRef as any).current = ref.current
|
|
if (!autoFocus || isMobile()) return // Don't make screen keyboard popup on mobile
|
|
ref.current.focus()
|
|
}, [])
|
|
|
|
|
|
useEffect(() => {
|
|
setValidationStyle(validateInput?.(value as any) ?? {})
|
|
}, [value, validateInput])
|
|
|
|
return <div id='input-container' className={styles.container} style={rootStyles}>
|
|
<input
|
|
ref={ref}
|
|
className={styles.input}
|
|
autoComplete='off'
|
|
autoCapitalize='off'
|
|
autoCorrect='off'
|
|
autoSave='off'
|
|
spellCheck='false'
|
|
style={{ ...validationStyle }}
|
|
{...inputProps}
|
|
value={value}
|
|
onChange={(e) => {
|
|
setValue(e.target.value)
|
|
inputProps.onChange?.(e)
|
|
}}
|
|
/>
|
|
</div>
|
|
}
|
|
|
|
export default Input
|
|
|
|
export const INPUT_LABEL_WIDTH = 190
|
|
|
|
export const InputWithLabel = ({ label, span, ...props }: React.ComponentProps<typeof Input> & { label, span? }) => {
|
|
return <div style={{
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
gridRow: span ? 'span 2 / span 2' : undefined,
|
|
}}
|
|
>
|
|
<label style={{ fontSize: 12, marginBottom: 1, color: 'lightgray' }}>{label}</label>
|
|
<Input rootStyles={{ width: INPUT_LABEL_WIDTH }} {...props} />
|
|
</div>
|
|
}
|