@atlaskit/button 16.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1485 -0
- package/LICENSE +13 -0
- package/README.md +13 -0
- package/__perf__/button.tsx +19 -0
- package/__perf__/custom.tsx +19 -0
- package/__perf__/customised.tsx +11 -0
- package/__perf__/default.tsx +5 -0
- package/__perf__/loading.tsx +5 -0
- package/__perf__/utils/example-runner.tsx +48 -0
- package/__perf__/utils/interaction-tasks.tsx +98 -0
- package/button-group/package.json +7 -0
- package/codemods/15.0.0-lite-mode.ts +49 -0
- package/codemods/15.1.1-data-testid.ts +173 -0
- package/codemods/__tests__/15.0.0-lite-mode/optimistic.ts +646 -0
- package/codemods/__tests__/15.0.0-lite-mode/safe.ts +223 -0
- package/codemods/__tests__/15.0.0-lite-mode/shared.ts +257 -0
- package/codemods/__tests__/15.1.1-data-testid/rename-data-testid.ts +186 -0
- package/codemods/__tests__/_framework.ts +47 -0
- package/codemods/helpers/15.0.0-runner.ts +169 -0
- package/codemods/helpers/helpers-generic.ts +662 -0
- package/codemods/optimistic-15.0.0-lite-mode.ts +279 -0
- package/codemods/readme.md +1 -0
- package/custom-theme-button/package.json +7 -0
- package/dist/cjs/button-group.js +50 -0
- package/dist/cjs/button.js +104 -0
- package/dist/cjs/custom-theme-button/custom-theme-button-types.js +5 -0
- package/dist/cjs/custom-theme-button/custom-theme-button.js +229 -0
- package/dist/cjs/custom-theme-button/index.js +23 -0
- package/dist/cjs/custom-theme-button/theme.js +108 -0
- package/dist/cjs/entry-points/button-group.js +15 -0
- package/dist/cjs/entry-points/custom-theme-button.js +25 -0
- package/dist/cjs/entry-points/loading-button.js +15 -0
- package/dist/cjs/entry-points/standard-button.js +15 -0
- package/dist/cjs/entry-points/types.js +5 -0
- package/dist/cjs/index.js +51 -0
- package/dist/cjs/loading-button.js +34 -0
- package/dist/cjs/shared/block-events.js +44 -0
- package/dist/cjs/shared/button-base.js +158 -0
- package/dist/cjs/shared/colors.js +409 -0
- package/dist/cjs/shared/css.js +265 -0
- package/dist/cjs/shared/get-is-only-single-icon.js +26 -0
- package/dist/cjs/shared/loading-spinner.js +45 -0
- package/dist/cjs/types.js +5 -0
- package/dist/cjs/version.json +5 -0
- package/dist/es2019/button-group.js +36 -0
- package/dist/es2019/button.js +69 -0
- package/dist/es2019/custom-theme-button/custom-theme-button-types.js +1 -0
- package/dist/es2019/custom-theme-button/custom-theme-button.js +164 -0
- package/dist/es2019/custom-theme-button/index.js +2 -0
- package/dist/es2019/custom-theme-button/theme.js +81 -0
- package/dist/es2019/entry-points/button-group.js +1 -0
- package/dist/es2019/entry-points/custom-theme-button.js +1 -0
- package/dist/es2019/entry-points/loading-button.js +1 -0
- package/dist/es2019/entry-points/standard-button.js +1 -0
- package/dist/es2019/entry-points/types.js +1 -0
- package/dist/es2019/index.js +6 -0
- package/dist/es2019/loading-button.js +17 -0
- package/dist/es2019/shared/block-events.js +37 -0
- package/dist/es2019/shared/button-base.js +127 -0
- package/dist/es2019/shared/colors.js +393 -0
- package/dist/es2019/shared/css.js +249 -0
- package/dist/es2019/shared/get-is-only-single-icon.js +19 -0
- package/dist/es2019/shared/loading-spinner.js +33 -0
- package/dist/es2019/types.js +1 -0
- package/dist/es2019/version.json +5 -0
- package/dist/esm/button-group.js +35 -0
- package/dist/esm/button.js +79 -0
- package/dist/esm/custom-theme-button/custom-theme-button-types.js +1 -0
- package/dist/esm/custom-theme-button/custom-theme-button.js +203 -0
- package/dist/esm/custom-theme-button/index.js +2 -0
- package/dist/esm/custom-theme-button/theme.js +90 -0
- package/dist/esm/entry-points/button-group.js +1 -0
- package/dist/esm/entry-points/custom-theme-button.js +1 -0
- package/dist/esm/entry-points/loading-button.js +1 -0
- package/dist/esm/entry-points/standard-button.js +1 -0
- package/dist/esm/entry-points/types.js +1 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/loading-button.js +19 -0
- package/dist/esm/shared/block-events.js +36 -0
- package/dist/esm/shared/button-base.js +135 -0
- package/dist/esm/shared/colors.js +393 -0
- package/dist/esm/shared/css.js +245 -0
- package/dist/esm/shared/get-is-only-single-icon.js +19 -0
- package/dist/esm/shared/loading-spinner.js +35 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/version.json +5 -0
- package/dist/types/button-group.d.ts +18 -0
- package/dist/types/button.d.ts +8 -0
- package/dist/types/custom-theme-button/custom-theme-button-types.d.ts +21 -0
- package/dist/types/custom-theme-button/custom-theme-button.d.ts +6 -0
- package/dist/types/custom-theme-button/index.d.ts +2 -0
- package/dist/types/custom-theme-button/theme.d.ts +21 -0
- package/dist/types/entry-points/button-group.d.ts +1 -0
- package/dist/types/entry-points/custom-theme-button.d.ts +2 -0
- package/dist/types/entry-points/loading-button.d.ts +2 -0
- package/dist/types/entry-points/standard-button.d.ts +2 -0
- package/dist/types/entry-points/types.d.ts +4 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/loading-button.d.ts +11 -0
- package/dist/types/shared/block-events.d.ts +3 -0
- package/dist/types/shared/button-base.d.ts +10 -0
- package/dist/types/shared/colors.d.ts +31 -0
- package/dist/types/shared/css.d.ts +22 -0
- package/dist/types/shared/get-is-only-single-icon.d.ts +2 -0
- package/dist/types/shared/loading-spinner.d.ts +4 -0
- package/dist/types/types.d.ts +51 -0
- package/extract-react-types/custom-theme-button-props.tsx +7 -0
- package/extract-react-types/loading-button-props.tsx +5 -0
- package/extract-react-types/shared-props.tsx +5 -0
- package/loading-button/package.json +7 -0
- package/package.json +83 -0
- package/standard-button/package.json +7 -0
- package/types/package.json +7 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
import React, { useCallback, useState } from 'react';
|
|
3
|
+
import GlobalTheme from '@atlaskit/theme/components';
|
|
4
|
+
import ButtonBase from '../shared/button-base';
|
|
5
|
+
import getIsOnlySingleIcon from '../shared/get-is-only-single-icon';
|
|
6
|
+
import LoadingSpinner from '../shared/loading-spinner';
|
|
7
|
+
import Theme, { defaultThemeFn, getSpecifiers } from './theme';
|
|
8
|
+
|
|
9
|
+
function getInteractionState({
|
|
10
|
+
isDisabled = false,
|
|
11
|
+
isActive = false,
|
|
12
|
+
isFocus = false,
|
|
13
|
+
isHover = false,
|
|
14
|
+
isSelected = false,
|
|
15
|
+
isLoading = false
|
|
16
|
+
}) {
|
|
17
|
+
if (isDisabled) {
|
|
18
|
+
return 'disabled';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (isSelected && isFocus) {
|
|
22
|
+
return 'focusSelected';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (isSelected) {
|
|
26
|
+
return 'selected';
|
|
27
|
+
} // not allowing active or focus style changes while loading
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if (!isLoading && isActive) {
|
|
31
|
+
return 'active';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!isLoading && isHover) {
|
|
35
|
+
return 'hover';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (isFocus) {
|
|
39
|
+
return 'focus';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return 'default';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const initial = {
|
|
46
|
+
isHover: false,
|
|
47
|
+
isActive: false,
|
|
48
|
+
isFocus: false
|
|
49
|
+
};
|
|
50
|
+
const CustomThemeButton = /*#__PURE__*/React.memo( /*#__PURE__*/React.forwardRef(function CustomThemeButton({
|
|
51
|
+
// Calculate default props for use in custom themes
|
|
52
|
+
appearance = 'default',
|
|
53
|
+
autoFocus = false,
|
|
54
|
+
isDisabled = false,
|
|
55
|
+
isSelected = false,
|
|
56
|
+
shouldFitContainer = false,
|
|
57
|
+
spacing = 'default',
|
|
58
|
+
isLoading = false,
|
|
59
|
+
onMouseEnter: providedOnMouseEnter,
|
|
60
|
+
onMouseLeave: providedOnMouseLeave,
|
|
61
|
+
onMouseDown: providedOnMouseDown,
|
|
62
|
+
onMouseUp: providedOnMouseUp,
|
|
63
|
+
onFocus: providedOnFocus,
|
|
64
|
+
onBlur: providedOnBlur,
|
|
65
|
+
theme = defaultThemeFn,
|
|
66
|
+
...rest
|
|
67
|
+
}, ref) {
|
|
68
|
+
// TODO is there a nicer way to do this?
|
|
69
|
+
// Add default props back into object for spreading
|
|
70
|
+
const restProps = {
|
|
71
|
+
appearance,
|
|
72
|
+
autoFocus,
|
|
73
|
+
isDisabled,
|
|
74
|
+
isSelected,
|
|
75
|
+
shouldFitContainer,
|
|
76
|
+
spacing,
|
|
77
|
+
...rest
|
|
78
|
+
};
|
|
79
|
+
const [state, setState] = useState(initial);
|
|
80
|
+
const onMouseEnter = useCallback(event => {
|
|
81
|
+
setState(current => ({ ...current,
|
|
82
|
+
isHover: true
|
|
83
|
+
}));
|
|
84
|
+
|
|
85
|
+
if (providedOnMouseEnter) {
|
|
86
|
+
providedOnMouseEnter(event);
|
|
87
|
+
}
|
|
88
|
+
}, [providedOnMouseEnter]);
|
|
89
|
+
const onMouseLeave = useCallback(event => {
|
|
90
|
+
setState(current => ({ ...current,
|
|
91
|
+
isHover: false,
|
|
92
|
+
isActive: false
|
|
93
|
+
}));
|
|
94
|
+
|
|
95
|
+
if (providedOnMouseLeave) {
|
|
96
|
+
providedOnMouseLeave(event);
|
|
97
|
+
}
|
|
98
|
+
}, [providedOnMouseLeave]);
|
|
99
|
+
const onMouseDown = useCallback(event => {
|
|
100
|
+
setState(current => ({ ...current,
|
|
101
|
+
isActive: true
|
|
102
|
+
}));
|
|
103
|
+
|
|
104
|
+
if (providedOnMouseDown) {
|
|
105
|
+
providedOnMouseDown(event);
|
|
106
|
+
}
|
|
107
|
+
}, [providedOnMouseDown]);
|
|
108
|
+
const onMouseUp = useCallback(event => {
|
|
109
|
+
setState(current => ({ ...current,
|
|
110
|
+
isActive: false
|
|
111
|
+
}));
|
|
112
|
+
|
|
113
|
+
if (providedOnMouseUp) {
|
|
114
|
+
providedOnMouseUp(event);
|
|
115
|
+
}
|
|
116
|
+
}, [providedOnMouseUp]);
|
|
117
|
+
const onFocus = useCallback(event => {
|
|
118
|
+
setState(current => ({ ...current,
|
|
119
|
+
isFocus: true
|
|
120
|
+
}));
|
|
121
|
+
|
|
122
|
+
if (providedOnFocus) {
|
|
123
|
+
providedOnFocus(event);
|
|
124
|
+
}
|
|
125
|
+
}, [providedOnFocus]);
|
|
126
|
+
const onBlur = useCallback(event => {
|
|
127
|
+
setState(current => ({ ...current,
|
|
128
|
+
isFocus: false
|
|
129
|
+
}));
|
|
130
|
+
|
|
131
|
+
if (providedOnBlur) {
|
|
132
|
+
providedOnBlur(event);
|
|
133
|
+
}
|
|
134
|
+
}, [providedOnBlur]);
|
|
135
|
+
return /*#__PURE__*/React.createElement(Theme.Provider, {
|
|
136
|
+
value: theme
|
|
137
|
+
}, /*#__PURE__*/React.createElement(GlobalTheme.Consumer, null, ({
|
|
138
|
+
mode
|
|
139
|
+
}) => /*#__PURE__*/React.createElement(Theme.Consumer, _extends({
|
|
140
|
+
mode: mode,
|
|
141
|
+
state: getInteractionState({ ...state,
|
|
142
|
+
isLoading,
|
|
143
|
+
isSelected: restProps.isSelected,
|
|
144
|
+
isDisabled: restProps.isDisabled
|
|
145
|
+
}),
|
|
146
|
+
iconIsOnlyChild: getIsOnlySingleIcon(restProps),
|
|
147
|
+
isLoading: isLoading
|
|
148
|
+
}, restProps), ({
|
|
149
|
+
buttonStyles
|
|
150
|
+
}) => /*#__PURE__*/React.createElement(ButtonBase, _extends({}, restProps, {
|
|
151
|
+
ref: ref,
|
|
152
|
+
overlay: isLoading ? /*#__PURE__*/React.createElement(LoadingSpinner, restProps) : null,
|
|
153
|
+
onMouseEnter: onMouseEnter,
|
|
154
|
+
onMouseLeave: onMouseLeave,
|
|
155
|
+
onMouseDown: onMouseDown,
|
|
156
|
+
onMouseUp: onMouseUp,
|
|
157
|
+
onFocus: onFocus,
|
|
158
|
+
onBlur: onBlur,
|
|
159
|
+
buttonCss: getSpecifiers(buttonStyles)
|
|
160
|
+
})))));
|
|
161
|
+
})); // Tools including enzyme rely on components having a display name
|
|
162
|
+
|
|
163
|
+
CustomThemeButton.displayName = 'CustomThemeButton';
|
|
164
|
+
export default CustomThemeButton;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { createTheme } from '@atlaskit/theme/components';
|
|
2
|
+
import { getCss } from '../shared/css';
|
|
3
|
+
const stateToSelectorMap = {
|
|
4
|
+
focus: '&:focus',
|
|
5
|
+
focusSelected: '&:focus',
|
|
6
|
+
hover: '&:hover',
|
|
7
|
+
active: '&:active',
|
|
8
|
+
disabled: '&[disabled]'
|
|
9
|
+
}; // Mapping the new clean css back to the legacy theme format.
|
|
10
|
+
// The legacy theme format has all styles at the top level (no nested selectors)
|
|
11
|
+
// and uses `getSpecifiers()` to apply the style to all pseudo states
|
|
12
|
+
|
|
13
|
+
export function getCustomCss({
|
|
14
|
+
appearance = 'default',
|
|
15
|
+
spacing = 'default',
|
|
16
|
+
mode = 'light',
|
|
17
|
+
isSelected = false,
|
|
18
|
+
shouldFitContainer = false,
|
|
19
|
+
iconIsOnlyChild = false,
|
|
20
|
+
isLoading = false,
|
|
21
|
+
state
|
|
22
|
+
}) {
|
|
23
|
+
let result = getCss({
|
|
24
|
+
appearance,
|
|
25
|
+
spacing,
|
|
26
|
+
mode,
|
|
27
|
+
isSelected,
|
|
28
|
+
shouldFitContainer,
|
|
29
|
+
isOnlySingleIcon: iconIsOnlyChild
|
|
30
|
+
}); // we need to disable the default browser focus styles always
|
|
31
|
+
// this is because we are not expressing that we can have two pseudo states at a time
|
|
32
|
+
|
|
33
|
+
result.outline = 'none'; // Pulling relevant styles up to the top level
|
|
34
|
+
|
|
35
|
+
const selector = stateToSelectorMap[state];
|
|
36
|
+
|
|
37
|
+
if (selector) {
|
|
38
|
+
result = { ...result,
|
|
39
|
+
...result[selector]
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (isLoading) {
|
|
44
|
+
result = { ...result,
|
|
45
|
+
// Pull overlay styles up to the top
|
|
46
|
+
...result['&[data-has-overlay="true"]']
|
|
47
|
+
};
|
|
48
|
+
} // Delete all selectors and just keep root styles
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
Object.keys(result).forEach(key => {
|
|
52
|
+
// want to keep this one
|
|
53
|
+
if (key === '&::-moz-focus-inner') {
|
|
54
|
+
return;
|
|
55
|
+
} // Not using .startsWith for ie11
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if (key.indexOf('&') === 0) {
|
|
59
|
+
delete result[key];
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return result;
|
|
63
|
+
} // This styling approach works by generating a 'style' and applying with maximum specificity
|
|
64
|
+
// To do this we are overwriting all pseudo selectors
|
|
65
|
+
|
|
66
|
+
export function getSpecifiers(styles) {
|
|
67
|
+
return {
|
|
68
|
+
'&, &:hover, &:active, &:focus, &:visited, &:disabled, &[disabled]': styles
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export function defaultThemeFn(current, values) {
|
|
72
|
+
return current(values);
|
|
73
|
+
}
|
|
74
|
+
const Theme = createTheme(themeProps => ({
|
|
75
|
+
buttonStyles: getCustomCss(themeProps),
|
|
76
|
+
// No styles being applied directly to spinner by default
|
|
77
|
+
// Keeping this for legacy compat. We could remove it, but given
|
|
78
|
+
// that we are changing theme soon there is no point
|
|
79
|
+
spinnerStyles: {}
|
|
80
|
+
}));
|
|
81
|
+
export default Theme;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '../button-group';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default, Theme } from '../custom-theme-button';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '../loading-button';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '../button';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Ideally this file is not used directly. But rather, you go through the entry points
|
|
2
|
+
export { // default export is Button
|
|
3
|
+
default } from './entry-points/standard-button';
|
|
4
|
+
export { default as LoadingButton } from './entry-points/loading-button';
|
|
5
|
+
export { default as CustomThemeButton, Theme } from './entry-points/custom-theme-button';
|
|
6
|
+
export { default as ButtonGroup } from './entry-points/button-group';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import Button from './button';
|
|
4
|
+
import LoadingSpinner from './shared/loading-spinner';
|
|
5
|
+
const LoadingButton = /*#__PURE__*/React.forwardRef(function LoadingButton({
|
|
6
|
+
isLoading = false,
|
|
7
|
+
...rest
|
|
8
|
+
}, ref) {
|
|
9
|
+
// Button already has React.memo, so just leaning on that
|
|
10
|
+
return /*#__PURE__*/React.createElement(Button, _extends({}, rest, {
|
|
11
|
+
ref: ref,
|
|
12
|
+
overlay: isLoading ? /*#__PURE__*/React.createElement(LoadingSpinner, rest) : null
|
|
13
|
+
}));
|
|
14
|
+
}); // Tools including enzyme rely on components having a display name
|
|
15
|
+
|
|
16
|
+
LoadingButton.displayName = 'LoadingButton';
|
|
17
|
+
export default LoadingButton;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
function abort(event) {
|
|
2
|
+
event.preventDefault();
|
|
3
|
+
event.stopPropagation();
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const tabKeyCode = 9;
|
|
7
|
+
|
|
8
|
+
function onKey(event) {
|
|
9
|
+
// Allowing tab so that a user can move focus away
|
|
10
|
+
if (event.keyCode === tabKeyCode) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
abort(event);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const block = {
|
|
18
|
+
onMouseDownCapture: abort,
|
|
19
|
+
onMouseUpCapture: abort,
|
|
20
|
+
// because we have tabIndex = -1 when disabled,
|
|
21
|
+
// keyboard events can only occur when there is an overlay
|
|
22
|
+
onKeyDownCapture: onKey,
|
|
23
|
+
onKeyUpCapture: onKey,
|
|
24
|
+
onTouchStartCapture: abort,
|
|
25
|
+
onTouchEndCapture: abort,
|
|
26
|
+
onPointerDownCapture: abort,
|
|
27
|
+
onPointerUpCapture: abort,
|
|
28
|
+
onClickCapture: abort,
|
|
29
|
+
// Just smashing the existing onClick for good measure
|
|
30
|
+
onClick: abort
|
|
31
|
+
};
|
|
32
|
+
const doNotBlock = {};
|
|
33
|
+
export default function blockEvents({
|
|
34
|
+
isInteractive
|
|
35
|
+
}) {
|
|
36
|
+
return isInteractive ? doNotBlock : block;
|
|
37
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
|
|
3
|
+
/** @jsx jsx */
|
|
4
|
+
import React, { useCallback, useEffect, useRef } from 'react';
|
|
5
|
+
import { jsx } from '@emotion/core';
|
|
6
|
+
import { usePlatformLeafEventHandler } from '@atlaskit/analytics-next';
|
|
7
|
+
import useAutoFocus from '@atlaskit/ds-lib/use-auto-focus';
|
|
8
|
+
import blockEvents from './block-events';
|
|
9
|
+
import { getContentStyle, getFadingCss, getIconStyle, overlayCss } from './css';
|
|
10
|
+
|
|
11
|
+
function noop() {} // Disabled buttons will still publish events for nested elements in webkit.
|
|
12
|
+
// We are disabling pointer events on child elements so that
|
|
13
|
+
// the button will always be the target of events
|
|
14
|
+
// Note: firefox does not have this behaviour for child elements
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
const noPointerEventsOnChildrenCss = {
|
|
18
|
+
'> *': {
|
|
19
|
+
pointerEvents: 'none'
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
export default /*#__PURE__*/React.forwardRef(function ButtonBase(props, ref) {
|
|
23
|
+
const {
|
|
24
|
+
appearance = 'default',
|
|
25
|
+
buttonCss,
|
|
26
|
+
spacing = 'default',
|
|
27
|
+
autoFocus = false,
|
|
28
|
+
isDisabled = false,
|
|
29
|
+
shouldFitContainer = false,
|
|
30
|
+
isSelected = false,
|
|
31
|
+
iconBefore,
|
|
32
|
+
iconAfter,
|
|
33
|
+
children,
|
|
34
|
+
className,
|
|
35
|
+
href,
|
|
36
|
+
overlay,
|
|
37
|
+
tabIndex = 0,
|
|
38
|
+
type = !href ? 'button' : undefined,
|
|
39
|
+
onMouseDown: providedOnMouseDown = noop,
|
|
40
|
+
onClick: providedOnClick = noop,
|
|
41
|
+
// use the provided component prop,
|
|
42
|
+
// else default to anchor if there is a href, and button if there is no href
|
|
43
|
+
component: Component = href ? 'a' : 'button',
|
|
44
|
+
testId,
|
|
45
|
+
// I don't think this should be in button, but for now it is
|
|
46
|
+
analyticsContext,
|
|
47
|
+
...rest
|
|
48
|
+
} = props;
|
|
49
|
+
const ourRef = useRef();
|
|
50
|
+
const setRef = useCallback(node => {
|
|
51
|
+
ourRef.current = node;
|
|
52
|
+
|
|
53
|
+
if (ref == null) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (typeof ref === 'function') {
|
|
58
|
+
ref(node);
|
|
59
|
+
return;
|
|
60
|
+
} // @ts-ignore
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
ref.current = node;
|
|
64
|
+
}, [ourRef, ref]); // Cross browser auto focusing is pretty broken, so we are doing it ourselves
|
|
65
|
+
|
|
66
|
+
useAutoFocus(ourRef, autoFocus);
|
|
67
|
+
const onClick = usePlatformLeafEventHandler({
|
|
68
|
+
fn: providedOnClick,
|
|
69
|
+
action: 'clicked',
|
|
70
|
+
componentName: 'button',
|
|
71
|
+
packageName: "@atlaskit/button",
|
|
72
|
+
packageVersion: "16.1.2",
|
|
73
|
+
analyticsData: analyticsContext
|
|
74
|
+
}); // Button currently calls preventDefault, which is not standard button behaviour
|
|
75
|
+
|
|
76
|
+
const onMouseDown = useCallback(event => {
|
|
77
|
+
event.preventDefault();
|
|
78
|
+
providedOnMouseDown(event);
|
|
79
|
+
}, [providedOnMouseDown]); // Lose focus when becoming disabled (standard button behaviour)
|
|
80
|
+
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
const el = ourRef.current;
|
|
83
|
+
|
|
84
|
+
if (isDisabled && el && el === document.activeElement) {
|
|
85
|
+
el.blur();
|
|
86
|
+
}
|
|
87
|
+
}, [isDisabled]); // we are 'disabling' input with a button when there is an overlay
|
|
88
|
+
|
|
89
|
+
const hasOverlay = Boolean(overlay);
|
|
90
|
+
const fadeCss = getFadingCss({
|
|
91
|
+
hasOverlay
|
|
92
|
+
});
|
|
93
|
+
const isInteractive = !isDisabled && !hasOverlay;
|
|
94
|
+
return jsx(Component, _extends({}, rest, {
|
|
95
|
+
css: [buttonCss, isInteractive ? null : noPointerEventsOnChildrenCss],
|
|
96
|
+
className: className,
|
|
97
|
+
ref: setRef,
|
|
98
|
+
onClick: onClick,
|
|
99
|
+
onMouseDown: onMouseDown,
|
|
100
|
+
disabled: isDisabled,
|
|
101
|
+
href: isInteractive ? href : undefined // using undefined so that the property doesn't exist when false
|
|
102
|
+
,
|
|
103
|
+
"data-has-overlay": hasOverlay ? true : undefined,
|
|
104
|
+
"data-testid": testId,
|
|
105
|
+
type: type // Adding a tab index so element is always focusable, even when not a <button> or <a>
|
|
106
|
+
// Disabling focus via keyboard navigation when disabled
|
|
107
|
+
// as this is standard button behaviour
|
|
108
|
+
,
|
|
109
|
+
tabIndex: isDisabled ? -1 : tabIndex
|
|
110
|
+
}, blockEvents({
|
|
111
|
+
isInteractive
|
|
112
|
+
})), iconBefore ? jsx("span", {
|
|
113
|
+
css: [fadeCss, getIconStyle({
|
|
114
|
+
spacing
|
|
115
|
+
})]
|
|
116
|
+
}, iconBefore) : null, children ? jsx("span", {
|
|
117
|
+
css: [fadeCss, getContentStyle({
|
|
118
|
+
spacing
|
|
119
|
+
})]
|
|
120
|
+
}, children) : null, iconAfter ? jsx("span", {
|
|
121
|
+
css: [fadeCss, getIconStyle({
|
|
122
|
+
spacing
|
|
123
|
+
})]
|
|
124
|
+
}, iconAfter) : null, overlay ? jsx("span", {
|
|
125
|
+
css: overlayCss
|
|
126
|
+
}, overlay) : null);
|
|
127
|
+
});
|