UNPKG

@razorpay/blade

Version:

The Design System that powers Razorpay

488 lines (484 loc) 21.3 kB
import _defineProperty from '@babel/runtime/helpers/defineProperty'; import _slicedToArray from '@babel/runtime/helpers/slicedToArray'; import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties'; import React__default from 'react'; import styled from 'styled-components'; import StyledBaseButton from './StyledBaseButton.web.js'; import { backgroundColor, textColor, buttonIconOnlySizeToIconSizeMap, buttonSizeToIconSizeMap, buttonSizeToSpinnerSizeMap, minHeight, buttonIconOnlyHeightWidth, buttonIconPadding, buttonPadding, typography } from './buttonTokens.js'; import AnimatedButtonContent from './AnimatedButtonContent.web.js'; import getIn from '../../../utils/lodashButBetter/get.js'; import '../../../utils/index.js'; import { useButtonGroupContext } from '../../ButtonGroup/ButtonGroupContext.js'; import '../../Box/styledProps/index.js'; import '../../Typography/BaseText/index.js'; import '../../BladeProvider/index.js'; import '../../LiveAnnouncer/index.web.js'; import '../../Spinner/BaseSpinner/index.js'; import '../../Box/BaseBox/index.js'; import '../../../utils/assignWithoutSideEffects/index.js'; import '../../../utils/usePrevious/index.js'; import '../../../utils/makeSize/index.js'; import '../../../utils/makeBorderSize/index.js'; import '../../../utils/makeAccessible/index.js'; import '../../../utils/makeSpace/index.js'; import '../../../utils/metaAttribute/index.js'; import '../../../utils/getStringChildren/index.js'; import '../../../utils/logger/index.js'; import '../../../utils/makeAnalyticsAttribute/index.js'; import { jsx, jsxs } from 'react/jsx-runtime'; import { isReactNative } from '../../../utils/platform/isReactNative.js'; import { throwBladeError } from '../../../utils/logger/logger.js'; import { makeSize } from '../../../utils/makeSize/makeSize.js'; import { makeSpace } from '../../../utils/makeSpace/makeSpace.js'; import { makeBorderSize } from '../../../utils/makeBorderSize/makeBorderSize.js'; import { BaseBox } from '../../Box/BaseBox/BaseBox.web.js'; import useTheme from '../../BladeProvider/useTheme.js'; import { getStringFromReactText } from '../../../utils/getStringChildren/getStringChildren.js'; import { usePrevious } from '../../../utils/usePrevious/usePrevious.js'; import { announce } from '../../LiveAnnouncer/LiveAnnouncer.web.js'; import { makeAccessible } from '../../../utils/makeAccessible/makeAccessible.web.js'; import { metaAttribute } from '../../../utils/metaAttribute/metaAttribute.web.js'; import { MetaConstants } from '../../../utils/metaAttribute/metaConstants.js'; import { getStyledProps } from '../../Box/styledProps/getStyledProps.js'; import { makeAnalyticsAttribute } from '../../../utils/makeAnalyticsAttribute/makeAnalyticsAttribute.js'; import { BaseSpinner } from '../../Spinner/BaseSpinner/BaseSpinner.js'; import { BaseText } from '../../Typography/BaseText/BaseText.web.js'; import { assignWithoutSideEffects } from '../../../utils/assignWithoutSideEffects/assignWithoutSideEffects.js'; var _excluded = ["href", "target", "rel", "tabIndex", "id", "variant", "color", "size", "icon", "iconPosition", "isDisabled", "isFullWidth", "isLoading", "onClick", "onBlur", "onKeyDown", "type", "children", "testID", "onFocus", "onMouseLeave", "onMouseMove", "onMouseDown", "onPointerDown", "onPointerEnter", "accessibilityProps", "onTouchEnd", "onTouchStart"]; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } var getRenderElement = function getRenderElement(href) { if (isReactNative()) { return undefined; // as property doesn't work with react native } if (href) { return 'a'; } return 'button'; }; var getBackgroundColorToken = function getBackgroundColorToken(_ref) { var property = _ref.property, variant = _ref.variant, state = _ref.state, color = _ref.color; var _state = state === 'focus' || state === 'hover' ? 'highlighted' : state; var tokens = backgroundColor(property); if (color === 'white') { return tokens.white[variant][_state]; } if (color && color !== 'primary') { if (variant === 'tertiary') { throw new Error("Tertiary variant can only be used with color: \"primary\" or \"white\" but received \"".concat(color, "\"")); } return tokens.color(color)[variant][_state]; } return tokens.base[variant][_state]; }; var getTextColorToken = function getTextColorToken(_ref2) { var property = _ref2.property, variant = _ref2.variant, state = _ref2.state, color = _ref2.color; var tokens = textColor(property); var _state = state === 'focus' || state === 'hover' ? 'highlighted' : state; if (color === 'white') { return tokens.white[variant][_state]; } if (color && color !== 'primary') { if (variant === 'tertiary') { throw new Error("Tertiary variant can only be used with color: \"primary\" or \"white\" but received \"".concat(color, "\"")); } return tokens.color(color)[variant][_state]; } return tokens.base[variant][_state]; }; var getProps = function getProps(_ref3) { var buttonTypographyTokens = _ref3.buttonTypographyTokens, childrenString = _ref3.childrenString, isDisabled = _ref3.isDisabled, size = _ref3.size, theme = _ref3.theme, variant = _ref3.variant, color = _ref3.color, hasIcon = _ref3.hasIcon; if (variant === 'tertiary' && color !== 'primary' && color !== 'white') { throwBladeError({ moduleName: 'BaseButton', message: "Tertiary variant can only be used with color: \"primary\" or \"white\" but received \"".concat(color, "\"") }); } var isIconOnly = hasIcon && (!childrenString || (childrenString === null || childrenString === void 0 ? void 0 : childrenString.trim().length) === 0); var props = { iconSize: isIconOnly ? buttonIconOnlySizeToIconSizeMap[size] : buttonSizeToIconSizeMap[size], spinnerSize: buttonSizeToSpinnerSizeMap[size], fontSize: buttonTypographyTokens.fonts.size[size], lineHeight: buttonTypographyTokens.lineHeights[size], minHeight: makeSize(minHeight[size]), height: isIconOnly ? buttonIconOnlyHeightWidth[size] : undefined, width: isIconOnly ? buttonIconOnlyHeightWidth[size] : undefined, iconPadding: hasIcon && childrenString !== null && childrenString !== void 0 && childrenString.trim() ? "spacing.".concat(buttonIconPadding[size]) : undefined, iconColor: getTextColorToken({ property: 'icon', variant: variant, color: color, state: 'default' }), textColor: getTextColorToken({ property: 'text', variant: variant, color: color, state: 'default' }), buttonPaddingTop: isIconOnly ? makeSpace(0) : makeSpace(theme.spacing[buttonPadding[size].top]), buttonPaddingBottom: isIconOnly ? makeSpace(0) : makeSpace(theme.spacing[buttonPadding[size].bottom]), buttonPaddingLeft: isIconOnly ? makeSpace(0) : makeSpace(theme.spacing[buttonPadding[size].left]), buttonPaddingRight: isIconOnly ? makeSpace(0) : makeSpace(theme.spacing[buttonPadding[size].right]), text: childrenString === null || childrenString === void 0 ? void 0 : childrenString.trim(), defaultBackgroundColor: getIn(theme.colors, getBackgroundColorToken({ property: 'background', variant: variant, color: color, state: 'default' })), defaultBorderColor: getIn(theme.colors, getBackgroundColorToken({ property: 'border', variant: variant, color: color, state: 'default' })), hoverBackgroundColor: getIn(theme.colors, getBackgroundColorToken({ property: 'background', variant: variant, color: color, state: 'hover' })), hoverBorderColor: getIn(theme.colors, getBackgroundColorToken({ property: 'border', variant: variant, color: color, state: 'hover' })), focusBackgroundColor: getIn(theme.colors, getBackgroundColorToken({ property: 'background', variant: variant, color: color, state: 'focus' })), focusBorderColor: getIn(theme.colors, getBackgroundColorToken({ property: 'border', variant: variant, color: color, state: 'focus' })), focusRingColor: getIn(theme.colors, 'surface.border.primary.muted'), borderWidth: variant == 'secondary' ? makeBorderSize(theme.border.width.thin) : '0px', borderRadius: makeBorderSize(theme.border.radius.medium), motionDuration: 'duration.xquick', motionEasing: 'easing.standard' }; if (isDisabled) { var disabledBackgroundColor = getIn(theme.colors, getBackgroundColorToken({ property: 'background', variant: variant, color: color, state: 'disabled' })); var disabledBorderColor = getIn(theme.colors, getBackgroundColorToken({ property: 'border', variant: variant, color: color, state: 'disabled' })); props.iconColor = getTextColorToken({ property: 'icon', variant: variant, color: color, state: 'disabled' }); props.textColor = getTextColorToken({ property: 'text', variant: variant, color: color, state: 'disabled' }); props.defaultBackgroundColor = disabledBackgroundColor; props.defaultBorderColor = disabledBorderColor; props.hoverBackgroundColor = disabledBackgroundColor; props.hoverBorderColor = disabledBorderColor; props.focusBackgroundColor = disabledBackgroundColor; props.focusBorderColor = disabledBorderColor; } return props; }; var ButtonContent = /*#__PURE__*/styled(BaseBox).withConfig({ displayName: "BaseButton__ButtonContent", componentId: "zf1huq-0" })(function (_ref4) { var isHidden = _ref4.isHidden; return { opacity: isHidden ? 0 : 1 }; }); var _BaseButton = function _BaseButton(_ref5, ref) { var _buttonGroupProps$siz, _buttonGroupProps$var, _buttonGroupProps$col, _accessibilityProps$r, _buttonGroupProps$isF; var href = _ref5.href, target = _ref5.target, rel = _ref5.rel, tabIndex = _ref5.tabIndex, id = _ref5.id, _ref5$variant = _ref5.variant, variant = _ref5$variant === void 0 ? 'primary' : _ref5$variant, _ref5$color = _ref5.color, color = _ref5$color === void 0 ? 'primary' : _ref5$color, _ref5$size = _ref5.size, size = _ref5$size === void 0 ? 'medium' : _ref5$size, Icon = _ref5.icon, _ref5$iconPosition = _ref5.iconPosition, iconPosition = _ref5$iconPosition === void 0 ? 'left' : _ref5$iconPosition, _ref5$isDisabled = _ref5.isDisabled, isDisabled = _ref5$isDisabled === void 0 ? false : _ref5$isDisabled, _ref5$isFullWidth = _ref5.isFullWidth, isFullWidth = _ref5$isFullWidth === void 0 ? false : _ref5$isFullWidth, _ref5$isLoading = _ref5.isLoading, isLoading = _ref5$isLoading === void 0 ? false : _ref5$isLoading, onClick = _ref5.onClick, onBlur = _ref5.onBlur, _onKeyDown = _ref5.onKeyDown, _ref5$type = _ref5.type, type = _ref5$type === void 0 ? 'button' : _ref5$type, children = _ref5.children, testID = _ref5.testID, onFocus = _ref5.onFocus, onMouseLeave = _ref5.onMouseLeave, onMouseMove = _ref5.onMouseMove, _onMouseDown = _ref5.onMouseDown, onPointerDown = _ref5.onPointerDown, onPointerEnter = _ref5.onPointerEnter, accessibilityProps = _ref5.accessibilityProps, _onTouchEnd = _ref5.onTouchEnd, _onTouchStart = _ref5.onTouchStart, rest = _objectWithoutProperties(_ref5, _excluded); var _useTheme = useTheme(), theme = _useTheme.theme; var buttonGroupProps = useButtonGroupContext(); var _React$useState = React__default.useState(false), _React$useState2 = _slicedToArray(_React$useState, 2), isPressed = _React$useState2[0], setIsPressed = _React$useState2[1]; var isLink = Boolean(href); var childrenString = getStringFromReactText(children); var isChildrenComponent = /*#__PURE__*/React__default.isValidElement(children); // Button cannot be disabled when its rendered as Link // button should be allowed to be disabled in any case... // either through button group or we should allow to disable an individual button var disabled = buttonGroupProps.isDisabled || isLoading || isDisabled && !isLink; if (false) { if (!Icon && !(childrenString !== null && childrenString !== void 0 && childrenString.trim())) { throwBladeError({ message: 'At least one of icon or text is required to render a button.', moduleName: 'BaseButton' }); } } var prevLoading = usePrevious(isLoading); React__default.useEffect(function () { if (isLoading) announce('Started loading'); if (!isLoading && prevLoading) announce('Stopped loading'); }, [isLoading, prevLoading]); var _getProps = getProps({ buttonTypographyTokens: typography, childrenString: childrenString, isDisabled: disabled, size: (_buttonGroupProps$siz = buttonGroupProps.size) !== null && _buttonGroupProps$siz !== void 0 ? _buttonGroupProps$siz : size, variant: (_buttonGroupProps$var = buttonGroupProps.variant) !== null && _buttonGroupProps$var !== void 0 ? _buttonGroupProps$var : variant, theme: theme, color: (_buttonGroupProps$col = buttonGroupProps.color) !== null && _buttonGroupProps$col !== void 0 ? _buttonGroupProps$col : color, hasIcon: Boolean(Icon) }), defaultBorderColor = _getProps.defaultBorderColor, defaultBackgroundColor = _getProps.defaultBackgroundColor, minHeight = _getProps.minHeight, height = _getProps.height, width = _getProps.width, buttonPaddingTop = _getProps.buttonPaddingTop, buttonPaddingBottom = _getProps.buttonPaddingBottom, buttonPaddingLeft = _getProps.buttonPaddingLeft, buttonPaddingRight = _getProps.buttonPaddingRight, focusBorderColor = _getProps.focusBorderColor, focusBackgroundColor = _getProps.focusBackgroundColor, focusRingColor = _getProps.focusRingColor, fontSize = _getProps.fontSize, hoverBorderColor = _getProps.hoverBorderColor, hoverBackgroundColor = _getProps.hoverBackgroundColor, iconColor = _getProps.iconColor, iconSize = _getProps.iconSize, iconPadding = _getProps.iconPadding, spinnerSize = _getProps.spinnerSize, lineHeight = _getProps.lineHeight, text = _getProps.text, textColor = _getProps.textColor, borderWidth = _getProps.borderWidth, borderRadius = _getProps.borderRadius, motionDuration = _getProps.motionDuration, motionEasing = _getProps.motionEasing; var renderElement = React__default.useMemo(function () { return getRenderElement(href); }, [href]); var defaultRole = isLink ? 'link' : 'button'; var handlePointerPressedIn = React__default.useCallback(function () { if (disabled) return; setIsPressed(true); }, [disabled]); var handlePointerPressedOut = React__default.useCallback(function () { if (disabled) return; setIsPressed(false); }, [disabled]); var handleKeyboardPressedIn = React__default.useCallback(function (e) { if (disabled) return; if (e.key === ' ' || e.key === 'Enter') { setIsPressed(true); } }, [disabled]); var handleKeyboardPressedOut = React__default.useCallback(function (e) { if (disabled) return; if (e.key === ' ' || e.key === 'Enter') { setIsPressed(false); } }, [disabled]); return /*#__PURE__*/jsx(StyledBaseButton, _objectSpread(_objectSpread(_objectSpread(_objectSpread({ ref: ref, id: id // eslint-disable-next-line @typescript-eslint/ban-ts-comment, @typescript-eslint/prefer-ts-expect-error // @ts-ignore: On React Native it will always be undefined but TS doesn't understand that , as: renderElement, href: href, target: target, rel: rel, accessibilityProps: _objectSpread({}, makeAccessible(_objectSpread(_objectSpread({}, accessibilityProps), {}, { role: (_accessibilityProps$r = accessibilityProps === null || accessibilityProps === void 0 ? void 0 : accessibilityProps.role) !== null && _accessibilityProps$r !== void 0 ? _accessibilityProps$r : defaultRole }))), variant: variant, isLoading: isLoading, disabled: disabled, defaultBorderColor: defaultBorderColor, minHeight: minHeight, buttonPaddingTop: buttonPaddingTop, buttonPaddingBottom: buttonPaddingBottom, buttonPaddingLeft: buttonPaddingLeft, buttonPaddingRight: buttonPaddingRight, defaultBackgroundColor: defaultBackgroundColor, focusBorderColor: focusBorderColor, focusBackgroundColor: focusBackgroundColor, focusRingColor: focusRingColor, hoverBorderColor: hoverBorderColor, hoverBackgroundColor: hoverBackgroundColor, isFullWidth: (_buttonGroupProps$isF = buttonGroupProps.isFullWidth) !== null && _buttonGroupProps$isF !== void 0 ? _buttonGroupProps$isF : isFullWidth, onClick: onClick, onBlur: onBlur, onFocus: onFocus, onMouseLeave: onMouseLeave, onMouseMove: onMouseMove, tabIndex: tabIndex, onPointerDown: onPointerDown, onPointerEnter: onPointerEnter // Setting type for web fails it on native typecheck and vice versa , onKeyDown: function onKeyDown(event) { handleKeyboardPressedIn(event); _onKeyDown === null || _onKeyDown === void 0 ? void 0 : _onKeyDown(event); }, onTouchStart: function onTouchStart(event) { handlePointerPressedIn(); _onTouchStart === null || _onTouchStart === void 0 ? void 0 : _onTouchStart(event); }, onTouchEnd: function onTouchEnd(event) { handlePointerPressedOut(); _onTouchEnd === null || _onTouchEnd === void 0 ? void 0 : _onTouchEnd(event); }, type: type, borderWidth: borderWidth, borderRadius: borderRadius, motionDuration: motionDuration, motionEasing: motionEasing, height: height, width: width, isPressed: isPressed, onMouseDown: function onMouseDown(event) { handlePointerPressedIn(); _onMouseDown === null || _onMouseDown === void 0 ? void 0 : _onMouseDown(event); }, onMouseUp: handlePointerPressedOut, onMouseOut: handlePointerPressedOut, onKeyUp: handleKeyboardPressedOut }, metaAttribute({ name: MetaConstants.Button, testID: testID })), getStyledProps(rest)), makeAnalyticsAttribute(rest)), {}, { children: /*#__PURE__*/jsxs(AnimatedButtonContent, { motionDuration: motionDuration, motionEasing: motionEasing, isPressed: isPressed, children: [isLoading ? /*#__PURE__*/jsx(BaseBox, { display: "flex", justifyContent: "center", alignItems: "center", position: "absolute", top: "0px", left: "0px", bottom: "0px", right: "0px", zIndex: 1, children: /*#__PURE__*/jsx(BaseSpinner, { accessibilityLabel: "Loading", size: spinnerSize, color: color }) }) : null, /*#__PURE__*/jsxs(ButtonContent, { display: "flex", flexDirection: "row", alignItems: "center", justifyContent: "center", flex: 1, isHidden: isLoading, zIndex: 1, children: [Icon && iconPosition == 'left' ? /*#__PURE__*/jsx(BaseBox, { paddingRight: iconPadding, display: "flex", justifyContent: "center", alignItems: "center", children: /*#__PURE__*/jsx(Icon, { size: iconSize, color: iconColor }) }) : null, text ? isChildrenComponent ? children : /*#__PURE__*/jsx(BaseText, { lineHeight: lineHeight, fontSize: fontSize // figma and web have different font-smoothing properties // which causes web version of button text to look much bolder // than figma version. To fix this we are changing font-weight from 600 to 500 // https://forum.figma.com/t/why-does-a-font-weight-in-figma-seem-lighter-than-the-same-weight-in-the-browser/2207 , fontWeight: "medium", textAlign: "center", color: textColor, children: text }) : null, Icon && iconPosition == 'right' ? /*#__PURE__*/jsx(BaseBox, { paddingLeft: iconPadding, display: "flex", justifyContent: "center", alignItems: "center", children: /*#__PURE__*/jsx(Icon, { size: iconSize, color: iconColor }) }) : null] })] }) })); }; var BaseButton = /*#__PURE__*/assignWithoutSideEffects( /*#__PURE__*/React__default.forwardRef(_BaseButton), { displayName: 'BaseButton' }); export { BaseButton as default, getBackgroundColorToken, getTextColorToken }; //# sourceMappingURL=BaseButton.js.map