UNPKG

@syncfusion/react-buttons

Version:

Syncfusion React Buttons package is a feature-rich collection of UI components including Button, CheckBox, RadioButton, Switch, Chip, and more for building modern, interactive React applications.

107 lines (106 loc) 5.92 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { useState, useEffect, useCallback, useMemo, useImperativeHandle, useRef, forwardRef, memo } from 'react'; import { preRender, useProviderContext, useRippleEffect, Color, Size, Position, useStableId } from '@syncfusion/react-base'; const WRAPPER = 'sf-control sf-switch-wrapper sf-display-inline-flex sf-prevent-select'; const DISABLED = 'sf-disabled sf-no-pointer'; const SWITCH_CLASS = 'sf-switch'; const HANDLE_BASE = 'sf-switch-handle sf-content-center'; const HANDLE_CHECKED = 'sf-switch-handle-checked'; const HANDLE_ICON = 'sf-switch-handle-icon'; const TRACK_BASE = 'sf-switch-track sf-display-inline-flex'; const TRACK_CHECKED = 'sf-switch-track-checked'; const STATE_LAYER = 'sf-switch-state-layer'; const ICON_CENTER = 'sf-switch-icon sf-content-center'; const TRACK_LABEL_BASE = 'sf-switch-track-label sf-display-inline-flex'; const TRACK_LABEL_ON = 'sf-switch-track-label-on'; const TRACK_LABEL_OFF = 'sf-switch-track-label-off'; const LABEL_CLASS_NAME = 'sf-label'; const SWITCH_LABEL_BASE = 'sf-switch-label'; /** * The Switch component is a binary toggle control for settings and standalone options * that take immediate effect. It follows the Material Design 3 specification. * * ```typescript * import { Switch } from "@syncfusion/react-buttons"; * * <Switch checked={true} label="Dark mode" /> * ``` */ export const Switch = forwardRef((props, ref) => { const { onChange, checked, defaultChecked = false, color = Color.Primary, checkedIcon, uncheckedIcon, className = '', disabled = false, labelPlacement = Position.Right, name = '', label = '', value = '', size = Size.Medium, onTrackLabel = null, offTrackLabel = null, readOnly = false, ...domProps } = props; const inputId = domProps.id ?? useStableId('sf-switch'); const isControlled = checked !== undefined; const [checkedState, setCheckedState] = useState(() => { if (isControlled) { return !!checked; } return defaultChecked; }); const inputRef = useRef(null); const { dir, ripple } = useProviderContext(); const { rippleMouseDown, Ripple } = useRippleEffect(ripple, { isCenterRipple: true }); const hasIcon = !!(checkedIcon || uncheckedIcon); const publicAPI = useMemo(() => ({ checked, value, color, size, checkedIcon, uncheckedIcon, onTrackLabel, offTrackLabel }), [checked, value, color, size, checkedIcon, uncheckedIcon, onTrackLabel, offTrackLabel]); useImperativeHandle(ref, () => ({ ...publicAPI, element: inputRef.current }), [publicAPI]); useEffect(() => { if (isControlled) { setCheckedState(!!checked); } }, [checked, isControlled]); useEffect(() => { preRender('switch'); }, []); const handleStateChange = useCallback((event) => { if (disabled || readOnly) { return; } const newChecked = event.target.checked; if (!isControlled) { setCheckedState(newChecked); } if (onChange) { onChange({ event, value: newChecked }); } }, [onChange, isControlled, disabled, readOnly]); const handleMouseDown = useCallback((e) => { if (ripple && rippleMouseDown) { rippleMouseDown(e); } }, [ripple, rippleMouseDown]); const currentChecked = isControlled ? !!checked : checkedState; const wrapperClass = useMemo(() => [ WRAPPER, className, color ? `sf-${color.toLowerCase()}` : '', disabled ? DISABLED : '', readOnly ? 'sf-readonly' : '', dir === 'rtl' ? 'sf-rtl' : '', size ? `sf-${size.toLowerCase()}` : '' ].filter(Boolean).join(' '), [className, color, disabled, readOnly, dir, size]); const handleClass = useMemo(() => [ HANDLE_BASE, currentChecked ? HANDLE_CHECKED : '', hasIcon ? HANDLE_ICON : '' ].filter(Boolean).join(' '), [currentChecked, hasIcon]); const trackClass = useMemo(() => [ TRACK_BASE, currentChecked ? TRACK_CHECKED : '' ].filter(Boolean).join(' '), [currentChecked]); const activeIcon = currentChecked ? (checkedIcon ?? null) : (uncheckedIcon ?? null); const labelClass = useMemo(() => `${SWITCH_LABEL_BASE} sf-${labelPlacement.toLowerCase()} sf-display-inline-flex sf-switch-${size.toLowerCase()}`, [labelPlacement, size]); const trackLabelClass = useMemo(() => `${TRACK_LABEL_BASE} ${currentChecked ? TRACK_LABEL_ON : TRACK_LABEL_OFF}`, [currentChecked]); return (_jsx("div", { className: wrapperClass, children: _jsxs("label", { className: labelClass, htmlFor: inputId, onMouseDown: handleMouseDown, children: [(labelPlacement === Position.Left || labelPlacement === Position.Top) && label && (_jsx("span", { className: LABEL_CLASS_NAME, children: label })), _jsx("input", { ref: inputRef, id: inputId, className: SWITCH_CLASS, type: "checkbox", role: "switch", name: name, value: value, checked: currentChecked, disabled: disabled, readOnly: readOnly, "aria-checked": currentChecked, "aria-label": !label ? 'Switch' : undefined, "aria-readonly": readOnly, title: label || 'Toggle switch', onChange: handleStateChange, ...domProps }), _jsxs("div", { className: trackClass, children: [(onTrackLabel || offTrackLabel) && (_jsx("span", { className: trackLabelClass, children: currentChecked ? onTrackLabel : offTrackLabel })), _jsxs("span", { className: handleClass, children: [_jsx("span", { className: STATE_LAYER, children: ripple && !disabled && _jsx(Ripple, {}) }), hasIcon && (_jsx("span", { className: ICON_CENTER, children: activeIcon }))] })] }), (labelPlacement === Position.Right || labelPlacement === Position.Bottom) && label && (_jsx("span", { className: LABEL_CLASS_NAME, children: label }))] }) })); }); export default memo(Switch);