brf/client/shared/components/checkbox_factory.tsx
2025-12-13 21:12:08 +01:00

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
}