UNPKG

@mskcc/carbon-react

Version:

Carbon react components for the MSKCC DSM

318 lines (311 loc) 9.71 kB
/** * MSKCC 2021, 2024 */ import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js'; import PropTypes from 'prop-types'; import React__default, { useRef } from 'react'; import cx from 'classnames'; import { IconButton } from '../IconButton/IconButton.js'; import { composeEventHandlers } from '../../tools/events.js'; import { usePrefix } from '../../internal/usePrefix.js'; import { useId } from '../../internal/useId.js'; const ButtonKinds = ['primary', 'secondary', 'danger', 'ghost', 'danger--primary', 'danger--ghost', 'danger--tertiary', 'tertiary']; // export const ButtonSizes = ['field', 'sm', 'md', 'lg', 'xl', '2xl'] as const; const ButtonSizes = ['sm', 'md', 'lg']; const ButtonTooltipAlignments = ['start', 'center', 'end']; const ButtonTooltipPositions = ['top', 'right', 'bottom', 'left']; const ButtonIconPositions = ['top', 'right', 'left']; const Button = /*#__PURE__*/React__default.forwardRef(function Button(_ref, // ref: React.Ref<unknown>, ref) { let { as, children, className, dangerDescription = 'danger', disabled = false, hasIconOnly = false, href, iconDescription, icon, iconPosition, isExpressive = false, isSelected, kind = 'primary', width = 'content', onBlur, onClick, onFocus, onMouseEnter, onMouseLeave, // renderIcon: ButtonImageElement, renderIcon, size = 'md', tabIndex, tooltipAlignment = 'center', tooltipPosition = 'top', type = 'button', wrapperClasses, disableTooltip, disableFocus, ...rest } = _ref; const tooltipRef = useRef(null); const prefix = usePrefix(); const handleClick = evt => { // Prevent clicks on the tooltip from triggering the button click event if (evt.target === tooltipRef.current) { evt.preventDefault(); return; } }; const buttonClasses = cx(className, { [`msk-btn`]: true, [`msk-btn--${size}`]: size, [`msk-btn--${kind}`]: kind, [`msk-btn--icon-only`]: hasIconOnly, [`msk-btn--icon-${iconPosition}`]: iconPosition && !hasIconOnly, [`msk-btn--disabled`]: disabled, [`msk-btn--${width}`]: width, [`msk-btn--disable-focus`]: disableFocus // [`${prefix}--btn--sm`]: size === 'sm' && !isExpressive, // TODO: V12 - Remove this class // [`${prefix}--btn--md`]: size === 'md' && !isExpressive, // TODO: V12 - Remove this class // [`${prefix}--btn--xl`]: size === 'xl', // TODO: V12 - Remove this class // [`${prefix}--btn--2xl`]: size === '2xl', // TODO: V12 - Remove this class // [`${prefix}--layout--size-${size}`]: size, // [`${prefix}--btn--${kind}`]: kind, // [`${prefix}--btn--expressive`]: isExpressive, // [`${prefix}--btn--icon-only`]: hasIconOnly, // [`${prefix}--btn--selected`]: hasIconOnly && isSelected && kind === 'ghost', }); const commonProps = { tabIndex, className: buttonClasses, ref }; // const iconOnlyImage = !ButtonImageElement ? null : <ButtonImageElement />; // const iconOnlyImage = null; const dangerButtonVariants = ['danger', 'danger--tertiary', 'danger--ghost']; let component = 'button'; const assistiveId = useId('danger-description'); const { 'aria-pressed': ariaPressed } = rest; let otherProps = { disabled, type, 'aria-describedby': dangerButtonVariants.includes(kind) ? assistiveId : undefined, 'aria-pressed': ariaPressed ?? undefined }; const anchorProps = { href }; let assistiveText = null; if (dangerButtonVariants.includes(kind)) { assistiveText = /*#__PURE__*/React__default.createElement("span", { id: assistiveId, className: `${prefix}--visually-hidden` }, dangerDescription); } if (as) { component = as; otherProps = { ...otherProps, ...anchorProps }; } else if (href && !disabled) { component = 'a'; otherProps = anchorProps; } if (hasIconOnly) { let align = undefined; if (tooltipPosition === 'top' || tooltipPosition === 'bottom') { if (tooltipAlignment === 'center') { align = tooltipPosition; } if (tooltipAlignment === 'end') { align = `${tooltipPosition}-right`; } if (tooltipAlignment === 'start') { align = `${tooltipPosition}-left`; } } if (tooltipPosition === 'right' || tooltipPosition === 'left') { align = tooltipPosition; } return /*#__PURE__*/React__default.createElement(IconButton, _extends({ as: as, align: align, label: iconDescription, kind: kind, size: size, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onFocus: onFocus, onBlur: onBlur, onClick: composeEventHandlers([onClick, handleClick]), wrapperClasses: wrapperClasses, disableTooltip: disableTooltip }, rest, commonProps, otherProps), icon); } if (!hasIconOnly) { const Button = /*#__PURE__*/React__default.createElement(component, { onMouseEnter, onMouseLeave, onFocus, onBlur, onClick, ...rest, ...commonProps, ...otherProps }, assistiveText, (!!icon && iconPosition === 'left' || iconPosition === 'top') && /*#__PURE__*/React__default.createElement("span", { className: "msk-icon msk-btn--icon", "aria-label": iconDescription }, icon), children, !!icon && iconPosition === 'right' && /*#__PURE__*/React__default.createElement("span", { className: "msk-icon msk-btn--icon", "aria-label": iconDescription }, icon)); return Button; } return /*#__PURE__*/React__default.createElement(component, { onMouseEnter, onMouseLeave, onFocus, onBlur, onClick, ...rest, ...commonProps, ...otherProps }, assistiveText, children); }); Button.displayName = 'Button'; Button.propTypes = { /** * Specify how the button itself should be rendered. * Make sure to apply all props to the root node and render children appropriately */ as: PropTypes.oneOfType([PropTypes.func, PropTypes.string, PropTypes.elementType]), /** * Specify the content of your Button */ children: PropTypes.node, /** * Specify an optional className to be added to your Button */ className: PropTypes.string, /** * Specify the message read by screen readers for the danger button variant */ dangerDescription: PropTypes.string, /** * Specify whether the button should be disabled from focus */ disableFocus: PropTypes.bool, /** * Specify whether the tooltip should be disabled */ disableTooltip: PropTypes.bool, /** * Specify whether the Button should be disabled, or not */ disabled: PropTypes.bool, /** * Specify if the button is an icon-only button */ hasIconOnly: PropTypes.bool, /** * Optionally specify an href for your Button to become an `<a>` element */ href: PropTypes.string, /* render a msk icon from types MskIcon */ icon: PropTypes.string, /** * If specifying the `renderIcon` prop, provide a description for that icon that can * be read by screen readers */ iconDescription: props => { if (props.renderIcon && !props.children && !props.iconDescription) { return new Error('renderIcon property specified without also providing an iconDescription property.'); } return null; }, /** * Specify whether the Button is expressive, or not */ isExpressive: PropTypes.bool, /** * Specify whether the Button is currently selected. Only applies to the Ghost variant. */ isSelected: PropTypes.bool, /** * Specify the kind of Button you want to create */ kind: PropTypes.oneOf(ButtonKinds), /** * Provide an optional function to be called when the button element * loses focus */ onBlur: PropTypes.func, /** * Provide an optional function to be called when the button element * is clicked */ onClick: PropTypes.func, /** * Provide an optional function to be called when the button element * receives focus */ onFocus: PropTypes.func, /** * Provide an optional function to be called when the mouse * enters the button element */ onMouseEnter: PropTypes.func, /** * Provide an optional function to be called when the mouse * leaves the button element */ onMouseLeave: PropTypes.func, /** * Optional prop to allow overriding the icon rendering. * Can be a React component class */ renderIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), /** * Optional prop to specify the role of the Button */ role: PropTypes.string, /** * Specify the size of the button, from the following list of sizes: */ size: PropTypes.oneOf(['sm', 'md', 'lg']), /** * Optional prop to specify the tabIndex of the Button */ tabIndex: PropTypes.number, /** * Specify the alignment of the tooltip to the icon-only button. * Can be one of: start, center, or end. */ tooltipAlignment: PropTypes.oneOf(['start', 'center', 'end']), /** * Specify the direction of the tooltip for icon-only buttons. * Can be either top, right, bottom, or left. */ tooltipPosition: PropTypes.oneOf(['top', 'right', 'bottom', 'left']), /** * Optional prop to specify the type of the Button */ type: PropTypes.oneOf(['button', 'reset', 'submit']), /** * Specify the width of the button */ width: PropTypes.oneOf(['content', 'full']), /** * Specify an optional className to be added to your Icon Only Tooltip wrapper */ wrapperClasses: PropTypes.string }; export { ButtonIconPositions, ButtonKinds, ButtonSizes, ButtonTooltipAlignments, ButtonTooltipPositions, Button as default };