@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
JavaScript
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);