@spark-web/button 1.2.0 → 1.4.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.
@@ -4,13 +4,13 @@ import { Box } from '@spark-web/box';
4
4
  import { useComposedRefs } from '@spark-web/utils';
5
5
  import { forwardRef, useRef, useCallback, Children, isValidElement, cloneElement } from 'react';
6
6
  import { jsx, jsxs } from 'react/jsx-runtime';
7
+ import _slicedToArray from '@babel/runtime/helpers/esm/slicedToArray';
8
+ import { css } from '@emotion/css';
7
9
  import { useFocusRing, VisuallyHidden } from '@spark-web/a11y';
8
10
  import { Spinner } from '@spark-web/spinner';
9
11
  import { Text } from '@spark-web/text';
10
- import { css } from '@emotion/css';
11
12
  import { useTheme } from '@spark-web/theme';
12
13
  import { useLinkComponent } from '@spark-web/link';
13
- import { buildDataAttributes } from '@spark-web/utils/internal';
14
14
  import { forwardRefWithAs } from '@spark-web/utils/ts';
15
15
 
16
16
  var _excluded$2 = ["onClick", "disabled", "type"];
@@ -20,7 +20,7 @@ var BaseButton = /*#__PURE__*/forwardRef(function (_ref, forwardedRef) {
20
20
  disabled = _ref$disabled === void 0 ? false : _ref$disabled,
21
21
  _ref$type = _ref.type,
22
22
  type = _ref$type === void 0 ? 'button' : _ref$type,
23
- rest = _objectWithoutProperties(_ref, _excluded$2);
23
+ consumerProps = _objectWithoutProperties(_ref, _excluded$2);
24
24
 
25
25
  var internalRef = useRef(null);
26
26
  var composedRef = useComposedRefs(internalRef, forwardedRef);
@@ -38,11 +38,12 @@ var BaseButton = /*#__PURE__*/forwardRef(function (_ref, forwardedRef) {
38
38
  var preventableClickHandler = getPreventableClickHandler(onClickProp, disabled);
39
39
  preventableClickHandler(event);
40
40
  }, [disabled, onClickProp]);
41
- return /*#__PURE__*/jsx(Box, _objectSpread(_objectSpread({
42
- as: "button"
43
- }, rest), {}, {
41
+ return /*#__PURE__*/jsx(Box, _objectSpread(_objectSpread({}, consumerProps), {}, {
42
+ as: "button",
43
+ ref: composedRef // Hide aria-disabled attribute when button is not disabled
44
+ ,
45
+ "aria-disabled": disabled || undefined,
44
46
  onClick: onClick,
45
- ref: composedRef,
46
47
  type: type
47
48
  }));
48
49
  });
@@ -62,39 +63,62 @@ function getPreventableClickHandler(onClick, disabled) {
62
63
  };
63
64
  }
64
65
 
66
+ var highDisabledStyles = {
67
+ backgroundDisabled: 'disabled',
68
+ borderDisabled: 'fieldDisabled',
69
+ textToneDisabled: 'neutralInverted'
70
+ };
71
+ var highDisabledAltStyles = {
72
+ backgroundDisabled: 'neutral',
73
+ borderDisabled: 'standard',
74
+ textToneDisabled: 'placeholder'
75
+ };
76
+ var lowDisabledStyles = {
77
+ backgroundDisabled: 'inputDisabled',
78
+ textToneDisabled: 'disabled'
79
+ };
80
+ var lowDisabledAltStyles = {
81
+ backgroundDisabled: 'inputDisabled',
82
+ borderDisabled: 'fieldDisabled',
83
+ textToneDisabled: 'disabled'
84
+ };
85
+ var noneDisabledStyles = {
86
+ backgroundDisabled: 'neutral',
87
+ textToneDisabled: 'disabled'
88
+ };
65
89
  var variants = {
66
90
  high: {
67
- primary: {
91
+ primary: _objectSpread({
68
92
  background: 'primary',
69
93
  backgroundHover: 'primaryHover',
70
94
  backgroundActive: 'primaryActive'
71
- },
72
- secondary: {
95
+ }, highDisabledStyles),
96
+ secondary: _objectSpread({
73
97
  background: 'secondary',
74
98
  backgroundHover: 'secondaryHover',
75
99
  backgroundActive: 'secondaryActive'
76
- },
77
- neutral: {
100
+ }, highDisabledStyles),
101
+ neutral: _objectSpread({
78
102
  background: 'neutral',
79
103
  border: 'field',
80
104
  backgroundHover: 'neutralHover',
81
105
  backgroundActive: 'neutralActive'
82
- },
83
- positive: {
106
+ }, highDisabledAltStyles),
107
+ positive: _objectSpread({
84
108
  background: 'positive',
85
109
  backgroundHover: 'positiveHover',
86
110
  backgroundActive: 'positiveActive'
87
- },
88
- critical: {
111
+ }, highDisabledStyles),
112
+ critical: _objectSpread({
89
113
  background: 'critical',
90
114
  backgroundHover: 'criticalHover',
91
115
  backgroundActive: 'criticalActive'
92
- },
116
+ }, highDisabledStyles),
93
117
  caution: undefined,
94
118
  info: undefined
95
119
  },
96
120
  low: {
97
- primary: {
121
+ primary: _objectSpread({
98
122
  background: 'surface',
99
123
  border: 'primary',
100
124
  borderWidth: 'large',
@@ -105,8 +129,8 @@ var variants = {
105
129
  backgroundActive: 'none',
106
130
  borderActive: 'primaryActive',
107
131
  textToneActive: 'primaryActive'
108
- },
109
- secondary: {
132
+ }, lowDisabledAltStyles),
133
+ secondary: _objectSpread({
110
134
  background: 'surface',
111
135
  border: 'secondary',
112
136
  borderWidth: 'large',
@@ -117,76 +141,76 @@ var variants = {
117
141
  backgroundActive: 'none',
118
142
  borderActive: 'secondaryActive',
119
143
  textToneActive: 'secondaryActive'
120
- },
121
- neutral: {
144
+ }, lowDisabledAltStyles),
145
+ neutral: _objectSpread({
122
146
  background: 'neutralLow',
123
147
  backgroundHover: 'neutralLowHover',
124
148
  backgroundActive: 'neutralLowActive'
125
- },
126
- positive: {
149
+ }, lowDisabledStyles),
150
+ positive: _objectSpread({
127
151
  background: 'positiveLow',
128
152
  backgroundHover: 'positiveLowHover',
129
153
  backgroundActive: 'positiveLowActive'
130
- },
131
- caution: {
154
+ }, lowDisabledStyles),
155
+ caution: _objectSpread({
132
156
  background: 'cautionLow',
133
157
  backgroundHover: 'cautionLowHover',
134
158
  backgroundActive: 'cautionLowActive'
135
- },
136
- critical: {
159
+ }, lowDisabledStyles),
160
+ critical: _objectSpread({
137
161
  background: 'criticalLow',
138
162
  backgroundHover: 'criticalLowHover',
139
163
  backgroundActive: 'criticalLowActive'
140
- },
141
- info: {
164
+ }, lowDisabledStyles),
165
+ info: _objectSpread({
142
166
  background: 'infoLow',
143
167
  backgroundHover: 'infoLowHover',
144
168
  backgroundActive: 'infoLowActive'
145
- }
169
+ }, lowDisabledStyles)
146
170
  },
147
171
  none: {
148
- primary: {
172
+ primary: _objectSpread({
149
173
  background: 'surface',
150
174
  textTone: 'primaryActive',
151
175
  backgroundHover: 'primaryLowHover',
152
176
  backgroundActive: 'primaryLowActive'
153
- },
154
- secondary: {
177
+ }, noneDisabledStyles),
178
+ secondary: _objectSpread({
155
179
  background: 'surface',
156
180
  textTone: 'secondaryActive',
157
181
  backgroundHover: 'secondaryLowHover',
158
182
  backgroundActive: 'secondaryLowActive'
159
- },
160
- neutral: {
183
+ }, noneDisabledStyles),
184
+ neutral: _objectSpread({
161
185
  background: 'surface',
162
186
  textTone: 'neutral',
163
187
  backgroundHover: 'neutralLowHover',
164
188
  backgroundActive: 'neutralLowActive'
165
- },
166
- positive: {
189
+ }, noneDisabledStyles),
190
+ positive: _objectSpread({
167
191
  background: 'surface',
168
192
  textTone: 'positive',
169
193
  backgroundHover: 'positiveLowHover',
170
194
  backgroundActive: 'positiveLowActive'
171
- },
172
- caution: {
195
+ }, noneDisabledStyles),
196
+ caution: _objectSpread({
173
197
  background: 'surface',
174
198
  textTone: 'caution',
175
199
  backgroundHover: 'cautionLowHover',
176
200
  backgroundActive: 'cautionLowActive'
177
- },
178
- critical: {
201
+ }, noneDisabledStyles),
202
+ critical: _objectSpread({
179
203
  background: 'surface',
180
204
  textTone: 'critical',
181
205
  backgroundHover: 'criticalLowHover',
182
206
  backgroundActive: 'criticalLowActive'
183
- },
184
- info: {
207
+ }, noneDisabledStyles),
208
+ info: _objectSpread({
185
209
  background: 'surface',
186
210
  textTone: 'info',
187
211
  backgroundHover: 'infoLowHover',
188
212
  backgroundActive: 'infoLowActive'
189
- }
213
+ }, noneDisabledStyles)
190
214
  }
191
215
  };
192
216
  var mapTokens = {
@@ -212,7 +236,7 @@ var resolveButtonChildren = function resolveButtonChildren(_ref) {
212
236
  tone = _ref.tone;
213
237
  var variant = variants[prominence][tone];
214
238
  return Children.map(children, function (child) {
215
- if (typeof child === 'string') {
239
+ if (typeof child === 'string' || typeof child === 'number') {
216
240
  return /*#__PURE__*/jsx(HiddenWhenLoading, {
217
241
  isLoading: isLoading,
218
242
  children: /*#__PURE__*/jsx(Text, {
@@ -259,6 +283,15 @@ function HiddenWhenLoading(_ref2) {
259
283
  });
260
284
  }
261
285
 
286
+ /**
287
+ * useButtonStyles
288
+ *
289
+ * Custom hook for styling buttons and certain links.
290
+ * Returns a tuple where the first item is an object of props to spread onto the
291
+ * underlying `Box` component, and the second item is a CSS object that can be
292
+ * passed to Emotion's `css` function.
293
+ */
294
+
262
295
  function useButtonStyles(_ref) {
263
296
  var iconOnly = _ref.iconOnly,
264
297
  prominence = _ref.prominence,
@@ -268,14 +301,17 @@ function useButtonStyles(_ref) {
268
301
  var focusRingStyles = useFocusRing({
269
302
  tone: tone
270
303
  });
304
+ var disabledFocusRingStyles = useFocusRing({
305
+ tone: 'disabled'
306
+ });
271
307
  var variant = variants[prominence][tone];
272
308
  var isLarge = size === 'large';
273
- var transitionColours = {
274
- transitionProperty: 'color, background-color, border-color, text-decoration-color',
275
- transitionTimingFunction: 'cubic-bezier(0.02, 1.505, 0.745, 1.235)',
309
+ var transitionColors = {
310
+ transitionProperty: 'color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter',
311
+ transitionTimingFunction: theme.animation.standard.easing,
276
312
  transitionDuration: "".concat(theme.animation.standard.duration, "ms")
277
313
  };
278
- var buttonStyleProps = {
314
+ return [{
279
315
  alignItems: 'center',
280
316
  background: variant === null || variant === void 0 ? void 0 : variant.background,
281
317
  border: variant === null || variant === void 0 ? void 0 : variant.border,
@@ -288,32 +324,44 @@ function useButtonStyles(_ref) {
288
324
  justifyContent: 'center',
289
325
  paddingX: iconOnly ? undefined : mapTokens.spacing[size],
290
326
  position: 'relative',
291
- width: iconOnly ? mapTokens.size[size] : undefined,
292
- // interactions styles
293
- className: css(_objectSpread(_objectSpread({}, transitionColours), {}, {
294
- '&:hover': {
327
+ width: iconOnly ? mapTokens.size[size] : undefined
328
+ }, _objectSpread(_objectSpread({}, transitionColors), {}, {
329
+ // Styles for buttons that aren't disabled.
330
+ // Using the :not() pseudo-class so we don't have to undo the styles when
331
+ // the button is disabled.
332
+ '&:not([aria-disabled=true])': {
333
+ ':hover': {
295
334
  borderColor: variant !== null && variant !== void 0 && variant.borderHover ? theme.border.color[variant.borderHover] : undefined,
296
335
  backgroundColor: variant !== null && variant !== void 0 && variant.backgroundHover ? theme.backgroundInteractions[variant.backgroundHover] : undefined,
297
336
  // Style button text when hovering
298
- '> *': _objectSpread(_objectSpread({}, transitionColours), {}, {
337
+ '> *': _objectSpread(_objectSpread({}, transitionColors), {}, {
299
338
  color: variant !== null && variant !== void 0 && variant.textToneHover ? theme.color.foreground[variant.textToneHover] : undefined,
300
339
  stroke: variant !== null && variant !== void 0 && variant.textToneHover ? theme.color.foreground[variant.textToneHover] : undefined
301
340
  })
302
341
  },
303
- '&:active': {
342
+ ':active': {
304
343
  borderColor: variant !== null && variant !== void 0 && variant.borderActive ? theme.border.color[variant.borderActive] : undefined,
305
344
  backgroundColor: variant !== null && variant !== void 0 && variant.backgroundActive ? theme.backgroundInteractions[variant === null || variant === void 0 ? void 0 : variant.backgroundActive] : undefined,
306
345
  transform: 'scale(0.98)',
307
346
  // Style button text when it's active
308
- '> *': _objectSpread(_objectSpread({}, transitionColours), {}, {
347
+ '> *': _objectSpread(_objectSpread({}, transitionColors), {}, {
309
348
  color: variant !== null && variant !== void 0 && variant.textToneActive ? theme.color.foreground[variant.textToneActive] : undefined,
310
349
  stroke: variant !== null && variant !== void 0 && variant.textToneActive ? theme.color.foreground[variant.textToneActive] : undefined
311
350
  })
312
351
  },
313
352
  ':focus': focusRingStyles
314
- }))
315
- };
316
- return buttonStyleProps;
353
+ },
354
+ '&[aria-disabled=true]': {
355
+ backgroundColor: variant !== null && variant !== void 0 && variant.backgroundDisabled ? theme.color.background[variant === null || variant === void 0 ? void 0 : variant.backgroundDisabled] : undefined,
356
+ borderColor: variant !== null && variant !== void 0 && variant.borderDisabled ? theme.border.color[variant.borderDisabled] : undefined,
357
+ cursor: 'default',
358
+ '*': {
359
+ color: variant !== null && variant !== void 0 && variant.textToneDisabled ? theme.color.foreground[variant.textToneDisabled] : undefined,
360
+ stroke: variant !== null && variant !== void 0 && variant.textToneDisabled ? theme.color.foreground[variant.textToneDisabled] : undefined
361
+ },
362
+ ':focus': disabledFocusRingStyles
363
+ }
364
+ })];
317
365
  }
318
366
 
319
367
  var _excluded$1 = ["aria-controls", "aria-describedby", "aria-expanded", "data", "disabled", "id", "loading", "onClick", "prominence", "size", "tone", "type"];
@@ -342,21 +390,26 @@ var Button = /*#__PURE__*/forwardRef(function (_ref, forwardedRef) {
342
390
  props = _objectWithoutProperties(_ref, _excluded$1);
343
391
 
344
392
  var iconOnly = Boolean(props.label);
345
- var buttonStyleProps = useButtonStyles({
393
+
394
+ var _useButtonStyles = useButtonStyles({
346
395
  iconOnly: iconOnly,
347
396
  size: size,
348
397
  tone: tone,
349
398
  prominence: prominence
350
- });
399
+ }),
400
+ _useButtonStyles2 = _slicedToArray(_useButtonStyles, 2),
401
+ boxProps = _useButtonStyles2[0],
402
+ buttonStyles = _useButtonStyles2[1];
403
+
351
404
  var isDisabled = disabled || loading;
352
405
  var isLoading = loading && !disabled;
353
406
  var variant = variants[prominence][tone];
354
- return /*#__PURE__*/jsxs(BaseButton, _objectSpread(_objectSpread({}, buttonStyleProps), {}, {
407
+ return /*#__PURE__*/jsxs(BaseButton, _objectSpread(_objectSpread({}, boxProps), {}, {
355
408
  "aria-controls": ariaControls,
356
409
  "aria-describedby": ariaDescribedBy,
357
- "aria-disabled": isDisabled,
358
410
  "aria-expanded": ariaExpanded,
359
411
  "aria-label": props.label,
412
+ className: css(buttonStyles),
360
413
  data: data,
361
414
  disabled: isDisabled,
362
415
  id: id,
@@ -399,7 +452,7 @@ function Loading(_ref2) {
399
452
  var _excluded = ["data", "href", "id", "prominence", "size", "tone"];
400
453
 
401
454
  /** The appearance of a `Button`, with the semantics of a link. */
402
- var ButtonLink = forwardRefWithAs(function (_ref, ref) {
455
+ var ButtonLink = forwardRefWithAs(function (_ref, forwardedRef) {
403
456
  var data = _ref.data,
404
457
  href = _ref.href,
405
458
  id = _ref.id,
@@ -409,25 +462,31 @@ var ButtonLink = forwardRefWithAs(function (_ref, ref) {
409
462
  size = _ref$size === void 0 ? 'medium' : _ref$size,
410
463
  _ref$tone = _ref.tone,
411
464
  tone = _ref$tone === void 0 ? 'primary' : _ref$tone,
412
- props = _objectWithoutProperties(_ref, _excluded);
465
+ consumerProps = _objectWithoutProperties(_ref, _excluded);
413
466
 
414
- var LinkComponent = useLinkComponent(ref);
415
- var iconOnly = Boolean(props.label);
416
- var buttonStyleProps = useButtonStyles({
467
+ var LinkComponent = useLinkComponent(forwardedRef);
468
+ var iconOnly = Boolean(consumerProps.label);
469
+
470
+ var _useButtonStyles = useButtonStyles({
417
471
  iconOnly: iconOnly,
418
472
  prominence: prominence,
419
473
  size: size,
420
474
  tone: tone
421
- });
422
- return /*#__PURE__*/jsx(Box, _objectSpread(_objectSpread(_objectSpread({
423
- "aria-label": props.label,
475
+ }),
476
+ _useButtonStyles2 = _slicedToArray(_useButtonStyles, 2),
477
+ boxProps = _useButtonStyles2[0],
478
+ buttonStyles = _useButtonStyles2[1];
479
+
480
+ return /*#__PURE__*/jsx(Box, _objectSpread(_objectSpread({}, boxProps), {}, {
481
+ "aria-label": consumerProps.label,
424
482
  as: LinkComponent,
425
483
  asElement: "a",
426
- id: id,
484
+ className: css(buttonStyles),
485
+ data: data,
427
486
  href: href,
428
- ref: ref
429
- }, buttonStyleProps), data ? buildDataAttributes(data) : undefined), {}, {
430
- children: resolveButtonChildren(_objectSpread(_objectSpread({}, props), {}, {
487
+ id: id,
488
+ ref: forwardedRef,
489
+ children: resolveButtonChildren(_objectSpread(_objectSpread({}, consumerProps), {}, {
431
490
  isLoading: false,
432
491
  prominence: prominence,
433
492
  size: size,
@@ -436,4 +495,4 @@ var ButtonLink = forwardRefWithAs(function (_ref, ref) {
436
495
  }));
437
496
  });
438
497
 
439
- export { BaseButton, Button, ButtonLink };
498
+ export { BaseButton, Button, ButtonLink, useButtonStyles };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spark-web/button",
3
- "version": "1.2.0",
3
+ "version": "1.4.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.3",
11
+ "@babel/runtime": "^7.18.9",
12
12
  "@emotion/css": "^11.9.0",
13
- "@spark-web/a11y": "^1.1.0",
14
- "@spark-web/box": "^1.0.6",
15
- "@spark-web/icon": "^1.1.4",
16
- "@spark-web/link": "^1.0.6",
17
- "@spark-web/spinner": "^1.0.4",
18
- "@spark-web/text": "^1.0.6",
19
- "@spark-web/theme": "^3.0.2",
20
- "@spark-web/utils": "^1.1.5"
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.4",
20
+ "@spark-web/utils": "^1.2.0"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/react": "^17.0.12",