'use client'; import * as React from 'react'; import { classNames } from '@vkontakte/vkjs'; import { useAdaptivity } from '../../hooks/useAdaptivity'; import { useConfigDirection } from '../../hooks/useConfigDirection'; import { useFocusVisible } from '../../hooks/useFocusVisible'; import { useFocusVisibleClassName } from '../../hooks/useFocusVisibleClassName'; import { useMergeProps } from '../../hooks/useMergeProps'; import { usePlatform } from '../../hooks/usePlatform'; import { callMultiple } from '../../lib/callMultiple'; import { warnOnce } from '../../lib/warnOnce'; import { withLabelClickWrapper } from '../../lib/withLabelClickWrapper'; import type { HasDataAttribute, HasRootRef } from '../../types'; import { RootComponent } from '../RootComponent/RootComponent'; import { VisuallyHidden, type VisuallyHiddenProps } from '../VisuallyHidden/VisuallyHidden'; import styles from './Switch.module.css'; const warn = warnOnce('Switch'); const densityClassNames = { none: styles.densityNone, compact: styles.densityCompact, }; export interface SwitchProps extends Pick< React.InputHTMLAttributes, | 'checked' | 'defaultChecked' | 'disabled' | 'readOnly' | 'required' | 'autoFocus' | 'name' | 'value' | 'form' | 'onChange' | 'onFocus' | 'onBlur' >, Omit, 'onChange' | 'onFocus' | 'onBlur'>, HasRootRef { /** * Свойства, которые можно прокинуть внутрь компонента: * - `root`: свойства для прокидывания в корень компонента; * - `input`: свойства для прокидывания в скрытый `input`. */ slotProps?: | { root?: | (Omit, 'children'> & HasRootRef & HasDataAttribute) | undefined; input?: | (React.InputHTMLAttributes & HasRootRef & HasDataAttribute) | undefined; } | undefined; /** * @deprecated Since 7.9.0. Вместо этого используйте `slotProps={ input: { getRootRef: ... } }`. */ getRef?: React.Ref | undefined; } /** * @see https://vkui.io/components/switch */ export const Switch = ({ getRef, // Input props checked, defaultChecked, disabled, readOnly, required, autoFocus, id, name, value, form, onChange, onFocus, onBlur, slotProps, ...restProps }: SwitchProps): React.ReactNode => { /* istanbul ignore if: не проверяем в тестах */ if (process.env.NODE_ENV === 'development' && getRef) { warn('Свойство `getRef` устаревшее, используйте `slotProps={ input: { getRootRef: ... } }`'); } const { onClick: onRootClick, ...rootRest } = useMergeProps(restProps, slotProps?.root); const { checked: checkedProp, onBlur: onInputBlur, onFocus: onInputFocus, onClick, className: inputClassName, ...inputRest } = useMergeProps( { getRootRef: getRef, checked, defaultChecked, disabled, readOnly, required, autoFocus, id, name, value, form, onChange, onFocus, onBlur, }, slotProps?.input, ); const direction = useConfigDirection(); const isRtl = direction === 'rtl'; const platform = usePlatform(); const { density = 'none' } = useAdaptivity(); const { focusVisible, onBlur: onFocusVisibleBlur, onFocus: onFocusVisibleFocus, } = useFocusVisible(); const focusVisibleClassNames = useFocusVisibleClassName({ focusVisible, mode: 'outside' }); const handleBlur = callMultiple(onFocusVisibleBlur, onInputBlur); const handleFocus = callMultiple(onFocusVisibleFocus, onInputFocus); const [localUncontrolledChecked, setLocalUncontrolledChecked] = React.useState( Boolean(inputRest.defaultChecked), ); const isControlled = checkedProp !== undefined; const syncUncontrolledCheckedStateOnClick = React.useCallback( (e: React.MouseEvent) => { if (isControlled) { return; } const switchTarget = e.target as HTMLInputElement; setLocalUncontrolledChecked(switchTarget.checked); }, [isControlled], ); const inputProps: VisuallyHiddenProps = { Component: 'input', type: 'checkbox', role: 'switch', onBlur: handleBlur, onFocus: handleFocus, onClick: callMultiple(syncUncontrolledCheckedStateOnClick, onClick), ...inputRest, }; if (isControlled) { inputProps.checked = checkedProp; inputProps['aria-checked'] = checkedProp ? 'true' : 'false'; } else { inputProps['aria-checked'] = localUncontrolledChecked ? 'true' : 'false'; } return ( ); };