import { useEffect, useRef, useState, useId } from 'react' import { UnitInputSize, type UnitInputProps, UnitPosition } from './types' import Text from '../../Text/Text' import Block from '../../Primitives/Block/Block' import InputLabels from '../utils/InputLabels/InputLabels' import PrimitiveInput from '../../Primitives/PrimitiveInput/PrimitiveInput' import InputFooter from '../utils/InputFooter/InputFooter' import type { UnitInputTokensType } from './unitInput.tokens' import { useBreakpoints } from '../../../hooks/useBreakPoints' import { BREAKPOINTS } from '../../../breakpoints/breakPoints' import FloatingLabels from '../utils/FloatingLabels/FloatingLabels' import { toPixels } from '../../../global-utils/GlobalUtils' import { useResponsiveTokens } from '../../../hooks/useResponsiveTokens' import { useTheme } from '../../../context/ThemeContext' import { useErrorShake } from '../../common/useErrorShake' import { getErrorShakeStyle, errorShakeAnimation, } from '../../common/error.animations' import styled from 'styled-components' const Wrapper = styled(Block)` ${errorShakeAnimation} ` const UnitInput = ({ value, onChange, min, max, step, error = false, errorMessage, required, disabled, size = UnitInputSize.MEDIUM, placeholder, sublabel, helpIconHintText, label = 'Number Input', hintText, leftSlot, rightSlot, unit, unitPosition = UnitPosition.RIGHT, name, onFocus, onBlur, ...rest }: UnitInputProps) => { const { foundationTokens } = useTheme() const unitInputTokens = useResponsiveTokens('UNIT_INPUT') const [leftSlotWidth, setLeftSlotWidth] = useState(0) const [rightSlotWidth, setRightSlotWidth] = useState(0) const [unitWidth, setUnitWidth] = useState(0) const [isFocused, setIsFocused] = useState(false) const shouldShake = useErrorShake(error) const { breakPointLabel } = useBreakpoints(BREAKPOINTS) const isSmallScreen = breakPointLabel === 'sm' const inputFocusedOrWithValue = isFocused || value !== undefined const isSmallScreenWithLargeSize = isSmallScreen && size === UnitInputSize.LARGE const paddingX = toPixels(unitInputTokens.inputContainer.padding.x[size]) const paddingY = toPixels(unitInputTokens.inputContainer.padding.y[size]) + (isSmallScreenWithLargeSize ? 0.5 : 1) const generatedId = useId() const inputId = rest.id || generatedId const errorId = `${inputId}-error` const hintId = `${inputId}-hint` const leftSlotRef = useRef(null) const rightSlotRef = useRef(null) const unitRef = useRef(null) useEffect(() => { if (leftSlotRef.current) { setLeftSlotWidth(leftSlotRef.current.offsetWidth) } else { setLeftSlotWidth(0) } if (rightSlotRef.current) { setRightSlotWidth(rightSlotRef.current.offsetWidth) } else { setRightSlotWidth(0) } if (unitRef.current) { setUnitWidth(unitRef.current.offsetWidth) } else { setUnitWidth(0) } }, [leftSlot, rightSlot, unit]) const paddingInlineStart = (!leftSlot && unitPosition !== UnitPosition.LEFT ? paddingY : (unitPosition === UnitPosition.LEFT ? unitWidth + 8 : 8) + (leftSlot ? leftSlotWidth + 8 : 0)) + 4 const paddingInlineEnd = !rightSlot && unitPosition !== UnitPosition.RIGHT ? paddingY : (unitPosition === UnitPosition.RIGHT ? unitWidth + 8 : 0) + (rightSlot ? rightSlotWidth + 8 : 0) const RightUnitSlot = () => { return ( {unit} ) } const LeftUnitSlot = () => { return ( {unit} ) } return ( {(!isSmallScreen || size !== UnitInputSize.LARGE) && ( )} {leftSlot && ( {leftSlot} )} {rightSlot && ( {rightSlot} )} {unitPosition === UnitPosition.RIGHT && } {unitPosition === UnitPosition.LEFT && } {label && isSmallScreenWithLargeSize && ( )} { setIsFocused(true) onFocus?.(e) }} onBlur={(e) => { setIsFocused(false) onBlur?.(e) }} {...rest} /> ) } export default UnitInput