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