@carbon/react
Version:
React components for the Carbon Design System
183 lines (179 loc) • 5.92 kB
JavaScript
/**
* Copyright IBM Corp. 2016, 2023
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
import React, { useRef } from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import { useControllableState } from '../../internal/useControllableState.js';
import { usePrefix } from '../../internal/usePrefix.js';
import '../Text/index.js';
import { Text } from '../Text/Text.js';
var _path;
function Toggle({
'aria-labelledby': ariaLabelledby,
className,
defaultToggled = false,
disabled = false,
hideLabel = false,
id,
labelA = 'Off',
labelB = 'On',
labelText,
onClick,
onToggle,
readOnly,
size = 'md',
toggled,
...other
}) {
const prefix = usePrefix();
const buttonElement = useRef(null);
const [checked, setChecked] = useControllableState({
value: toggled,
onChange: onToggle,
defaultValue: defaultToggled
});
function handleClick(e) {
if (!readOnly) {
setChecked(!checked);
}
if (onClick) {
onClick(e);
}
}
const isSm = size === 'sm';
const sideLabel = hideLabel ? labelText : checked ? labelB : labelA;
const renderSideLabel = !(hideLabel && !labelText);
const LabelComponent = labelText ? 'label' : 'div';
const wrapperClasses = cx(`${prefix}--toggle`, {
[`${prefix}--toggle--disabled`]: disabled,
[`${prefix}--toggle--readonly`]: readOnly
}, className);
const labelTextClasses = cx(`${prefix}--toggle__label-text`, {
[`${prefix}--visually-hidden`]: hideLabel
});
const appearanceClasses = cx(`${prefix}--toggle__appearance`, {
[`${prefix}--toggle__appearance--sm`]: isSm
});
const switchClasses = cx(`${prefix}--toggle__switch`, {
[`${prefix}--toggle__switch--checked`]: checked
});
const labelId = `${id}_label`;
return (
/*#__PURE__*/
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
React.createElement("div", {
className: wrapperClasses,
onClick: !labelText ? e => {
// the underlying <button> can only be activated by keyboard as it is visually hidden;
// therefore, if this event's target is the <button>, it had to be triggered by
// the keyboard event which already calls handleClick. if we wouldn't catch this, the
// onClick and onToggle functions would be called twice whenever the user activates the
// toggle by keyboard and props['aria-labelledby'] is passed.
if (buttonElement.current && e.target !== buttonElement.current && !disabled) {
handleClick(e);
buttonElement.current.focus();
}
} : undefined
}, /*#__PURE__*/React.createElement("button", _extends({}, other, {
ref: buttonElement,
id: id,
className: `${prefix}--toggle__button`,
role: "switch",
type: "button",
"aria-checked": checked,
"aria-labelledby": ariaLabelledby ?? (labelText ? labelId : undefined),
disabled: disabled,
onClick: handleClick
})), /*#__PURE__*/React.createElement(LabelComponent, {
id: labelId,
htmlFor: ariaLabelledby ? undefined : id,
className: `${prefix}--toggle__label`
}, labelText && /*#__PURE__*/React.createElement(Text, {
className: labelTextClasses
}, labelText), /*#__PURE__*/React.createElement("div", {
className: appearanceClasses
}, /*#__PURE__*/React.createElement("div", {
className: switchClasses
}, isSm && /*#__PURE__*/React.createElement("svg", {
"aria-hidden": "true",
focusable: "false",
className: `${prefix}--toggle__check`,
width: "6px",
height: "5px",
viewBox: "0 0 6 5"
}, _path || (_path = /*#__PURE__*/React.createElement("path", {
d: "M2.2 2.7L5 0 6 1 2.2 5 0 2.7 1 1.5z"
})))), renderSideLabel && /*#__PURE__*/React.createElement(Text, {
className: `${prefix}--toggle__text`,
"aria-hidden": "true"
}, sideLabel))))
);
}
Toggle.propTypes = {
/**
* Specify another element's id to be used as the label for this toggle
*/
'aria-labelledby': PropTypes.string,
/**
* Specify a custom className to apply to the form-item node
*/
className: PropTypes.string,
/**
* Specify whether the toggle should be on by default
*/
defaultToggled: PropTypes.bool,
/**
* Whether this control should be disabled
*/
disabled: PropTypes.bool,
/**
* If true, the side labels (props.labelA and props.labelB) will be replaced by
* props.labelText (if passed), so that the toggle doesn't render a top label.
*/
hideLabel: PropTypes.bool,
/**
* Provide an id that unique represents the underlying `<button>`
*/
id: PropTypes.string.isRequired,
/**
* Specify the label for the "off" position
*/
labelA: PropTypes.node,
/**
* Specify the label for the "on" position
*/
labelB: PropTypes.node,
/**
* Provide the text that will be read by a screen reader when visiting this
* control. This should be provided unless 'aria-labelledby' is set instead
* or you use an external <label> element with its "for" attribute set to the
* toggle's id.
*/
labelText: PropTypes.string,
/**
* Provide an event listener that is called when the control is clicked
*/
onClick: PropTypes.func,
/**
* Provide an event listener that is called when the control is toggled
*/
onToggle: PropTypes.func,
/**
* Whether the toggle should be read-only
*/
readOnly: PropTypes.bool,
/**
* Specify the size of the Toggle. Currently only supports 'sm' or 'md' (default)
*/
size: PropTypes.oneOf(['sm', 'md']),
/**
* Specify whether the control is toggled
*/
toggled: PropTypes.bool
};
export { Toggle, Toggle as default };