import { useMemo } from '@wordpress/element'; import type { CSSProperties } from 'react'; import Popover from './Popover'; type InputProps = { label?: string; labelTip?: React.ReactNode; help?: string; value: string; onChange: ( value: string ) => void; type?: string; placeholder?: string; id?: string; name?: string; required?: boolean; className?: string; disabled?: boolean; isUrl?: boolean; inputClassName?: string; inputStyle?: CSSProperties; min?: number | string; max?: number | string; step?: number | string; }; /** * Text input control styled with Tailwind. * * @param {InputProps} props Component props. * @return {JSX.Element} Rendered input control. */ const Input = ( props: InputProps ) => { const { label, labelTip, help, value, onChange, type = 'text', placeholder, id, name, required = false, className, disabled = false, isUrl = false, inputClassName, inputStyle, min, max, step, } = props; const generatedId = useMemo( () => `airygen-input-${ Math.random().toString( 36 ).slice( 2 ) }`, [], ); const inputId = id ?? generatedId; const handleChange = ( event: { target: HTMLInputElement } ) => { onChange( event.target.value ); }; const trimmedValue = value.trim(); const urlInvalid = useMemo( () => { if ( ! isUrl || '' === trimmedValue ) { return false; } try { const parsed = new URL( trimmedValue ); return ! [ 'http:', 'https:' ].includes( parsed.protocol ); } catch { return true; } }, [ isUrl, trimmedValue ] ); const wrapperClass = [ 'flex flex-col gap-2', className ] .filter( Boolean ) .join( ' ' ); const inputClass = [ 'airygen-field', disabled ? 'cursor-not-allowed' : '', urlInvalid ? 'border-red-500 focus:border-red-500 focus:ring-red-500' : '', inputClassName, ] .filter( Boolean ) .join( ' ' ); return (
Please enter a valid URL starting with http:// or https://.
) : null }