@atlaskit/button
Version:
A button triggers an event or action. They let users know what will happen next.
218 lines (210 loc) • 10.4 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
var _react = _interopRequireWildcard(require("react"));
var _react2 = require("@emotion/react");
var _analyticsNext = require("@atlaskit/analytics-next");
var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop"));
var _useAutoFocus = _interopRequireDefault(require("@atlaskit/ds-lib/use-auto-focus"));
var _focusRing = _interopRequireDefault(require("@atlaskit/focus-ring"));
var _interactionContext = _interopRequireDefault(require("@atlaskit/interaction-context"));
var _colors = require("@atlaskit/theme/colors");
var _blockEvents = _interopRequireDefault(require("./block-events"));
var _css = require("./css");
var _getIfVisuallyHiddenChildren = require("./get-if-visually-hidden-children");
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
*/
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
// eslint-disable-next-line no-duplicate-imports
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
// 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 = (0, _react2.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 = (0, _react2.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 || (0, _getIfVisuallyHiddenChildren.getIfVisuallyHiddenChildren)(children)) {
return null;
}
return spacingStyles;
};
var getChildren = function getChildren(children, childrenStyles) {
if ((0, _getIfVisuallyHiddenChildren.getIfVisuallyHiddenChildren)(children)) {
return children;
}
return children ? (0, _react2.jsx)("span", {
css: childrenStyles
}, children) : null;
};
var ButtonBase = /*#__PURE__*/_react.default.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.default : _props$onClick,
onFocus = props.onFocus,
_props$onMouseDown = props.onMouseDown,
providedOnMouseDown = _props$onMouseDown === void 0 ? _noop.default : _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 = (0, _objectWithoutProperties2.default)(props, _excluded);
var ourRef = (0, _react.useRef)();
var setRef = (0, _react.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
(0, _useAutoFocus.default)(ourRef, autoFocus);
var interactionContext = (0, _react.useContext)(_interactionContext.default);
var handleClick = (0, _react.useCallback)(function (e, analyticsEvent) {
interactionContext && interactionContext.tracePress(interactionName, e.timeStamp);
providedOnClick(e, analyticsEvent);
}, [providedOnClick, interactionContext, interactionName]);
var onClick = (0, _analyticsNext.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 = (0, _react.useCallback)(function (event) {
event.preventDefault();
providedOnMouseDown(event);
}, [providedOnMouseDown]);
// Lose focus when becoming disabled (standard button behaviour)
(0, _react.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 = (0, _react2.css)((0, _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(_colors.N500, ")") : "var(--ds-icon-warning-inverse, ".concat(_colors.N500, ")"), " !important")
}
};
}
return (0, _react2.jsx)(_focusRing.default, null, (0, _react2.jsx)(Component, (0, _extends2.default)({}, 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
}, (0, _blockEvents.default)({
isInteractive: isInteractive
})), iconBefore ? (0, _react2.jsx)("span", {
css: [fadeStyles,
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
(0, _css.getIconStyle)({
spacing: spacing
}), getSpacingFix(children, iconBeforeSpacingFixStyle)]
}, iconBefore) : null, getChildren(children, [fadeStyles, (0, _css.getContentStyle)({
spacing: spacing
})]), iconAfter ? (0, _react2.jsx)("span", {
css: [fadeStyles,
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
(0, _css.getIconStyle)({
spacing: spacing
}), getSpacingFix(children, iconAfterSpacingFixStyle)]
}, iconAfter) : null, overlay ? (0, _react2.jsx)("span", {
css: [_css.overlayCss, spinnerHackCss]
}, overlay) : null));
});
// eslint-disable-next-line @repo/internal/react/require-jsdoc
var _default = exports.default = ButtonBase;