import React, { forwardRef } from 'react'; import cuid from 'cuid'; import isChromatic from 'chromatic/isChromatic'; import styled from 'styled-components'; import VisuallyHidden from '@reach/visually-hidden'; import { Spacer } from '../../foundations/Layout'; import { Text } from '../../foundations/Typography'; import { filterGlobalProps } from '@lendi-ui/commons/utils'; import { InputBaseComponent, ErrorText, HelpText, FieldLabel } from '../TextField'; import { cleanNumericValue } from '../NumericField'; import type { InputFieldBaseProps, InputProps } from '../TextField'; type Variant = 'age' | 'percent' | 'years' | 'months' | 'days'; interface UnitFieldProps extends InputFieldBaseProps { variant: Variant; inlineAction?: never; onValueChange?: (value?: number, name?: string) => void; } const getPaddingRight = (variant: Variant) => { switch (variant) { case 'age': return 98; case 'years': return 70; case 'months': return 86; case 'days': return 62; case 'percent': return 42; } }; const Input = styled(InputBaseComponent as any)<{ variant: Variant }>` padding-right: ${(props) => `${getPaddingRight(props.variant)}px`}; `; export type UnitFieldComponentProps = InputProps; const UnitLabelWrapper = styled.span` position: absolute; right: 1rem; top: 50%; transform: translateY(-50%); `; const Wrapper = styled.div` position: relative; width: 100%; `; const getVariantLabel = (variant: Variant) => { switch (variant) { case 'age': return 'years old'; case 'percent': return '%'; default: return variant; } }; const UnitLabel = ({ variant }: Pick) => { return ( ); }; function UnitFieldComponent( { inputId, name, variant, helpText, errorText, validationState, placeholder, ariaLabel, ariaLabelledBy, label, labelSuffix, isRequired, isDisabled, value, defaultValue, onChange, onValueChange, width = '100%', minWidth, maxWidth, maxCharCount, minCharCount, ...props }: UnitFieldComponentProps, ref: React.Ref ) { const globalProps = filterGlobalProps(props); const inputElId = isChromatic() ? '1' : inputId ?? cuid(); const helpTextId = isChromatic() ? '2' : cuid(); const errorTextId = isChromatic() ? '3' : cuid(); const hasError = errorText && validationState === 'invalid'; const ariaDescribedBy = []; if (helpText) { ariaDescribedBy.push(helpTextId); } if (hasError) { ariaDescribedBy.push(errorTextId); } const handleOnChange = (event: React.ChangeEvent) => { const valueOnly = cleanNumericValue({ value: event.target.value, allowDecimal: variant === 'percent' }); const [, hasDecimals] = valueOnly.split('.'); event.target.value = valueOnly; onChange?.(event); if (valueOnly === '') { onValueChange?.(undefined, name); return; } if (valueOnly === '.') { onValueChange?.(undefined, name); return; } if (hasDecimals) { onValueChange?.(parseFloat(valueOnly), name); return; } onValueChange?.(Number(valueOnly), name); }; return ( {label && ( ( <> {label} {`(${getVariantLabel(variant)})`} )} labelSuffix={labelSuffix} /> )} 0 ? ariaDescribedBy.join(' ') : undefined} required={isRequired} disabled={isDisabled} maxLength={maxCharCount} minLength={minCharCount} value={value} defaultValue={defaultValue} onChange={handleOnChange} ref={ref} inputMode={variant === 'percent' ? 'decimal' : 'numeric'} variant={variant} /> {helpText && } {hasError && errorText && } ); } export const UnitField = forwardRef(UnitFieldComponent);