UNPKG

@nex-ui/react

Version:

🎉 A beautiful, modern, and reliable React component library.

241 lines (238 loc) • 7.9 kB
"use client"; import { jsxs, jsx } from 'react/jsx-runtime'; import { useMemo, useId } from 'react'; import { isFunction, isString } from '@nex-ui/utils'; import { useFocusRing, useControlledState, useEvent } from '@nex-ui/hooks'; import { useNexUI } from '../provider/Context.mjs'; import { useDefaultProps } from '../utils/useDefaultProps.mjs'; import { useStyles } from '../utils/useStyles.mjs'; import { useSlot } from '../utils/useSlot.mjs'; import { composeClasses } from '../utils/composeClasses.mjs'; import { switchRecipe } from '../../theme/recipes/switch.mjs'; import { getUtilityClass } from '../utils/getUtilityClass.mjs'; const useSlotClasses = (ownerState)=>{ const { prefix } = useNexUI(); const { size, color, disabled, checked, classes } = ownerState; return useMemo(()=>{ const switchRoot = `${prefix}-switch`; const slots = { root: [ 'root', `color-${color}`, `size-${size}`, disabled && 'disabled', checked && 'checked' ], input: [ 'input' ], track: [ 'track' ], thumb: [ 'thumb' ], startIcon: [ 'start-icon' ], endIcon: [ 'end-icon' ], label: [ 'label' ] }; return composeClasses(slots, getUtilityClass(switchRoot), classes); }, [ checked, classes, color, disabled, size, prefix ]); }; const useSlotAriaProps = (ownerState)=>{ const { checked, disabled, children, slotProps, role = 'switch', tabIndex = 0, type = 'checkbox', as = 'input', 'aria-labelledby': labelledBy, 'aria-label': ariaLabel, 'aria-checked': ariaChecked, 'aria-disabled': ariaDisabled } = ownerState; const id = useId(); return useMemo(()=>{ const stringChildren = isString(children); const labelProps = slotProps?.label ?? {}; const labelId = labelProps.id ?? (stringChildren ? id : undefined); let input = { role, tabIndex: disabled ? -1 : tabIndex, 'aria-labelledby': labelledBy ?? labelId, 'aria-label': ariaLabel ?? (stringChildren ? children : undefined) }; if (as === 'input' || isFunction(as)) { input = { ...input, type, disabled, checked }; } else { input = { ...input, 'aria-checked': ariaChecked ?? checked, 'aria-disabled': ariaDisabled ?? (disabled || undefined) }; } const label = { id: labelId }; return { input, label }; }, [ ariaChecked, ariaDisabled, ariaLabel, as, checked, children, disabled, id, labelledBy, role, slotProps?.label, tabIndex, type ]); }; const Switch = (inProps)=>{ const { primaryThemeColor } = useNexUI(); const props = useDefaultProps({ name: 'Switch', props: inProps }); const { sx, children, slotProps, className, startIcon, endIcon, onCheckedChange, thumbIcon: thumbIconProp, checked: checkdeProp, disabled = false, size = 'md', defaultChecked = false, color = primaryThemeColor, ...remainingProps } = props; const { focusVisible, focusProps } = useFocusRing(); const [checked, setChecked] = useControlledState(checkdeProp, defaultChecked, onCheckedChange); const ownerState = { ...props, color, checked, disabled, size, defaultChecked }; const handleChange = useEvent((event)=>{ setChecked(event.target.checked); }); const handleClick = useEvent((event)=>{ // Compatible with non interactive elements if (event.currentTarget.tagName !== 'INPUT' && event.target === event.currentTarget) { setChecked(!checked); } }); const handleKeyUp = useEvent((event)=>{ // Keyboard accessibility for non interactive elements if (focusVisible && event.key === 'Space' && event.target === event.currentTarget && event.currentTarget.tagName !== 'INPUT') { event.currentTarget.click(); } }); const classes = useSlotClasses(ownerState); const styles = useStyles({ ownerState, name: 'Switch', recipe: switchRecipe }); const slotAriaProps = useSlotAriaProps(ownerState); const [SwitchRoot, getSwitchRootProps] = useSlot({ ownerState, elementType: 'label', externalSlotProps: slotProps?.root, style: styles.root, classNames: classes.root, additionalProps: { className, sx } }); const [SwitchInput, getSwitchInputProps] = useSlot({ ownerState, elementType: 'input', externalForwardedProps: remainingProps, style: styles.input, classNames: classes.input, a11y: slotAriaProps.input, additionalProps: { onChange: handleChange, onClick: handleClick, onKeyUp: handleKeyUp, 'data-focus-visible': focusVisible || undefined, ...focusProps } }); const [SwitchTrack, getSwitchTrackProps] = useSlot({ ownerState, elementType: 'span', externalSlotProps: slotProps?.track, style: styles.track, classNames: classes.track }); const [SwitchThumb, getSwitchThumbProps] = useSlot({ ownerState, elementType: 'span', externalSlotProps: slotProps?.thumb, style: styles.thumb, classNames: classes.thumb }); const [SwitchStartIcon, getSwitchStartIconProps] = useSlot({ ownerState, elementType: 'span', externalSlotProps: slotProps?.startIcon, style: styles.startIcon, classNames: classes.startIcon }); const [SwitchEndIcon, getSwitchEndIconProps] = useSlot({ ownerState, elementType: 'span', externalSlotProps: slotProps?.endIcon, style: styles.endIcon, classNames: classes.endIcon }); const [SwitchLabel, getSwitchLabelProps] = useSlot({ ownerState, elementType: 'span', externalSlotProps: slotProps?.label, style: styles.label, classNames: classes.label, a11y: slotAriaProps.label }); const thumbIcon = isFunction(thumbIconProp) ? thumbIconProp(ownerState) : thumbIconProp; return /*#__PURE__*/ jsxs(SwitchRoot, { ...getSwitchRootProps(), children: [ /*#__PURE__*/ jsx(SwitchInput, { ...getSwitchInputProps() }), /*#__PURE__*/ jsxs(SwitchTrack, { ...getSwitchTrackProps(), children: [ startIcon && /*#__PURE__*/ jsx(SwitchStartIcon, { ...getSwitchStartIconProps(), children: startIcon }), /*#__PURE__*/ jsx(SwitchThumb, { ...getSwitchThumbProps(), children: thumbIcon }), endIcon && /*#__PURE__*/ jsx(SwitchEndIcon, { ...getSwitchEndIconProps(), children: endIcon }) ] }), children && /*#__PURE__*/ jsx(SwitchLabel, { ...getSwitchLabelProps(), children: children }) ] }); }; Switch.displayName = 'Switch'; export { Switch };