@spark-web/button 1.1.1 → 1.3.0

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
- import { css } from '@emotion/css';
9
9
  import { Text } from '@spark-web/text';
10
- import { jsx, jsxs } from 'react/jsx-runtime';
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, {
@@ -199,10 +272,12 @@ var resolveButtonChildren = function resolveButtonChildren(_ref) {
199
272
  function HiddenWhenLoading(_ref2) {
200
273
  var children = _ref2.children,
201
274
  isLoading = _ref2.isLoading;
202
- return /*#__PURE__*/jsx("span", {
203
- className: isLoading ? css({
204
- opacity: 0
205
- }) : undefined,
275
+ return /*#__PURE__*/jsx(Box, {
276
+ as: "span",
277
+ display: "inline-flex",
278
+ alignItems: "center",
279
+ justifyContent: "center",
280
+ opacity: isLoading ? 0 : undefined,
206
281
  children: children
207
282
  });
208
283
  }
@@ -216,9 +291,12 @@ function useButtonStyles(_ref) {
216
291
  var focusRingStyles = useFocusRing({
217
292
  tone: tone
218
293
  });
294
+ var disabledFocusRingStyles = useFocusRing({
295
+ tone: 'disabled'
296
+ });
219
297
  var variant = variants[prominence][tone];
220
298
  var isLarge = size === 'large';
221
- var transitionColours = {
299
+ var transitionColors = {
222
300
  transitionProperty: 'color, background-color, border-color, text-decoration-color',
223
301
  transitionTimingFunction: 'cubic-bezier(0.02, 1.505, 0.745, 1.235)',
224
302
  transitionDuration: "".concat(theme.animation.standard.duration, "ms")
@@ -238,27 +316,38 @@ function useButtonStyles(_ref) {
238
316
  position: 'relative',
239
317
  width: iconOnly ? mapTokens.size[size] : undefined,
240
318
  // interactions styles
241
- className: css(_objectSpread(_objectSpread({}, transitionColours), {}, {
242
- '&:hover': {
243
- borderColor: variant !== null && variant !== void 0 && variant.borderHover ? theme.border.color[variant.borderHover] : undefined,
244
- backgroundColor: variant !== null && variant !== void 0 && variant.backgroundHover ? theme.backgroundInteractions[variant.backgroundHover] : undefined,
245
- // Style button text when hovering
246
- '> *': _objectSpread(_objectSpread({}, transitionColours), {}, {
247
- color: variant !== null && variant !== void 0 && variant.textToneHover ? theme.color.foreground[variant.textToneHover] : undefined,
248
- stroke: variant !== null && variant !== void 0 && variant.textToneHover ? theme.color.foreground[variant.textToneHover] : undefined
249
- })
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
250
341
  },
251
- '&:active': {
252
- borderColor: variant !== null && variant !== void 0 && variant.borderActive ? theme.border.color[variant.borderActive] : undefined,
253
- backgroundColor: variant !== null && variant !== void 0 && variant.backgroundActive ? theme.backgroundInteractions[variant === null || variant === void 0 ? void 0 : variant.backgroundActive] : undefined,
254
- transform: 'scale(0.98)',
255
- // Style button text when it's active
256
- '> *': _objectSpread(_objectSpread({}, transitionColours), {}, {
257
- color: variant !== null && variant !== void 0 && variant.textToneActive ? theme.color.foreground[variant.textToneActive] : undefined,
258
- stroke: variant !== null && variant !== void 0 && variant.textToneActive ? theme.color.foreground[variant.textToneActive] : undefined
259
- })
260
- },
261
- ':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
+ '*': {
346
+ color: variant !== null && variant !== void 0 && variant.textToneDisabled ? theme.color.foreground[variant.textToneDisabled] : undefined,
347
+ stroke: variant !== null && variant !== void 0 && variant.textToneDisabled ? theme.color.foreground[variant.textToneDisabled] : undefined
348
+ },
349
+ ':focus': disabledFocusRingStyles
350
+ }
262
351
  }))
263
352
  };
264
353
  return buttonStyleProps;
@@ -270,13 +359,12 @@ var _excluded$1 = ["aria-controls", "aria-describedby", "aria-expanded", "data",
270
359
  * Buttons are used to initialize an action, their label should express what
271
360
  * action will occur when the user interacts with it.
272
361
  */
273
- var Button = /*#__PURE__*/forwardRef(function (_ref, ref) {
362
+ var Button = /*#__PURE__*/forwardRef(function (_ref, forwardedRef) {
274
363
  var ariaControls = _ref['aria-controls'],
275
364
  ariaDescribedBy = _ref['aria-describedby'],
276
365
  ariaExpanded = _ref['aria-expanded'],
277
366
  data = _ref.data,
278
- _ref$disabled = _ref.disabled,
279
- disabled = _ref$disabled === void 0 ? false : _ref$disabled,
367
+ disabled = _ref.disabled,
280
368
  id = _ref.id,
281
369
  _ref$loading = _ref.loading,
282
370
  loading = _ref$loading === void 0 ? false : _ref$loading,
@@ -287,8 +375,7 @@ var Button = /*#__PURE__*/forwardRef(function (_ref, ref) {
287
375
  size = _ref$size === void 0 ? 'medium' : _ref$size,
288
376
  _ref$tone = _ref.tone,
289
377
  tone = _ref$tone === void 0 ? 'primary' : _ref$tone,
290
- _ref$type = _ref.type,
291
- type = _ref$type === void 0 ? 'button' : _ref$type,
378
+ type = _ref.type,
292
379
  props = _objectWithoutProperties(_ref, _excluded$1);
293
380
 
294
381
  var iconOnly = Boolean(props.label);
@@ -301,24 +388,17 @@ var Button = /*#__PURE__*/forwardRef(function (_ref, ref) {
301
388
  var isDisabled = disabled || loading;
302
389
  var isLoading = loading && !disabled;
303
390
  var variant = variants[prominence][tone];
304
- /**
305
- * handle "disabled" behaviour w/o disabling buttons
306
- * @see https://axesslab.com/disabled-buttons-suck/
307
- */
308
-
309
- var handleClick = getPreventableClickHandler(onClick, isDisabled);
310
- return /*#__PURE__*/jsxs(Box, _objectSpread(_objectSpread(_objectSpread({
391
+ return /*#__PURE__*/jsxs(BaseButton, _objectSpread(_objectSpread({}, buttonStyleProps), {}, {
311
392
  "aria-controls": ariaControls,
312
393
  "aria-describedby": ariaDescribedBy,
313
- "aria-disabled": isDisabled,
314
394
  "aria-expanded": ariaExpanded,
315
395
  "aria-label": props.label,
316
- as: "button",
396
+ data: data,
397
+ disabled: isDisabled,
317
398
  id: id,
318
- onClick: handleClick,
319
- ref: ref,
320
- type: type
321
- }, buttonStyleProps), data ? buildDataAttributes(data) : undefined), {}, {
399
+ onClick: onClick,
400
+ ref: forwardedRef,
401
+ type: type,
322
402
  children: [resolveButtonChildren(_objectSpread(_objectSpread({}, props), {}, {
323
403
  isLoading: isLoading,
324
404
  prominence: prominence,
@@ -330,21 +410,6 @@ var Button = /*#__PURE__*/forwardRef(function (_ref, ref) {
330
410
  }));
331
411
  });
332
412
  Button.displayName = 'Button';
333
- /**
334
- * Prevent click events when the component is "disabled".
335
- * Note: we don't want to actually disable a button element for several reasons.
336
- * One being because that would prohibit the use of tooltips.
337
- */
338
-
339
- function getPreventableClickHandler(onClick, disabled) {
340
- return function handleClick(event) {
341
- if (disabled) {
342
- event.preventDefault();
343
- } else {
344
- onClick === null || onClick === void 0 ? void 0 : onClick(event);
345
- }
346
- };
347
- }
348
413
 
349
414
  function Loading(_ref2) {
350
415
  var tone = _ref2.tone;
@@ -407,4 +472,4 @@ var ButtonLink = forwardRefWithAs(function (_ref, ref) {
407
472
  }));
408
473
  });
409
474
 
410
- export { Button, ButtonLink };
475
+ export { BaseButton, Button, ButtonLink };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spark-web/button",
3
- "version": "1.1.1",
3
+ "version": "1.3.0",
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.2",
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",