@shopify/polaris
Version:
Shopify’s product component library
61 lines (60 loc) • 3.75 kB
JavaScript
import React from 'react';
import { CaretDownMinor } from '@shopify/polaris-icons';
import { classNames, variationName } from '../../utilities/css';
import { handleMouseUpByBlurring } from '../../utilities/focus';
import { useI18n } from '../../utilities/i18n';
import { UnstyledLink } from '../UnstyledLink';
import { Icon } from '../Icon';
import { Spinner } from '../Spinner';
import styles from './Button.scss';
const DEFAULT_SIZE = 'medium';
export function Button({ id, url, disabled, loading, children, accessibilityLabel, ariaControls, ariaExpanded, ariaPressed, onClick, onFocus, onBlur, onKeyDown, onKeyPress, onKeyUp, external, download, icon, primary, outline, destructive, disclosure, plain, monochrome, submit, size = DEFAULT_SIZE, textAlign, fullWidth, }) {
const i18n = useI18n();
const isDisabled = disabled || loading;
const className = classNames(styles.Button, primary && styles.primary, outline && styles.outline, destructive && styles.destructive, isDisabled && styles.disabled, loading && styles.loading, plain && styles.plain, monochrome && styles.monochrome, size && size !== DEFAULT_SIZE && styles[variationName('size', size)], textAlign && styles[variationName('textAlign', textAlign)], fullWidth && styles.fullWidth, icon && children == null && styles.iconOnly);
const disclosureIconMarkup = disclosure ? (<IconWrapper>
<Icon source={loading ? 'placeholder' : CaretDownMinor}/>
</IconWrapper>) : null;
let iconMarkup;
if (icon) {
const iconInner = isIconSource(icon) ? (<Icon source={loading ? 'placeholder' : icon}/>) : (icon);
iconMarkup = <IconWrapper>{iconInner}</IconWrapper>;
}
const childMarkup = children ? (<span className={styles.Text}>{children}</span>) : null;
const spinnerColor = primary || destructive ? 'white' : 'inkLightest';
const spinnerSVGMarkup = loading ? (<span className={styles.Spinner}>
<Spinner size="small" color={spinnerColor} accessibilityLabel={i18n.translate('Polaris.Button.spinnerAccessibilityLabel')}/>
</span>) : null;
const content = iconMarkup || disclosureIconMarkup ? (<span className={styles.Content}>
{spinnerSVGMarkup}
{iconMarkup}
{childMarkup}
{disclosureIconMarkup}
</span>) : (<span className={styles.Content}>
{spinnerSVGMarkup}
{childMarkup}
</span>);
const type = submit ? 'submit' : 'button';
if (url) {
return isDisabled ? (
// Render an `<a>` so toggling disabled/enabled state changes only the
// `href` attribute instead of replacing the whole element.
// eslint-disable-next-line jsx-a11y/anchor-is-valid
<a id={id} className={className} aria-label={accessibilityLabel}>
{content}
</a>) : (<UnstyledLink id={id} url={url} external={external} download={download} onClick={onClick} onFocus={onFocus} onBlur={onBlur} onMouseUp={handleMouseUpByBlurring} className={className} aria-label={accessibilityLabel}>
{content}
</UnstyledLink>);
}
return (<button id={id} type={type} onClick={onClick} onFocus={onFocus} onBlur={onBlur} onKeyDown={onKeyDown} onKeyUp={onKeyUp} onKeyPress={onKeyPress} onMouseUp={handleMouseUpByBlurring} className={className} disabled={isDisabled} aria-label={accessibilityLabel} aria-controls={ariaControls} aria-expanded={ariaExpanded} aria-pressed={ariaPressed} role={loading ? 'alert' : undefined} aria-busy={loading ? true : undefined}>
{content}
</button>);
}
export function IconWrapper({ children }) {
return <span className={styles.Icon}>{children}</span>;
}
function isIconSource(x) {
return (typeof x === 'string' ||
(typeof x === 'object' && x.body) ||
typeof x === 'function');
}