@mui/joy
Version:
Joy UI is an open-source React component library that implements MUI's own design principles. It's comprehensive and can be used in production out of the box.
462 lines (461 loc) • 16.9 kB
JavaScript
'use client';
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
import _extends from "@babel/runtime/helpers/esm/extends";
const _excluded = ["checked", "defaultChecked", "disabled", "onBlur", "onChange", "onFocus", "onFocusVisible", "readOnly", "required", "id", "color", "variant", "size", "startDecorator", "endDecorator", "component", "slots", "slotProps"];
import * as React from 'react';
import PropTypes from 'prop-types';
import { unstable_capitalize as capitalize } from '@mui/utils';
import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses';
import { useSwitch } from '@mui/base/useSwitch';
import { styled, useThemeProps } from '../styles';
import useSlot from '../utils/useSlot';
import switchClasses, { getSwitchUtilityClass } from './switchClasses';
import FormControlContext from '../FormControl/FormControlContext';
import { jsx as _jsx } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";
const useUtilityClasses = ownerState => {
const {
checked,
disabled,
focusVisible,
readOnly,
color,
variant,
size
} = ownerState;
const slots = {
root: ['root', checked && 'checked', disabled && 'disabled', focusVisible && 'focusVisible', readOnly && 'readOnly', variant && `variant${capitalize(variant)}`, color && `color${capitalize(color)}`, size && `size${capitalize(size)}`],
thumb: ['thumb', checked && 'checked'],
track: ['track', checked && 'checked'],
action: ['action', focusVisible && 'focusVisible'],
input: ['input'],
startDecorator: ['startDecorator'],
endDecorator: ['endDecorator']
};
return composeClasses(slots, getSwitchUtilityClass, {});
};
const switchColorVariables = ({
theme,
ownerState
}) => (data = {}) => {
var _theme$variants, _styles$backgroundCol, _styles$backgroundCol2;
const styles = ((_theme$variants = theme.variants[`${ownerState.variant}${data.state || ''}`]) == null ? void 0 : _theme$variants[ownerState.color]) || {};
return {
'--Switch-trackBackground': (_styles$backgroundCol = styles.backgroundColor) != null ? _styles$backgroundCol : theme.vars.palette.background.surface,
'--Switch-trackColor': styles.color,
'--Switch-trackBorderColor': ownerState.variant === 'outlined' ? styles.borderColor : 'currentColor',
'--Switch-thumbBackground': styles.color,
'--Switch-thumbColor': (_styles$backgroundCol2 = styles.backgroundColor) != null ? _styles$backgroundCol2 : theme.vars.palette.background.surface
};
};
const SwitchRoot = styled('div', {
name: 'JoySwitch',
slot: 'Root',
overridesResolver: (props, styles) => styles.root
})(({
theme,
ownerState
}) => {
var _theme$variants2;
const getColorVariables = switchColorVariables({
theme,
ownerState
});
return _extends({
'--Icon-color': 'currentColor',
'--variant-borderWidth': (_theme$variants2 = theme.variants[ownerState.variant]) == null || (_theme$variants2 = _theme$variants2[ownerState.color]) == null ? void 0 : _theme$variants2['--variant-borderWidth'],
'--Switch-trackRadius': theme.vars.radius.xl,
'--Switch-thumbShadow': ownerState.variant === 'soft' ? 'none' : '0 0 0 1px var(--Switch-trackBackground)'
}, ownerState.size === 'sm' && {
'--Switch-trackWidth': '26px',
'--Switch-trackHeight': '16px',
'--Switch-thumbSize': '10px',
fontSize: theme.vars.fontSize.sm,
gap: 'var(--Switch-gap, 6px)'
}, ownerState.size === 'md' && {
'--Switch-trackWidth': '32px',
'--Switch-trackHeight': '20px',
'--Switch-thumbSize': '14px',
fontSize: theme.vars.fontSize.md,
gap: 'var(--Switch-gap, 8px)'
}, ownerState.size === 'lg' && {
'--Switch-trackWidth': '40px',
'--Switch-trackHeight': '24px',
'--Switch-thumbSize': '18px',
gap: 'var(--Switch-gap, 12px)'
}, {
'--unstable_paddingBlock': `max((var(--Switch-trackHeight) - 2 * var(--variant-borderWidth, 0px) - var(--Switch-thumbSize)) / 2, 0px)`,
'--Switch-thumbRadius': `max(var(--Switch-trackRadius) - var(--unstable_paddingBlock), min(var(--unstable_paddingBlock) / 2, var(--Switch-trackRadius) / 2))`,
'--Switch-thumbWidth': 'var(--Switch-thumbSize)',
'--Switch-thumbOffset': `max((var(--Switch-trackHeight) - var(--Switch-thumbSize)) / 2, 0px)`
}, getColorVariables(), {
'&:hover': {
'@media (hover: hover)': _extends({}, getColorVariables({
state: 'Hover'
}))
},
[`&.${switchClasses.checked}`]: _extends({}, getColorVariables(), {
'&:hover': {
'@media (hover: hover)': _extends({}, getColorVariables({
state: 'Hover'
}))
}
}),
[`&.${switchClasses.disabled}`]: _extends({
pointerEvents: 'none',
color: theme.vars.palette.text.tertiary
}, getColorVariables({
state: 'Disabled'
})),
display: 'inline-flex',
alignItems: 'center',
alignSelf: 'center',
fontFamily: theme.vars.fontFamily.body,
position: 'relative',
padding: 'calc((var(--Switch-thumbSize) / 2) - (var(--Switch-trackHeight) / 2)) calc(-1 * var(--Switch-thumbOffset))',
backgroundColor: 'initial',
// clear background in case `outlined` variant contain background.
border: 'none',
margin: 'var(--unstable_Switch-margin)'
});
});
const SwitchAction = styled('div', {
name: 'JoySwitch',
slot: 'Action',
overridesResolver: (props, styles) => styles.action
})(({
theme
}) => ({
borderRadius: 'var(--Switch-trackRadius)',
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
[theme.focus.selector]: theme.focus.default
}));
const SwitchInput = styled('input', {
name: 'JoySwitch',
slot: 'Input',
overridesResolver: (props, styles) => styles.input
})({
margin: 0,
height: '100%',
width: '100%',
opacity: 0,
position: 'absolute',
cursor: 'pointer'
});
const SwitchTrack = styled('span', {
name: 'JoySwitch',
slot: 'Track',
overridesResolver: (props, styles) => styles.track
})(({
theme,
ownerState
}) => _extends({
position: 'relative',
color: 'var(--Switch-trackColor)',
height: 'var(--Switch-trackHeight)',
width: 'var(--Switch-trackWidth)',
display: 'flex',
flexShrink: 0,
justifyContent: 'space-between',
alignItems: 'center',
boxSizing: 'border-box',
border: 'var(--variant-borderWidth, 0px) solid',
borderColor: 'var(--Switch-trackBorderColor)',
backgroundColor: 'var(--Switch-trackBackground)',
borderRadius: 'var(--Switch-trackRadius)',
fontFamily: theme.vars.fontFamily.body
}, ownerState.size === 'sm' && {
fontSize: theme.vars.fontSize.xs
}, ownerState.size === 'md' && {
fontSize: theme.vars.fontSize.sm
}, ownerState.size === 'lg' && {
fontSize: theme.vars.fontSize.md
}));
const SwitchThumb = styled('span', {
name: 'JoySwitch',
slot: 'Thumb',
overridesResolver: (props, styles) => styles.thumb
})({
'--Icon-fontSize': 'calc(var(--Switch-thumbSize) * 0.75)',
display: 'inline-flex',
justifyContent: 'center',
alignItems: 'center',
position: 'absolute',
top: '50%',
left: 'calc(50% - var(--Switch-trackWidth) / 2 + var(--Switch-thumbWidth) / 2 + var(--Switch-thumbOffset))',
transform: 'translate(-50%, -50%)',
width: 'var(--Switch-thumbWidth)',
height: 'var(--Switch-thumbSize)',
borderRadius: 'var(--Switch-thumbRadius)',
boxShadow: 'var(--Switch-thumbShadow)',
color: 'var(--Switch-thumbColor)',
backgroundColor: 'var(--Switch-thumbBackground)',
[`&.${switchClasses.checked}`]: {
left: 'calc(50% + var(--Switch-trackWidth) / 2 - var(--Switch-thumbWidth) / 2 - var(--Switch-thumbOffset))'
}
});
const SwitchStartDecorator = styled('span', {
name: 'JoySwitch',
slot: 'StartDecorator',
overridesResolver: (props, styles) => styles.startDecorator
})({
display: 'inline-flex'
});
const SwitchEndDecorator = styled('span', {
name: 'JoySwitch',
slot: 'EndDecorator',
overridesResolver: (props, styles) => styles.endDecorator
})({
display: 'inline-flex'
});
/**
*
* Demos:
*
* - [Switch](https://mui.com/joy-ui/react-switch/)
*
* API:
*
* - [Switch API](https://mui.com/joy-ui/api/switch/)
*/
const Switch = /*#__PURE__*/React.forwardRef(function Switch(inProps, ref) {
var _ref, _inProps$size, _inProps$color, _formControl$color, _ref2, _inProps$disabled;
const props = useThemeProps({
props: inProps,
name: 'JoySwitch'
});
const {
disabled: disabledExternalProp,
id,
color: colorProp,
variant = 'solid',
size: sizeProp = 'md',
startDecorator,
endDecorator,
component,
slots = {},
slotProps = {}
} = props,
other = _objectWithoutPropertiesLoose(props, _excluded);
const formControl = React.useContext(FormControlContext);
if (process.env.NODE_ENV !== 'production') {
const registerEffect = formControl == null ? void 0 : formControl.registerEffect;
// eslint-disable-next-line react-hooks/rules-of-hooks
React.useEffect(() => {
if (registerEffect) {
return registerEffect();
}
return undefined;
}, [registerEffect]);
}
const size = (_ref = (_inProps$size = inProps.size) != null ? _inProps$size : formControl == null ? void 0 : formControl.size) != null ? _ref : sizeProp;
const color = (_inProps$color = inProps.color) != null ? _inProps$color : formControl != null && formControl.error ? 'danger' : (_formControl$color = formControl == null ? void 0 : formControl.color) != null ? _formControl$color : colorProp;
const useSwitchProps = _extends({
disabled: (_ref2 = (_inProps$disabled = inProps.disabled) != null ? _inProps$disabled : formControl == null ? void 0 : formControl.disabled) != null ? _ref2 : disabledExternalProp
}, props);
const {
getInputProps,
checked,
disabled,
focusVisible,
readOnly
} = useSwitch(useSwitchProps);
const ownerState = _extends({}, props, {
id,
checked,
disabled,
focusVisible,
readOnly,
color: checked ? color || 'primary' : color || 'neutral',
variant,
size
});
const classes = useUtilityClasses(ownerState);
const externalForwardedProps = _extends({}, other, {
component,
slots,
slotProps
});
const [SlotRoot, rootProps] = useSlot('root', {
ref,
className: classes.root,
elementType: SwitchRoot,
externalForwardedProps,
ownerState
});
const [SlotStartDecorator, startDecoratorProps] = useSlot('startDecorator', {
additionalProps: {
'aria-hidden': true // hide the decorator from assistive technology
},
className: classes.startDecorator,
elementType: SwitchStartDecorator,
externalForwardedProps,
ownerState
});
const [SlotEndDecorator, endDecoratorProps] = useSlot('endDecorator', {
additionalProps: {
'aria-hidden': true // hide the decorator from assistive technology
},
className: classes.endDecorator,
elementType: SwitchEndDecorator,
externalForwardedProps,
ownerState
});
const [SlotTrack, trackProps] = useSlot('track', {
className: classes.track,
elementType: SwitchTrack,
externalForwardedProps,
ownerState
});
const [SlotThumb, thumbProps] = useSlot('thumb', {
className: classes.thumb,
elementType: SwitchThumb,
externalForwardedProps,
ownerState
});
const [SlotAction, actionProps] = useSlot('action', {
className: classes.action,
elementType: SwitchAction,
externalForwardedProps,
ownerState
});
const [SlotInput, inputProps] = useSlot('input', {
additionalProps: {
id: id != null ? id : formControl == null ? void 0 : formControl.htmlFor,
'aria-describedby': formControl == null ? void 0 : formControl['aria-describedby']
},
className: classes.input,
elementType: SwitchInput,
externalForwardedProps,
getSlotProps: getInputProps,
ownerState
});
return /*#__PURE__*/_jsxs(SlotRoot, _extends({}, rootProps, {
children: [startDecorator && /*#__PURE__*/_jsx(SlotStartDecorator, _extends({}, startDecoratorProps, {
children: typeof startDecorator === 'function' ? startDecorator(ownerState) : startDecorator
})), /*#__PURE__*/_jsxs(SlotTrack, _extends({}, trackProps, {
children: [trackProps == null ? void 0 : trackProps.children, /*#__PURE__*/_jsx(SlotThumb, _extends({}, thumbProps))]
})), /*#__PURE__*/_jsx(SlotAction, _extends({}, actionProps, {
children: /*#__PURE__*/_jsx(SlotInput, _extends({}, inputProps))
})), endDecorator && /*#__PURE__*/_jsx(SlotEndDecorator, _extends({}, endDecoratorProps, {
children: typeof endDecorator === 'function' ? endDecorator(ownerState) : endDecorator
}))]
}));
});
process.env.NODE_ENV !== "production" ? Switch.propTypes /* remove-proptypes */ = {
// ┌────────────────────────────── Warning ──────────────────────────────┐
// │ These PropTypes are generated from the TypeScript type definitions. │
// │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
// └─────────────────────────────────────────────────────────────────────┘
/**
* If `true`, the component is checked.
*/
checked: PropTypes.bool,
/**
* @ignore
*/
children: PropTypes.node,
/**
* The color of the component. It supports those theme colors that make sense for this component.
* @default 'neutral'
*/
color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([PropTypes.oneOf(['danger', 'primary', 'success', 'warning']), PropTypes.string]),
/**
* The component used for the root node.
* Either a string to use a HTML element or a component.
*/
component: PropTypes.elementType,
/**
* The default checked state. Use when the component is not controlled.
*/
defaultChecked: PropTypes.bool,
/**
* If `true`, the component is disabled.
*/
disabled: PropTypes.bool,
/**
* The element that appears at the end of the switch.
*/
endDecorator: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([PropTypes.node, PropTypes.func]),
/**
* @ignore
*/
id: PropTypes.string,
/**
* @ignore
*/
onBlur: PropTypes.func,
/**
* Callback fired when the state is changed.
*
* @param {React.ChangeEvent<HTMLInputElement>} event The event source of the callback.
* You can pull out the new value by accessing `event.target.value` (string).
* You can pull out the new checked state by accessing `event.target.checked` (boolean).
*/
onChange: PropTypes.func,
/**
* @ignore
*/
onFocus: PropTypes.func,
/**
* @ignore
*/
onFocusVisible: PropTypes.func,
/**
* If `true`, the component is read only.
*/
readOnly: PropTypes.bool,
/**
* If `true`, the `input` element is required.
*/
required: PropTypes.bool,
/**
* The size of the component.
* @default 'md'
*/
size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([PropTypes.oneOf(['sm', 'md', 'lg']), PropTypes.string]),
/**
* The props used for each slot inside.
* @default {}
*/
slotProps: PropTypes.shape({
action: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
endDecorator: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
input: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
startDecorator: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
thumb: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
track: PropTypes.oneOfType([PropTypes.func, PropTypes.object])
}),
/**
* The components used for each slot inside.
* @default {}
*/
slots: PropTypes.shape({
action: PropTypes.elementType,
endDecorator: PropTypes.elementType,
input: PropTypes.elementType,
root: PropTypes.elementType,
startDecorator: PropTypes.elementType,
thumb: PropTypes.elementType,
track: PropTypes.elementType
}),
/**
* The element that appears at the end of the switch.
*/
startDecorator: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([PropTypes.node, PropTypes.func]),
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object]),
/**
* The [global variant](https://mui.com/joy-ui/main-features/global-variants/) to use.
* @default 'solid'
*/
variant: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([PropTypes.oneOf(['outlined', 'plain', 'soft', 'solid']), PropTypes.string])
} : void 0;
export default Switch;