UNPKG

@atlaskit/button

Version:

A button triggers an event or action. They let users know what will happen next.

211 lines (202 loc) 8.97 kB
import _extends from "@babel/runtime/helpers/extends"; import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; var _excluded = ["analyticsContext", "appearance", "autoFocus", "buttonCss", "children", "className", "href", "component", "iconAfter", "iconBefore", "interactionName", "isDisabled", "isSelected", "onBlur", "onClick", "onFocus", "onMouseDown", "overlay", "shouldFitContainer", "spacing", "tabIndex", "type", "testId"]; /* eslint-disable @atlaskit/design-system/consistent-css-prop-usage */ /** * @jsxRuntime classic * @jsx jsx */ import React, { useCallback, useContext, useEffect, useRef } from 'react'; // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766 import { css, jsx } from '@emotion/react'; import { usePlatformLeafEventHandler } from '@atlaskit/analytics-next'; import noop from '@atlaskit/ds-lib/noop'; import useAutoFocus from '@atlaskit/ds-lib/use-auto-focus'; import FocusRing from '@atlaskit/focus-ring'; // eslint-disable-next-line no-duplicate-imports import InteractionContext from '@atlaskit/interaction-context'; import { N500 } from '@atlaskit/theme/colors'; import blockEvents from './block-events'; import { getContentStyle, getFadingCss, getIconStyle, overlayCss } from './css'; import { getIfVisuallyHiddenChildren } from './get-if-visually-hidden-children'; // Disabled buttons will still publish events for nested elements in webkit. // We are disabling pointer events on child elements so that // the button will always be the target of events // Note: firefox does not have this behaviour for child elements var noPointerEventsOnChildrenCss = { '> *': { pointerEvents: 'none' } }; /** * These CSS variables consumed by the new icons, to allow them to have appropriate * padding inside Button while also maintaining spacing for the existing icons. * * These styles can be removed once the new icons are fully rolled out, feature flag * platform-visual-refresh-icons is cleaned up, * and we bump Button to set padding based on the new icons. */ var iconBeforeSpacingFixStyle = css({ '--ds--button--new-icon-padding-end': "var(--ds-space-025, 2px)", '--ds--button--new-icon-padding-start': "var(--ds-space-050, 4px)", marginInlineStart: "var(--ds-space-negative-025, -2px)" }); var iconAfterSpacingFixStyle = css({ '--ds--button--new-icon-padding-end': "var(--ds-space-050, 4px)", '--ds--button--new-icon-padding-start': "var(--ds-space-025, 2px)", marginInlineEnd: "var(--ds-space-negative-025, -2px)" }); var getSpacingFix = function getSpacingFix(children, spacingStyles) { if (!children || getIfVisuallyHiddenChildren(children)) { return null; } return spacingStyles; }; var getChildren = function getChildren(children, childrenStyles) { if (getIfVisuallyHiddenChildren(children)) { return children; } return children ? jsx("span", { css: childrenStyles }, children) : null; }; var ButtonBase = /*#__PURE__*/React.forwardRef(function ButtonBase(props, ref) { var analyticsContext = props.analyticsContext, _props$appearance = props.appearance, appearance = _props$appearance === void 0 ? 'default' : _props$appearance, _props$autoFocus = props.autoFocus, autoFocus = _props$autoFocus === void 0 ? false : _props$autoFocus, buttonCss = props.buttonCss, children = props.children, className = props.className, href = props.href, _props$component = props.component, Component = _props$component === void 0 ? href ? 'a' : 'button' : _props$component, iconAfter = props.iconAfter, iconBefore = props.iconBefore, interactionName = props.interactionName, _props$isDisabled = props.isDisabled, isDisabled = _props$isDisabled === void 0 ? false : _props$isDisabled, _props$isSelected = props.isSelected, isSelected = _props$isSelected === void 0 ? false : _props$isSelected, onBlur = props.onBlur, _props$onClick = props.onClick, providedOnClick = _props$onClick === void 0 ? noop : _props$onClick, onFocus = props.onFocus, _props$onMouseDown = props.onMouseDown, providedOnMouseDown = _props$onMouseDown === void 0 ? noop : _props$onMouseDown, overlay = props.overlay, shouldFitContainer = props.shouldFitContainer, _props$spacing = props.spacing, spacing = _props$spacing === void 0 ? 'default' : _props$spacing, _props$tabIndex = props.tabIndex, tabIndex = _props$tabIndex === void 0 ? 0 : _props$tabIndex, _props$type = props.type, type = _props$type === void 0 ? !href ? 'button' : undefined : _props$type, testId = props.testId, rest = _objectWithoutProperties(props, _excluded); var ourRef = useRef(); var setRef = useCallback(function (node) { ourRef.current = node; if (ref == null) { return; } if (typeof ref === 'function') { ref(node); return; } // We can write to ref's `current` property, but Typescript does not like it. // @ts-ignore ref.current = node; }, [ourRef, ref]); // Cross browser auto focusing is pretty broken, so we are doing it ourselves useAutoFocus(ourRef, autoFocus); var interactionContext = useContext(InteractionContext); var handleClick = useCallback(function (e, analyticsEvent) { interactionContext && interactionContext.tracePress(interactionName, e.timeStamp); providedOnClick(e, analyticsEvent); }, [providedOnClick, interactionContext, interactionName]); var onClick = usePlatformLeafEventHandler({ fn: handleClick, action: 'clicked', componentName: 'button', packageName: "@atlaskit/button", packageVersion: "0.0.0-development", analyticsData: analyticsContext }); // Button currently calls preventDefault, which is not standard button behaviour var onMouseDown = useCallback(function (event) { event.preventDefault(); providedOnMouseDown(event); }, [providedOnMouseDown]); // Lose focus when becoming disabled (standard button behaviour) useEffect(function () { var el = ourRef.current; if (isDisabled && el && el === document.activeElement) { el.blur(); } }, [isDisabled]); // we are 'disabling' input with a button when there is an overlay var hasOverlay = Boolean(overlay); // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766 var fadeStyles = css(getFadingCss({ hasOverlay: hasOverlay })); var isInteractive = !isDisabled && !hasOverlay; /** * HACK: Spinner needs to have different colours in the "new" tokens design compared to the old design. * For now, while we support both, these styles reach into Spinner when a theme is set, applies the right color. * Ticket to remove: https://product-fabric.atlassian.net/browse/DSP-2067. */ var spinnerHackCss = {}; if (isSelected || isDisabled || appearance === 'warning') { spinnerHackCss = { '[data-theme] & circle': { stroke: "".concat(isSelected || isDisabled ? "var(--ds-icon-subtle, ".concat(N500, ")") : "var(--ds-icon-warning-inverse, ".concat(N500, ")"), " !important") } }; } return jsx(FocusRing, null, jsx(Component, _extends({}, rest, { ref: setRef // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 , className: className, css: [buttonCss, isInteractive ? null : noPointerEventsOnChildrenCss] // using undefined so that the property doesn't exist when false , "data-has-overlay": hasOverlay ? true : undefined, "data-testid": testId, disabled: isDisabled, href: isInteractive ? href : undefined, onBlur: onBlur, onClick: onClick, onFocus: onFocus, onMouseDown: onMouseDown // Adding a tab index so element is always focusable, even when not a <button> or <a> // Disabling focus via keyboard navigation when disabled // as this is standard button behaviour , tabIndex: isDisabled ? -1 : tabIndex, type: type }, blockEvents({ isInteractive: isInteractive })), iconBefore ? jsx("span", { css: [fadeStyles, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766 getIconStyle({ spacing: spacing }), getSpacingFix(children, iconBeforeSpacingFixStyle)] }, iconBefore) : null, getChildren(children, [fadeStyles, getContentStyle({ spacing: spacing })]), iconAfter ? jsx("span", { css: [fadeStyles, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766 getIconStyle({ spacing: spacing }), getSpacingFix(children, iconAfterSpacingFixStyle)] }, iconAfter) : null, overlay ? jsx("span", { css: [overlayCss, spinnerHackCss] }, overlay) : null)); }); // eslint-disable-next-line @repo/internal/react/require-jsdoc export default ButtonBase;