UNPKG

@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.

501 lines (500 loc) 18.4 kB
'use client'; import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose"; import _extends from "@babel/runtime/helpers/esm/extends"; const _excluded = ["checked", "uncheckedIcon", "checkedIcon", "label", "defaultChecked", "disabled", "disableIcon", "overlay", "id", "indeterminate", "indeterminateIcon", "name", "onBlur", "onChange", "onFocus", "onFocusVisible", "readOnly", "required", "value", "color", "variant", "size", "component", "slots", "slotProps"]; import * as React from 'react'; import PropTypes from 'prop-types'; import { unstable_useId as useId, unstable_capitalize as capitalize } from '@mui/utils'; import { unstable_composeClasses as composeClasses } from '@mui/base'; import { useSwitch } from '@mui/base/useSwitch'; import { styled, useThemeProps } from '../styles'; import useSlot from '../utils/useSlot'; import checkboxClasses, { getCheckboxUtilityClass } from './checkboxClasses'; import CheckIcon from '../internal/svg-icons/Check'; import IndeterminateIcon from '../internal/svg-icons/HorizontalRule'; import { TypographyNestedContext } from '../Typography/Typography'; 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, disableIcon, focusVisible, color, variant, size, indeterminate } = ownerState; const slots = { root: ['root', checked && 'checked', disabled && 'disabled', focusVisible && 'focusVisible', variant && `variant${capitalize(variant)}`, color && `color${capitalize(color)}`, size && `size${capitalize(size)}`], checkbox: ['checkbox', checked && 'checked', indeterminate && 'indeterminate', disabled && 'disabled' // disabled class is necessary for displaying global variant ], action: ['action', checked && 'checked', disableIcon && disabled && 'disabled', // add disabled class to action element for displaying global variant focusVisible && 'focusVisible'], input: ['input'], label: ['label'] }; return composeClasses(slots, getCheckboxUtilityClass, {}); }; const CheckboxRoot = styled('span', { name: 'JoyCheckbox', slot: 'Root', overridesResolver: (props, styles) => styles.root })(({ ownerState, theme }) => { var _theme$variants$plain, _theme$variants, _theme$variants2; return _extends({ '--Icon-fontSize': 'var(--Checkbox-size)' }, ownerState.size === 'sm' && { '--Checkbox-size': '1rem', '& ~ *': { '--FormHelperText-margin': '0 0 0 1.5rem' }, fontSize: theme.vars.fontSize.sm, gap: 'var(--Checkbox-gap, 0.5rem)' }, ownerState.size === 'md' && { '--Checkbox-size': '1.25rem', '& ~ *': { '--FormHelperText-margin': '0.25rem 0 0 1.875rem' }, fontSize: theme.vars.fontSize.md, gap: 'var(--Checkbox-gap, 0.625rem)' }, ownerState.size === 'lg' && { '--Checkbox-size': '1.5rem', '& ~ *': { '--FormHelperText-margin': '0.375rem 0 0 2.25rem' }, fontSize: theme.vars.fontSize.lg, gap: 'var(--Checkbox-gap, 0.75rem)' }, { position: ownerState.overlay ? 'initial' : 'relative', display: 'inline-flex', fontFamily: theme.vars.fontFamily.body, lineHeight: 'var(--Checkbox-size)', color: theme.vars.palette.text.primary, [`&.${checkboxClasses.disabled}`]: { color: (_theme$variants$plain = theme.variants.plainDisabled) == null || (_theme$variants$plain = _theme$variants$plain[ownerState.color]) == null ? void 0 : _theme$variants$plain.color } }, ownerState.disableIcon && { color: (_theme$variants = theme.variants[ownerState.variant]) == null || (_theme$variants = _theme$variants[ownerState.color]) == null ? void 0 : _theme$variants.color, [`&.${checkboxClasses.disabled}`]: { color: (_theme$variants2 = theme.variants[`${ownerState.variant}Disabled`]) == null || (_theme$variants2 = _theme$variants2[ownerState.color]) == null ? void 0 : _theme$variants2.color } }); }); const CheckboxCheckbox = styled('span', { name: 'JoyCheckbox', slot: 'Checkbox', overridesResolver: (props, styles) => styles.checkbox })(({ theme, ownerState }) => { var _theme$variants3, _variantStyle$backgro, _theme$variants4, _theme$variants5, _theme$variants6; const variantStyle = (_theme$variants3 = theme.variants[`${ownerState.variant}`]) == null ? void 0 : _theme$variants3[ownerState.color]; return [_extends({ '--Icon-color': ownerState.color !== 'neutral' || ownerState.variant === 'solid' ? 'currentColor' : theme.vars.palette.text.icon, boxSizing: 'border-box', borderRadius: `min(${theme.vars.radius.sm}, 0.25rem)`, width: 'var(--Checkbox-size)', height: 'var(--Checkbox-size)', display: 'inline-flex', justifyContent: 'center', alignItems: 'center', flexShrink: 0 }, ownerState.disableIcon && { display: 'contents' }, { [`&.${checkboxClasses.checked}, &.${checkboxClasses.indeterminate}`]: { '--Icon-color': 'currentColor' } }), ...(!ownerState.disableIcon ? [_extends({}, variantStyle, { backgroundColor: (_variantStyle$backgro = variantStyle == null ? void 0 : variantStyle.backgroundColor) != null ? _variantStyle$backgro : theme.vars.palette.background.surface }), { '&:hover': { '@media (hover: hover)': (_theme$variants4 = theme.variants[`${ownerState.variant}Hover`]) == null ? void 0 : _theme$variants4[ownerState.color] } }, { '&:active': (_theme$variants5 = theme.variants[`${ownerState.variant}Active`]) == null ? void 0 : _theme$variants5[ownerState.color] }, { [`&.${checkboxClasses.disabled}`]: (_theme$variants6 = theme.variants[`${ownerState.variant}Disabled`]) == null ? void 0 : _theme$variants6[ownerState.color] }] : [])]; }); const CheckboxAction = styled('span', { name: 'JoyCheckbox', slot: 'Action', overridesResolver: (props, styles) => styles.action })(({ theme, ownerState }) => { var _theme$variants7, _theme$variants8, _theme$variants9, _theme$variants10; return [{ borderRadius: `var(--Checkbox-actionRadius, ${ownerState.overlay ? 'var(--unstable_actionRadius, inherit)' : 'inherit'})`, textAlign: 'left', // prevent text-align inheritance position: 'absolute', top: 'calc(-1 * var(--variant-borderWidth, 0px))', // clickable on the border and focus outline does not move when checked/unchecked left: 'calc(-1 * var(--variant-borderWidth, 0px))', bottom: 'calc(-1 * var(--variant-borderWidth, 0px))', right: 'calc(-1 * var(--variant-borderWidth, 0px))', zIndex: 1, // The action element usually cover the area of nearest positioned parent [theme.focus.selector]: theme.focus.default }, ...(ownerState.disableIcon ? [(_theme$variants7 = theme.variants[ownerState.variant]) == null ? void 0 : _theme$variants7[ownerState.color], { '&:hover': (_theme$variants8 = theme.variants[`${ownerState.variant}Hover`]) == null ? void 0 : _theme$variants8[ownerState.color] }, { '&:active': (_theme$variants9 = theme.variants[`${ownerState.variant}Active`]) == null ? void 0 : _theme$variants9[ownerState.color] }, { [`&.${checkboxClasses.disabled}`]: (_theme$variants10 = theme.variants[`${ownerState.variant}Disabled`]) == null ? void 0 : _theme$variants10[ownerState.color] }] : [])]; }); const CheckboxInput = styled('input', { name: 'JoyCheckbox', slot: 'Input', overridesResolver: (props, styles) => styles.input })(() => ({ margin: 0, opacity: 0, position: 'absolute', width: '100%', height: '100%', cursor: 'pointer' })); const CheckboxLabel = styled('label', { name: 'JoyCheckbox', slot: 'Label', overridesResolver: (props, styles) => styles.label })(({ ownerState }) => _extends({ flex: 1, minWidth: 0 }, ownerState.disableIcon && { zIndex: 1, // label should stay on top of the action. pointerEvents: 'none' // makes hover ineffect. })); const defaultCheckedIcon = /*#__PURE__*/_jsx(CheckIcon, {}); const defaultIndeterminateIcon = /*#__PURE__*/_jsx(IndeterminateIcon, {}); /** * * Demos: * * - [Checkbox](https://mui.com/joy-ui/react-checkbox/) * * API: * * - [Checkbox API](https://mui.com/joy-ui/api/checkbox/) */ const Checkbox = /*#__PURE__*/React.forwardRef(function Checkbox(inProps, ref) { var _ref, _inProps$disabled, _ref2, _inProps$size, _formControl$color; const props = useThemeProps({ props: inProps, name: 'JoyCheckbox' }); const { checked: checkedProp, uncheckedIcon, checkedIcon = defaultCheckedIcon, label, defaultChecked, disabled: disabledExternalProp, disableIcon = false, overlay, id: idOverride, indeterminate = false, indeterminateIcon = defaultIndeterminateIcon, name, onBlur, onChange, onFocus, onFocusVisible, readOnly, required, value, color: colorProp, variant: variantProp, size: sizeProp = 'md', component, slots = {}, slotProps = {} } = props, other = _objectWithoutPropertiesLoose(props, _excluded); const formControl = React.useContext(FormControlContext); const disabledProp = (_ref = (_inProps$disabled = inProps.disabled) != null ? _inProps$disabled : formControl == null ? void 0 : formControl.disabled) != null ? _ref : disabledExternalProp; const size = (_ref2 = (_inProps$size = inProps.size) != null ? _inProps$size : formControl == null ? void 0 : formControl.size) != null ? _ref2 : sizeProp; 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 id = useId(idOverride != null ? idOverride : formControl == null ? void 0 : formControl.htmlFor); const useCheckboxProps = { checked: checkedProp, defaultChecked, disabled: disabledProp, onBlur, onChange, onFocus, onFocusVisible }; const { getInputProps, checked, disabled, focusVisible } = useSwitch(useCheckboxProps); const isCheckboxActive = checked || indeterminate; const activeVariant = variantProp || 'solid'; const inactiveVariant = variantProp || 'outlined'; const variant = isCheckboxActive ? activeVariant : inactiveVariant; const color = inProps.color || (formControl != null && formControl.error ? 'danger' : (_formControl$color = formControl == null ? void 0 : formControl.color) != null ? _formControl$color : colorProp); const activeColor = color || 'primary'; const inactiveColor = color || 'neutral'; const ownerState = _extends({}, props, { checked, disabled, disableIcon, overlay, focusVisible, color: isCheckboxActive ? activeColor : inactiveColor, variant, size }); const classes = useUtilityClasses(ownerState); const externalForwardedProps = _extends({}, other, { component, slots, slotProps }); const [SlotRoot, rootProps] = useSlot('root', { ref, className: classes.root, elementType: CheckboxRoot, externalForwardedProps, ownerState }); const [SlotCheckbox, checkboxProps] = useSlot('checkbox', { className: classes.checkbox, elementType: CheckboxCheckbox, externalForwardedProps, ownerState }); const [SlotAction, actionProps] = useSlot('action', { className: classes.action, elementType: CheckboxAction, externalForwardedProps, ownerState }); const [SlotInput, inputProps] = useSlot('input', { additionalProps: _extends({ id, name, value, readOnly, role: undefined, required: required != null ? required : formControl == null ? void 0 : formControl.required, 'aria-describedby': formControl == null ? void 0 : formControl['aria-describedby'] }, indeterminate && { // https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-checked#values 'aria-checked': 'mixed' }), className: classes.input, elementType: CheckboxInput, externalForwardedProps, getSlotProps: getInputProps, ownerState }); const [SlotLabel, labelProps] = useSlot('label', { additionalProps: { htmlFor: id }, className: classes.label, elementType: CheckboxLabel, externalForwardedProps, ownerState }); let icon = uncheckedIcon; if (disableIcon) { icon = null; } else if (indeterminate) { icon = indeterminateIcon; } else if (checked) { icon = checkedIcon; } return /*#__PURE__*/_jsxs(SlotRoot, _extends({}, rootProps, { children: [/*#__PURE__*/_jsxs(SlotCheckbox, _extends({}, checkboxProps, { children: [/*#__PURE__*/_jsx(SlotAction, _extends({}, actionProps, { children: /*#__PURE__*/_jsx(SlotInput, _extends({}, inputProps)) })), icon] })), label && /*#__PURE__*/_jsx(TypographyNestedContext.Provider, { value: true, children: /*#__PURE__*/_jsx(SlotLabel, _extends({}, labelProps, { children: label })) })] })); }); process.env.NODE_ENV !== "production" ? Checkbox.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, /** * The icon to display when the component is checked. * @default <CheckIcon /> */ checkedIcon: PropTypes.node, /** * @ignore */ children: PropTypes.node, /** * Class name applied to the root element. */ className: PropTypes.string, /** * 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', 'neutral', '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, /** * If `true`, the checked icon is removed and the selected variant is applied on the `action` element instead. * @default false */ disableIcon: PropTypes.bool, /** * @ignore */ id: PropTypes.string, /** * If `true`, the component appears indeterminate. * This does not set the native input element to indeterminate due * to inconsistent behavior across browsers. * However, we set a `data-indeterminate` attribute on the `input`. * @default false */ indeterminate: PropTypes.bool, /** * The icon to display when the component is indeterminate. * @default <IndeterminateIcon /> */ indeterminateIcon: PropTypes.node, /** * The label element next to the checkbox. */ label: PropTypes.node, /** * The `name` attribute of the input. */ name: 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 root element's position is set to initial which allows the action area to fill the nearest positioned parent. * This prop is useful for composing Checkbox with ListItem component. * @default false */ overlay: PropTypes.bool, /** * 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.oneOf(['sm', 'md', 'lg']), /** * The props used for each slot inside. * @default {} */ slotProps: PropTypes.shape({ action: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), checkbox: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), input: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), label: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]) }), /** * The components used for each slot inside. * @default {} */ slots: PropTypes.shape({ action: PropTypes.elementType, checkbox: PropTypes.elementType, input: PropTypes.elementType, label: PropTypes.elementType, root: PropTypes.elementType }), /** * 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 icon when `checked` is false. */ uncheckedIcon: PropTypes.node, /** * The value of the component. The DOM API casts this to a string. * The browser uses "on" as the default value. */ value: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.string), PropTypes.number, PropTypes.string]), /** * The [global variant](https://mui.com/joy-ui/main-features/global-variants/) to use. * @default 'solid' */ variant: PropTypes.oneOf(['outlined', 'plain', 'soft', 'solid']) } : void 0; export default Checkbox;