82 lines
1.9 KiB
TypeScript
82 lines
1.9 KiB
TypeScript
import { h, type FunctionComponent } from 'preact'
|
|
import { type ChangeEvent } from 'preact/compat'
|
|
import { useCallback, useEffect, useRef, useState } from 'preact/hooks'
|
|
import cn from 'classnames'
|
|
|
|
type Styles<S extends string> = {
|
|
base?: string
|
|
element?: string
|
|
label?: string
|
|
touched?: string
|
|
} & Record<S, string>
|
|
|
|
type Props<S extends string> = {
|
|
autoFocus?: boolean
|
|
className?: string
|
|
defaultChecked?: any
|
|
disabled?: boolean
|
|
label?: string
|
|
name?: string
|
|
onChange?: (e: ChangeEvent) => void
|
|
required?: boolean
|
|
style: S
|
|
value?: string
|
|
}
|
|
|
|
export default function checkboxFactory<S extends string = never>(styles: Styles<S>) {
|
|
const Checkbox: FunctionComponent<Props<S>> = ({
|
|
autoFocus,
|
|
className,
|
|
defaultChecked,
|
|
disabled,
|
|
name,
|
|
label,
|
|
onChange,
|
|
required,
|
|
style,
|
|
value = 'true',
|
|
}) => {
|
|
const [touched, setTouched] = useState(false)
|
|
const checkboxRef = useRef<HTMLInputElement>(null)
|
|
|
|
const onBlur = useCallback(() => setTouched(true), [])
|
|
|
|
useEffect(() => {
|
|
const form = checkboxRef.current!.form!
|
|
const resetTouched = setTouched.bind(null, false)
|
|
|
|
form.addEventListener('reset', resetTouched)
|
|
|
|
return () => form.removeEventListener('reset', resetTouched)
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
if (autoFocus) checkboxRef.current!.focus()
|
|
}, [autoFocus])
|
|
|
|
return (
|
|
<label className={cn(styles.base, styles[style], touched && styles.touched, className)}>
|
|
<input
|
|
autoFocus={autoFocus}
|
|
className={styles.element}
|
|
defaultChecked={defaultChecked}
|
|
disabled={disabled}
|
|
name={name}
|
|
onBlur={onBlur}
|
|
onChange={onChange}
|
|
ref={checkboxRef}
|
|
required={required}
|
|
type='checkbox'
|
|
value={value}
|
|
/>
|
|
|
|
<i />
|
|
|
|
{label && <span className={styles.label}>{label}</span>}
|
|
</label>
|
|
)
|
|
}
|
|
|
|
return Checkbox
|
|
}
|