UNPKG

@carbon/react

Version:

React components for the Carbon Design System

183 lines (179 loc) 5.92 kB
/** * 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 };