UNPKG

@zendeskgarden/react-buttons

Version:

Components relating to buttons in the Garden Design System

319 lines (316 loc) 13 kB
/** * Copyright Zendesk, Inc. * * Use of this source code is governed under the Apache License, Version 2.0 * found at http://www.apache.org/licenses/LICENSE-2.0. */ import styled, { css } from 'styled-components'; import { math, em } from 'polished'; import { SELECTOR_FOCUS_VISIBLE, componentStyles, getColor, focusStyles, getFocusBoxShadow } from '@zendeskgarden/react-theming'; import { StyledSplitButton } from './StyledSplitButton.js'; import { StyledIcon } from './StyledIcon.js'; const COMPONENT_ID = 'buttons.button'; const getBorderRadius = props => { if (props.$isPill) { return '100px'; } return props.theme.borderRadii.md; }; const getHeight = props => { if (props.$size === 'small') { return `${props.theme.space.base * 8}px`; } else if (props.$size === 'large') { return `${props.theme.space.base * 12}px`; } return `${props.theme.space.base * 10}px`; }; const colorStyles = _ref => { let { theme, $isLink, $isBasic, $isDanger, $isNeutral, $isPrimary, $focusInset } = _ref; let retVal; const disabledBackgroundColor = getColor({ theme, variable: 'background.disabled' }); const disabledForegroundColor = getColor({ theme, variable: 'foreground.disabled' }); const offset100 = { dark: { offset: -100 }, light: { offset: 100 } }; const offset200 = { dark: { offset: -200 }, light: { offset: 200 } }; if ($isLink) { const options = { theme, variable: $isDanger ? 'foreground.danger' : 'foreground.primary' }; const foregroundColor = getColor(options); const hoverForegroundColor = getColor({ ...options, ...offset100 }); const activeForegroundColor = getColor({ ...options, ...offset200 }); const focusOutlineColor = getColor({ theme, variable: 'border.primaryEmphasis' }); retVal = css(["outline-color:transparent;background-color:transparent;color:", ";", " &:hover{color:", ";}&:active,&[aria-pressed='true'],&[aria-pressed='mixed']{color:", ";}&:disabled{color:", ";}"], foregroundColor, focusStyles({ theme, condition: false, styles: { color: foregroundColor , outlineColor: focusOutlineColor } }), hoverForegroundColor, activeForegroundColor, disabledForegroundColor); } else if ($isPrimary) { let backgroundVariable; if ($isDanger) { backgroundVariable = 'background.dangerEmphasis'; } else if ($isNeutral) { backgroundVariable = 'background.emphasis'; } else { backgroundVariable = 'background.primaryEmphasis'; } const options = { theme, variable: backgroundVariable }; const backgroundColor = getColor(options); const hoverBackgroundColor = getColor({ ...options, ...offset100 }); const activeBackgroundColor = getColor({ ...options, ...offset200 }); const foregroundColor = getColor({ theme, variable: 'foreground.onEmphasis' }); retVal = css(["outline-color:transparent;background-color:", ";color:", ";&:hover{background-color:", ";}", " &:active,&[aria-pressed='true'],&[aria-pressed='mixed']{background-color:", ";}&:disabled{background-color:", ";color:", ";}"], backgroundColor, foregroundColor, hoverBackgroundColor, focusStyles({ theme, inset: $focusInset, shadowWidth: $focusInset ? 'sm' : 'md', spacerWidth: $focusInset ? 'sm' : 'xs', styles: ($isDanger || $isNeutral) && $focusInset ? { borderColor: getColor({ theme, variable: 'border.primaryEmphasis' }) } : undefined }), activeBackgroundColor, disabledBackgroundColor, disabledForegroundColor); } else { let borderColor; let hoverBorderColor; let activeBorderColor; let focusBorderColor; let backgroundVariable; let foregroundVariable; if ($isDanger) { if (!$isBasic) { const borderOptions = { theme, variable: 'border.dangerEmphasis' }; borderColor = getColor(borderOptions); hoverBorderColor = getColor({ ...borderOptions, ...offset100 }); activeBorderColor = getColor({ ...borderOptions, ...offset200 }); if ($isNeutral) { focusBorderColor = getColor(borderOptions); } } backgroundVariable = 'background.dangerEmphasis'; foregroundVariable = $isNeutral ? 'foreground.default' : 'foreground.danger'; } else { if (!$isBasic) { const borderOptions = { theme, variable: 'border.primaryEmphasis' }; if ($isNeutral) { borderColor = getColor({ theme, variable: 'border.default', ...offset100 }); hoverBorderColor = getColor(borderOptions); focusBorderColor = hoverBorderColor; activeBorderColor = getColor({ ...borderOptions, ...offset100 }); } else { borderColor = getColor(borderOptions); hoverBorderColor = getColor({ ...borderOptions, ...offset100 }); activeBorderColor = getColor({ ...borderOptions, ...offset200 }); } } backgroundVariable = 'background.primaryEmphasis'; foregroundVariable = $isNeutral ? 'foreground.default' : 'foreground.primary'; } const hoverBackgroundColor = getColor({ theme, variable: backgroundVariable, transparency: theme.opacity[100] }); const activeBackgroundColor = getColor({ theme, variable: backgroundVariable, transparency: theme.opacity[200] }); const foregroundOptions = { theme, variable: foregroundVariable }; const foregroundColor = getColor(foregroundOptions); let hoverForegroundColor; let activeForegroundColor; let iconForegroundColor; let hoverIconForegroundColor; let activeIconForegroundColor; if ($isNeutral) { const iconOptions = { theme, variable: 'foreground.subtle' }; iconForegroundColor = getColor(iconOptions); hoverIconForegroundColor = getColor({ ...iconOptions, ...offset100 }); activeIconForegroundColor = getColor({ ...iconOptions, ...offset200 }); } else { hoverForegroundColor = getColor({ ...foregroundOptions, ...offset100 }); activeForegroundColor = getColor({ ...foregroundOptions, ...offset200 }); } retVal = css(["outline-color:transparent;border-color:", ";background-color:transparent;color:", ";&:hover{border-color:", ";background-color:", ";color:", ";}", " &:active,&[aria-pressed='true'],&[aria-pressed='mixed']{border-color:", ";background-color:", ";color:", ";}&:disabled{border-color:transparent;background-color:", ";color:", ";}& ", "{color:", ";}&:hover ", ",&:focus-visible ", "{color:", ";}&:active ", "{color:", ";}&:disabled ", "{color:", ";}"], borderColor, foregroundColor, hoverBorderColor, hoverBackgroundColor, hoverForegroundColor, focusStyles({ theme, inset: $focusInset, styles: { borderColor: focusBorderColor } }), activeBorderColor, activeBackgroundColor, activeForegroundColor, disabledBackgroundColor, disabledForegroundColor, StyledIcon, iconForegroundColor, StyledIcon, StyledIcon, hoverIconForegroundColor, StyledIcon, activeIconForegroundColor, StyledIcon, disabledForegroundColor); } return retVal; }; const groupStyles = _ref2 => { let { theme, $isPrimary, $isBasic, $isPill, $focusInset } = _ref2; const { rtl, borderWidths, borders } = theme; const startPosition = rtl ? 'right' : 'left'; const endPosition = rtl ? 'left' : 'right'; const marginOffset = borderWidths.sm; const marginDisplacement = `${$isPrimary || $isBasic ? '' : '-'}${marginOffset}`; const iconMarginDisplacement = $isPill && '-2px'; const disabledBackgroundColor = !$isPrimary && getColor({ theme, variable: 'background.disabled' }); const borderColor = $isBasic ? 'transparent' : 'revert'; const focusColor = getColor({ theme, variable: 'border.primaryEmphasis' }); const focusBoxShadow = $isBasic && !$isPrimary && getFocusBoxShadow({ theme, inset: $focusInset, spacerColor: { hue: focusColor }, color: { hue: 'transparent' } }); return css(["position:relative;transition:border-color 0.1s ease-in-out,background-color 0.1s ease-in-out,box-shadow 0.1s ease-in-out,color 0.1s ease-in-out,margin-", " 0.1s ease-in-out,outline-color 0.1s ease-in-out,z-index 0.25s ease-in-out;border:", " ", ";", "{border-color:", ";box-shadow:", ";}&:hover,&:active,", "{z-index:1;}&:disabled{z-index:-1;background-color:", ";}&:not(:first-of-type){margin-", ":", ";}&:not(:first-of-type):disabled{margin-", ":", ";}&:not(:first-of-type):not(:last-of-type){border-radius:0;}&:first-of-type:not(:last-of-type){border-top-", "-radius:0;border-bottom-", "-radius:0;}&:last-of-type:not(:first-of-type){border-top-", "-radius:0;border-bottom-", "-radius:0;}&:first-of-type:not(:last-of-type) ", "{margin-", ":", ";}&:last-of-type:not(:first-of-type) ", "{margin-", ":", ";}"], startPosition, borders.sm, borderColor, SELECTOR_FOCUS_VISIBLE, focusColor, focusBoxShadow, SELECTOR_FOCUS_VISIBLE, disabledBackgroundColor, startPosition, marginDisplacement, startPosition, marginOffset, endPosition, endPosition, startPosition, startPosition, StyledIcon, endPosition, iconMarginDisplacement, StyledIcon, startPosition, iconMarginDisplacement); }; const iconStyles = props => { const $size = props.$size === 'small' ? props.theme.iconSizes.sm : props.theme.iconSizes.md; return css(["width:", ";min-width:", ";height:", ";vertical-align:", ";"], $size, $size, $size, props.$isLink && 'middle'); }; const sizeStyles = props => { let retVal; if (props.$isLink) { retVal = css(["padding:0;font-size:inherit;"]); } else { const height = getHeight(props); const lineHeight = math(`${height} - (${props.theme.borderWidths.sm} * 2)`); let padding; let fontSize; if (props.$size === 'small') { fontSize = props.theme.fontSizes.sm; padding = `${props.theme.space.base * 3}px`; } else { fontSize = props.theme.fontSizes.md; if (props.$size === 'large') { padding = `${props.theme.space.base * 5}px`; } else { padding = `${props.theme.space.base * 4}px`; } } retVal = css(["padding:0 ", ";height:", ";line-height:", ";font-size:", ";"], em(math(`${padding} - ${props.theme.borderWidths.sm}`), fontSize), height, lineHeight, fontSize); } return retVal; }; const StyledButton = styled.button.attrs(props => ({ 'data-garden-id': props['data-garden-id'] || COMPONENT_ID, 'data-garden-version': '9.5.4', type: props.type || 'button' })).withConfig({ displayName: "StyledButton", componentId: "sc-qe3ace-0" })(["display:", ";align-items:", ";justify-content:", ";transition:border-color 0.25s ease-in-out,box-shadow 0.1s ease-in-out,background-color 0.25s ease-in-out,color 0.25s ease-in-out,outline-color 0.1s ease-in-out,z-index 0.25s ease-in-out;margin:0;border:", ";border-radius:", ";cursor:pointer;width:", ";overflow:hidden;text-decoration:", ";text-overflow:ellipsis;white-space:", ";font-family:inherit;font-weight:", ";-webkit-font-smoothing:subpixel-antialiased;box-sizing:border-box;user-select:", ";-webkit-touch-callout:none;", ";&::-moz-focus-inner{border:0;padding:0;}", "{text-decoration:none;}&:hover{text-decoration:", ";}&:active,&[aria-pressed='true'],&[aria-pressed='mixed']{transition:border-color 0.1s ease-in-out,background-color 0.1s ease-in-out,box-shadow 0.1s ease-in-out,color 0.1s ease-in-out,outline-color 0.1s ease-in-out,z-index 0.25s ease-in-out;text-decoration:", ";}", ";&:disabled{cursor:default;text-decoration:", ";}& ", "{", "}", " &&{", "}", ""], props => props.$isLink ? 'inline' : 'inline-flex', props => !props.$isLink && 'center', props => !props.$isLink && 'center', props => `${props.$isLink ? `0px solid` : props.theme.borders.sm} transparent`, props => getBorderRadius(props), props => props.$isStretched ? '100%' : '', props => props.$isUnderlined ? 'underline' : 'none', props => !props.$isLink && 'nowrap', props => props.$isLink ? 'inherit' : props.theme.fontWeights.regular, props => !props.$isLink && 'none', props => sizeStyles(props), SELECTOR_FOCUS_VISIBLE, props => props.$isLink ? 'underline' : 'none', props => props.$isLink ? 'underline' : 'none', props => colorStyles(props), props => props.$isLink && 'none', StyledIcon, props => iconStyles(props), StyledSplitButton, props => groupStyles(props), componentStyles); export { COMPONENT_ID, StyledButton, getHeight };