@spark-web/button 1.1.2 → 1.3.1

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.
@@ -1,50 +1,123 @@
1
1
  import _objectSpread from '@babel/runtime/helpers/esm/objectSpread2';
2
2
  import _objectWithoutProperties from '@babel/runtime/helpers/esm/objectWithoutProperties';
3
- import { useFocusRing, VisuallyHidden } from '@spark-web/a11y';
4
3
  import { Box } from '@spark-web/box';
4
+ import { useComposedRefs } from '@spark-web/utils';
5
+ import { forwardRef, useRef, useCallback, Children, isValidElement, cloneElement } from 'react';
6
+ import { jsx, jsxs } from 'react/jsx-runtime';
7
+ import { useFocusRing, VisuallyHidden } from '@spark-web/a11y';
5
8
  import { Spinner } from '@spark-web/spinner';
6
- import { buildDataAttributes } from '@spark-web/utils/internal';
7
- import { Children, isValidElement, cloneElement, forwardRef } from 'react';
8
9
  import { Text } from '@spark-web/text';
9
- import { jsx, jsxs } from 'react/jsx-runtime';
10
10
  import { css } from '@emotion/css';
11
11
  import { useTheme } from '@spark-web/theme';
12
12
  import { useLinkComponent } from '@spark-web/link';
13
+ import { buildDataAttributes } from '@spark-web/utils/internal';
13
14
  import { forwardRefWithAs } from '@spark-web/utils/ts';
14
15
 
16
+ var _excluded$2 = ["onClick", "disabled", "type"];
17
+ var BaseButton = /*#__PURE__*/forwardRef(function (_ref, forwardedRef) {
18
+ var onClickProp = _ref.onClick,
19
+ _ref$disabled = _ref.disabled,
20
+ disabled = _ref$disabled === void 0 ? false : _ref$disabled,
21
+ _ref$type = _ref.type,
22
+ type = _ref$type === void 0 ? 'button' : _ref$type,
23
+ consumerProps = _objectWithoutProperties(_ref, _excluded$2);
24
+
25
+ var internalRef = useRef(null);
26
+ var composedRef = useComposedRefs(internalRef, forwardedRef);
27
+ /**
28
+ * In Safari buttons are not focused automatically by the browser once
29
+ * pressed, the default behaviour is to focus the nearest focusable ancestor.
30
+ * To fix this we need to manually focus the button element after the user
31
+ * presses the element.
32
+ */
33
+
34
+ var onClick = useCallback(function (event) {
35
+ var _internalRef$current;
36
+
37
+ (_internalRef$current = internalRef.current) === null || _internalRef$current === void 0 ? void 0 : _internalRef$current.focus();
38
+ var preventableClickHandler = getPreventableClickHandler(onClickProp, disabled);
39
+ preventableClickHandler(event);
40
+ }, [disabled, onClickProp]);
41
+ return /*#__PURE__*/jsx(Box, _objectSpread(_objectSpread({}, consumerProps), {}, {
42
+ as: "button",
43
+ ref: composedRef,
44
+ "aria-disabled": disabled,
45
+ onClick: onClick,
46
+ type: type
47
+ }));
48
+ });
49
+ BaseButton.displayName = 'BaseButton';
50
+ /**
51
+ * handle "disabled" behaviour w/o disabling buttons
52
+ * @see https://axesslab.com/disabled-buttons-suck/
53
+ */
54
+
55
+ function getPreventableClickHandler(onClick, disabled) {
56
+ return function handleClick(event) {
57
+ if (disabled) {
58
+ event.preventDefault();
59
+ } else {
60
+ onClick === null || onClick === void 0 ? void 0 : onClick(event);
61
+ }
62
+ };
63
+ }
64
+
65
+ var highDisabledStyles = {
66
+ backgroundDisabled: 'disabled',
67
+ borderDisabled: 'fieldDisabled',
68
+ textToneDisabled: 'neutralInverted'
69
+ };
70
+ var highDisabledAltStyles = {
71
+ backgroundDisabled: 'neutral',
72
+ borderDisabled: 'standard',
73
+ textToneDisabled: 'placeholder'
74
+ };
75
+ var lowDisabledStyles = {
76
+ backgroundDisabled: 'inputDisabled',
77
+ textToneDisabled: 'disabled'
78
+ };
79
+ var lowDisabledAltStyles = {
80
+ backgroundDisabled: 'inputDisabled',
81
+ borderDisabled: 'fieldDisabled',
82
+ textToneDisabled: 'disabled'
83
+ };
84
+ var noneDisabledStyles = {
85
+ backgroundDisabled: 'neutral',
86
+ textToneDisabled: 'disabled'
87
+ };
15
88
  var variants = {
16
89
  high: {
17
- primary: {
90
+ primary: _objectSpread({
18
91
  background: 'primary',
19
92
  backgroundHover: 'primaryHover',
20
93
  backgroundActive: 'primaryActive'
21
- },
22
- secondary: {
94
+ }, highDisabledStyles),
95
+ secondary: _objectSpread({
23
96
  background: 'secondary',
24
97
  backgroundHover: 'secondaryHover',
25
98
  backgroundActive: 'secondaryActive'
26
- },
27
- neutral: {
99
+ }, highDisabledStyles),
100
+ neutral: _objectSpread({
28
101
  background: 'neutral',
29
102
  border: 'field',
30
103
  backgroundHover: 'neutralHover',
31
104
  backgroundActive: 'neutralActive'
32
- },
33
- positive: {
105
+ }, highDisabledAltStyles),
106
+ positive: _objectSpread({
34
107
  background: 'positive',
35
108
  backgroundHover: 'positiveHover',
36
109
  backgroundActive: 'positiveActive'
37
- },
38
- critical: {
110
+ }, highDisabledStyles),
111
+ critical: _objectSpread({
39
112
  background: 'critical',
40
113
  backgroundHover: 'criticalHover',
41
114
  backgroundActive: 'criticalActive'
42
- },
115
+ }, highDisabledStyles),
43
116
  caution: undefined,
44
117
  info: undefined
45
118
  },
46
119
  low: {
47
- primary: {
120
+ primary: _objectSpread({
48
121
  background: 'surface',
49
122
  border: 'primary',
50
123
  borderWidth: 'large',
@@ -55,8 +128,8 @@ var variants = {
55
128
  backgroundActive: 'none',
56
129
  borderActive: 'primaryActive',
57
130
  textToneActive: 'primaryActive'
58
- },
59
- secondary: {
131
+ }, lowDisabledAltStyles),
132
+ secondary: _objectSpread({
60
133
  background: 'surface',
61
134
  border: 'secondary',
62
135
  borderWidth: 'large',
@@ -67,76 +140,76 @@ var variants = {
67
140
  backgroundActive: 'none',
68
141
  borderActive: 'secondaryActive',
69
142
  textToneActive: 'secondaryActive'
70
- },
71
- neutral: {
143
+ }, lowDisabledAltStyles),
144
+ neutral: _objectSpread({
72
145
  background: 'neutralLow',
73
146
  backgroundHover: 'neutralLowHover',
74
147
  backgroundActive: 'neutralLowActive'
75
- },
76
- positive: {
148
+ }, lowDisabledStyles),
149
+ positive: _objectSpread({
77
150
  background: 'positiveLow',
78
151
  backgroundHover: 'positiveLowHover',
79
152
  backgroundActive: 'positiveLowActive'
80
- },
81
- caution: {
153
+ }, lowDisabledStyles),
154
+ caution: _objectSpread({
82
155
  background: 'cautionLow',
83
156
  backgroundHover: 'cautionLowHover',
84
157
  backgroundActive: 'cautionLowActive'
85
- },
86
- critical: {
158
+ }, lowDisabledStyles),
159
+ critical: _objectSpread({
87
160
  background: 'criticalLow',
88
161
  backgroundHover: 'criticalLowHover',
89
162
  backgroundActive: 'criticalLowActive'
90
- },
91
- info: {
163
+ }, lowDisabledStyles),
164
+ info: _objectSpread({
92
165
  background: 'infoLow',
93
166
  backgroundHover: 'infoLowHover',
94
167
  backgroundActive: 'infoLowActive'
95
- }
168
+ }, lowDisabledStyles)
96
169
  },
97
170
  none: {
98
- primary: {
171
+ primary: _objectSpread({
99
172
  background: 'surface',
100
173
  textTone: 'primaryActive',
101
174
  backgroundHover: 'primaryLowHover',
102
175
  backgroundActive: 'primaryLowActive'
103
- },
104
- secondary: {
176
+ }, noneDisabledStyles),
177
+ secondary: _objectSpread({
105
178
  background: 'surface',
106
179
  textTone: 'secondaryActive',
107
180
  backgroundHover: 'secondaryLowHover',
108
181
  backgroundActive: 'secondaryLowActive'
109
- },
110
- neutral: {
182
+ }, noneDisabledStyles),
183
+ neutral: _objectSpread({
111
184
  background: 'surface',
112
185
  textTone: 'neutral',
113
186
  backgroundHover: 'neutralLowHover',
114
187
  backgroundActive: 'neutralLowActive'
115
- },
116
- positive: {
188
+ }, noneDisabledStyles),
189
+ positive: _objectSpread({
117
190
  background: 'surface',
118
191
  textTone: 'positive',
119
192
  backgroundHover: 'positiveLowHover',
120
193
  backgroundActive: 'positiveLowActive'
121
- },
122
- caution: {
194
+ }, noneDisabledStyles),
195
+ caution: _objectSpread({
123
196
  background: 'surface',
124
197
  textTone: 'caution',
125
198
  backgroundHover: 'cautionLowHover',
126
199
  backgroundActive: 'cautionLowActive'
127
- },
128
- critical: {
200
+ }, noneDisabledStyles),
201
+ critical: _objectSpread({
129
202
  background: 'surface',
130
203
  textTone: 'critical',
131
204
  backgroundHover: 'criticalLowHover',
132
205
  backgroundActive: 'criticalLowActive'
133
- },
134
- info: {
206
+ }, noneDisabledStyles),
207
+ info: _objectSpread({
135
208
  background: 'surface',
136
209
  textTone: 'info',
137
210
  backgroundHover: 'infoLowHover',
138
211
  backgroundActive: 'infoLowActive'
139
- }
212
+ }, noneDisabledStyles)
140
213
  }
141
214
  };
142
215
  var mapTokens = {
@@ -162,7 +235,7 @@ var resolveButtonChildren = function resolveButtonChildren(_ref) {
162
235
  tone = _ref.tone;
163
236
  var variant = variants[prominence][tone];
164
237
  return Children.map(children, function (child) {
165
- if (typeof child === 'string') {
238
+ if (typeof child === 'string' || typeof child === 'number') {
166
239
  return /*#__PURE__*/jsx(HiddenWhenLoading, {
167
240
  isLoading: isLoading,
168
241
  children: /*#__PURE__*/jsx(Text, {
@@ -218,9 +291,12 @@ function useButtonStyles(_ref) {
218
291
  var focusRingStyles = useFocusRing({
219
292
  tone: tone
220
293
  });
294
+ var disabledFocusRingStyles = useFocusRing({
295
+ tone: 'disabled'
296
+ });
221
297
  var variant = variants[prominence][tone];
222
298
  var isLarge = size === 'large';
223
- var transitionColours = {
299
+ var transitionColors = {
224
300
  transitionProperty: 'color, background-color, border-color, text-decoration-color',
225
301
  transitionTimingFunction: 'cubic-bezier(0.02, 1.505, 0.745, 1.235)',
226
302
  transitionDuration: "".concat(theme.animation.standard.duration, "ms")
@@ -240,27 +316,39 @@ function useButtonStyles(_ref) {
240
316
  position: 'relative',
241
317
  width: iconOnly ? mapTokens.size[size] : undefined,
242
318
  // interactions styles
243
- className: css(_objectSpread(_objectSpread({}, transitionColours), {}, {
244
- '&:hover': {
245
- borderColor: variant !== null && variant !== void 0 && variant.borderHover ? theme.border.color[variant.borderHover] : undefined,
246
- backgroundColor: variant !== null && variant !== void 0 && variant.backgroundHover ? theme.backgroundInteractions[variant.backgroundHover] : undefined,
247
- // Style button text when hovering
248
- '> *': _objectSpread(_objectSpread({}, transitionColours), {}, {
249
- color: variant !== null && variant !== void 0 && variant.textToneHover ? theme.color.foreground[variant.textToneHover] : undefined,
250
- stroke: variant !== null && variant !== void 0 && variant.textToneHover ? theme.color.foreground[variant.textToneHover] : undefined
251
- })
319
+ className: css(_objectSpread(_objectSpread({}, transitionColors), {}, {
320
+ '&:not([aria-disabled=true])': {
321
+ ':hover': {
322
+ borderColor: variant !== null && variant !== void 0 && variant.borderHover ? theme.border.color[variant.borderHover] : undefined,
323
+ backgroundColor: variant !== null && variant !== void 0 && variant.backgroundHover ? theme.backgroundInteractions[variant.backgroundHover] : undefined,
324
+ // Style button text when hovering
325
+ '> *': _objectSpread(_objectSpread({}, transitionColors), {}, {
326
+ color: variant !== null && variant !== void 0 && variant.textToneHover ? theme.color.foreground[variant.textToneHover] : undefined,
327
+ stroke: variant !== null && variant !== void 0 && variant.textToneHover ? theme.color.foreground[variant.textToneHover] : undefined
328
+ })
329
+ },
330
+ ':active': {
331
+ borderColor: variant !== null && variant !== void 0 && variant.borderActive ? theme.border.color[variant.borderActive] : undefined,
332
+ backgroundColor: variant !== null && variant !== void 0 && variant.backgroundActive ? theme.backgroundInteractions[variant === null || variant === void 0 ? void 0 : variant.backgroundActive] : undefined,
333
+ transform: 'scale(0.98)',
334
+ // Style button text when it's active
335
+ '> *': _objectSpread(_objectSpread({}, transitionColors), {}, {
336
+ color: variant !== null && variant !== void 0 && variant.textToneActive ? theme.color.foreground[variant.textToneActive] : undefined,
337
+ stroke: variant !== null && variant !== void 0 && variant.textToneActive ? theme.color.foreground[variant.textToneActive] : undefined
338
+ })
339
+ },
340
+ ':focus': focusRingStyles
252
341
  },
253
- '&:active': {
254
- borderColor: variant !== null && variant !== void 0 && variant.borderActive ? theme.border.color[variant.borderActive] : undefined,
255
- backgroundColor: variant !== null && variant !== void 0 && variant.backgroundActive ? theme.backgroundInteractions[variant === null || variant === void 0 ? void 0 : variant.backgroundActive] : undefined,
256
- transform: 'scale(0.98)',
257
- // Style button text when it's active
258
- '> *': _objectSpread(_objectSpread({}, transitionColours), {}, {
259
- color: variant !== null && variant !== void 0 && variant.textToneActive ? theme.color.foreground[variant.textToneActive] : undefined,
260
- stroke: variant !== null && variant !== void 0 && variant.textToneActive ? theme.color.foreground[variant.textToneActive] : undefined
261
- })
262
- },
263
- ':focus': focusRingStyles
342
+ '&[aria-disabled=true]': {
343
+ backgroundColor: variant !== null && variant !== void 0 && variant.backgroundDisabled ? theme.color.background[variant === null || variant === void 0 ? void 0 : variant.backgroundDisabled] : undefined,
344
+ borderColor: variant !== null && variant !== void 0 && variant.borderDisabled ? theme.border.color[variant.borderDisabled] : undefined,
345
+ cursor: 'default',
346
+ '*': {
347
+ color: variant !== null && variant !== void 0 && variant.textToneDisabled ? theme.color.foreground[variant.textToneDisabled] : undefined,
348
+ stroke: variant !== null && variant !== void 0 && variant.textToneDisabled ? theme.color.foreground[variant.textToneDisabled] : undefined
349
+ },
350
+ ':focus': disabledFocusRingStyles
351
+ }
264
352
  }))
265
353
  };
266
354
  return buttonStyleProps;
@@ -272,13 +360,12 @@ var _excluded$1 = ["aria-controls", "aria-describedby", "aria-expanded", "data",
272
360
  * Buttons are used to initialize an action, their label should express what
273
361
  * action will occur when the user interacts with it.
274
362
  */
275
- var Button = /*#__PURE__*/forwardRef(function (_ref, ref) {
363
+ var Button = /*#__PURE__*/forwardRef(function (_ref, forwardedRef) {
276
364
  var ariaControls = _ref['aria-controls'],
277
365
  ariaDescribedBy = _ref['aria-describedby'],
278
366
  ariaExpanded = _ref['aria-expanded'],
279
367
  data = _ref.data,
280
- _ref$disabled = _ref.disabled,
281
- disabled = _ref$disabled === void 0 ? false : _ref$disabled,
368
+ disabled = _ref.disabled,
282
369
  id = _ref.id,
283
370
  _ref$loading = _ref.loading,
284
371
  loading = _ref$loading === void 0 ? false : _ref$loading,
@@ -289,8 +376,7 @@ var Button = /*#__PURE__*/forwardRef(function (_ref, ref) {
289
376
  size = _ref$size === void 0 ? 'medium' : _ref$size,
290
377
  _ref$tone = _ref.tone,
291
378
  tone = _ref$tone === void 0 ? 'primary' : _ref$tone,
292
- _ref$type = _ref.type,
293
- type = _ref$type === void 0 ? 'button' : _ref$type,
379
+ type = _ref.type,
294
380
  props = _objectWithoutProperties(_ref, _excluded$1);
295
381
 
296
382
  var iconOnly = Boolean(props.label);
@@ -303,24 +389,17 @@ var Button = /*#__PURE__*/forwardRef(function (_ref, ref) {
303
389
  var isDisabled = disabled || loading;
304
390
  var isLoading = loading && !disabled;
305
391
  var variant = variants[prominence][tone];
306
- /**
307
- * handle "disabled" behaviour w/o disabling buttons
308
- * @see https://axesslab.com/disabled-buttons-suck/
309
- */
310
-
311
- var handleClick = getPreventableClickHandler(onClick, isDisabled);
312
- return /*#__PURE__*/jsxs(Box, _objectSpread(_objectSpread(_objectSpread({
392
+ return /*#__PURE__*/jsxs(BaseButton, _objectSpread(_objectSpread({}, buttonStyleProps), {}, {
313
393
  "aria-controls": ariaControls,
314
394
  "aria-describedby": ariaDescribedBy,
315
- "aria-disabled": isDisabled,
316
395
  "aria-expanded": ariaExpanded,
317
396
  "aria-label": props.label,
318
- as: "button",
397
+ data: data,
398
+ disabled: isDisabled,
319
399
  id: id,
320
- onClick: handleClick,
321
- ref: ref,
322
- type: type
323
- }, buttonStyleProps), data ? buildDataAttributes(data) : undefined), {}, {
400
+ onClick: onClick,
401
+ ref: forwardedRef,
402
+ type: type,
324
403
  children: [resolveButtonChildren(_objectSpread(_objectSpread({}, props), {}, {
325
404
  isLoading: isLoading,
326
405
  prominence: prominence,
@@ -332,21 +411,6 @@ var Button = /*#__PURE__*/forwardRef(function (_ref, ref) {
332
411
  }));
333
412
  });
334
413
  Button.displayName = 'Button';
335
- /**
336
- * Prevent click events when the component is "disabled".
337
- * Note: we don't want to actually disable a button element for several reasons.
338
- * One being because that would prohibit the use of tooltips.
339
- */
340
-
341
- function getPreventableClickHandler(onClick, disabled) {
342
- return function handleClick(event) {
343
- if (disabled) {
344
- event.preventDefault();
345
- } else {
346
- onClick === null || onClick === void 0 ? void 0 : onClick(event);
347
- }
348
- };
349
- }
350
414
 
351
415
  function Loading(_ref2) {
352
416
  var tone = _ref2.tone;
@@ -409,4 +473,4 @@ var ButtonLink = forwardRefWithAs(function (_ref, ref) {
409
473
  }));
410
474
  });
411
475
 
412
- export { Button, ButtonLink };
476
+ export { BaseButton, Button, ButtonLink };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spark-web/button",
3
- "version": "1.1.2",
3
+ "version": "1.3.1",
4
4
  "license": "MIT",
5
5
  "main": "dist/spark-web-button.cjs.js",
6
6
  "module": "dist/spark-web-button.esm.js",
@@ -8,16 +8,16 @@
8
8
  "dist"
9
9
  ],
10
10
  "dependencies": {
11
- "@babel/runtime": "^7.18.0",
11
+ "@babel/runtime": "^7.18.9",
12
12
  "@emotion/css": "^11.9.0",
13
- "@spark-web/a11y": "^1.0.5",
14
- "@spark-web/box": "^1.0.5",
15
- "@spark-web/icon": "^1.1.3",
16
- "@spark-web/link": "^1.0.5",
17
- "@spark-web/spinner": "^1.0.3",
18
- "@spark-web/text": "^1.0.5",
19
- "@spark-web/theme": "^3.0.1",
20
- "@spark-web/utils": "^1.1.3"
13
+ "@spark-web/a11y": "^1.2.0",
14
+ "@spark-web/box": "^1.0.7",
15
+ "@spark-web/icon": "^1.1.5",
16
+ "@spark-web/link": "^1.0.7",
17
+ "@spark-web/spinner": "^1.0.5",
18
+ "@spark-web/text": "^1.0.7",
19
+ "@spark-web/theme": "^3.0.3",
20
+ "@spark-web/utils": "^1.2.0"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/react": "^17.0.12",