import React, { forwardRef, useId, useMemo } from 'react' import { type SwitchProps, SwitchSize } from './types' import { getSwitchDataState, getSwitchTextProps, getSwitchSubtextProps, getSwitchLabelStyles, getSubtextId, mergeAriaDescribedBy, } from './utils' import { StyledSwitchRoot, StyledSwitchThumb } from './StyledSwitch' import Block from '../Primitives/Block/Block' import PrimitiveText from '../Primitives/PrimitiveText/PrimitiveText' import { Tooltip } from '../Tooltip/Tooltip' import type { SwitchTokensType } from './switch.token' import { useResponsiveTokens } from '../../hooks/useResponsiveTokens' import { useErrorShake } from '../common/useErrorShake' import { getErrorShakeStyle } from '../common/error.animations' import { getTruncatedText } from '../../global-utils/GlobalUtils' export const Switch = forwardRef( ( { id, checked, onChange, disabled = false, required = false, error = false, size = SwitchSize.MEDIUM, label, subtext, slot, name, value, maxLength, ...rest }, ref ) => { const tokens = useResponsiveTokens('SWITCH') const generatedId = useId() const uniqueId = id || generatedId const shouldShake = useErrorShake(error) const labelMaxLength = maxLength?.label const subtextMaxLength = maxLength?.subtext const subtextId = getSubtextId(uniqueId, !!subtext) const { 'aria-describedby': customAriaDescribedBy, ...restProps } = rest as { 'aria-describedby'?: string; [key: string]: unknown } // Calculate switch width and gap from tokens const switchDimensions = useMemo( () => ({ switchWidth: String(tokens.switchContainer.width[size]), gap: String(tokens.gap), total: `calc(${tokens.switchContainer.width[size]} + ${tokens.gap})`, }), [tokens, size] ) const ariaAttributes = { 'aria-required': required ? true : undefined, 'aria-invalid': error ? true : undefined, 'aria-disabled': disabled ? true : undefined, 'aria-describedby': mergeAriaDescribedBy( subtextId, customAriaDescribedBy ), } return ( onChange?.(!checked)} aria-checked={checked} data-state={getSwitchDataState(checked || false)} size={size} $isDisabled={disabled} $isChecked={checked || false} $tokens={tokens} value={value} name={name} style={getErrorShakeStyle(shouldShake)} {...restProps} {...ariaAttributes} > {slot && ( <> {slot} )} {subtext && ( {subtext} )} ) } ) Switch.displayName = 'Switch' export default Switch const SwitchContent: React.FC<{ uniqueId: string disabled: boolean error: boolean required: boolean size: SwitchSize label?: string tokens: SwitchTokensType maxLength?: number }> = ({ uniqueId, disabled, error, required, size, label, tokens, maxLength, }) => { if (!label) return null const labelStyles = getSwitchLabelStyles(disabled) const textProps = getSwitchTextProps(tokens, size, disabled, error) const { truncatedValue, fullValue, isTruncated } = getTruncatedText( label, maxLength ) const content = ( ) return isTruncated ? ( {content} ) : ( content ) } const SwitchSubtext: React.FC<{ id?: string size: SwitchSize disabled: boolean error: boolean tokens: SwitchTokensType children: React.ReactNode maxLength?: number }> = ({ id, size, disabled, error, tokens, children, maxLength }) => { const subtextProps = getSwitchSubtextProps(tokens, size, disabled, error) const isStringLike = typeof children === 'string' || typeof children === 'number' const stringValue = isStringLike ? String(children) : '' const truncation = isStringLike ? getTruncatedText(stringValue, maxLength) : null const content = ( {truncation?.isTruncated ? truncation.truncatedValue : children} ) if (truncation?.isTruncated) { return {content} } return content }