@mskcc/carbon-react
Version:
Carbon react components for the MSKCC DSM
318 lines (311 loc) • 9.71 kB
JavaScript
/**
* 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 };