import { InputHTMLAttributes, ReactNode, forwardRef, useId, FunctionComponent, SVGProps, useState, useEffect, } from 'react'; import cx from 'clsx'; import styles from './input.module.scss'; type Props = Omit, 'id' | 'size'> & { icon?: FunctionComponent & { title?: string | undefined }>; size?: 'default' | 'small'; label?: string; error?: ReactNode; block?: boolean; }; function useWidth() { const [width, setWidth] = useState(0); const id = useId(); useEffect(() => { // not using ref to obatain node, cuz forwardRef for svg disabled in svgr by default const node = document.getElementById(id); if (!node) return; setWidth(node.getBoundingClientRect().width); }, [id]); return [width, id] as const; } const Input = forwardRef( ( { icon: Icon, className, label, error, type = 'text', placeholder = ' ', size = 'default', block, ...attrs }, ref, ) => { const { disabled } = attrs; const id = useId(); // TODO: find a better way to display icon // input, label and fieldset should have the same parent to detect input's state, // therefore label requires position absolute const [iconWidth, iconId] = useWidth(); const padding = 14; const gap = 8; const labelStyle = { left: `${iconWidth ? padding + gap + iconWidth : padding}px` }; return ( {Icon && } {label && ( {label} )} {label} {error && {error}} ); }, ); export { Input }; export type { Props as InputProps };
{error}