@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.
@@ -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 css = require('@emotion/css');
13
- var text = require('@spark-web/text');
14
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
+ 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, {
@@ -203,10 +276,12 @@ var resolveButtonChildren = function resolveButtonChildren(_ref) {
203
276
  function HiddenWhenLoading(_ref2) {
204
277
  var children = _ref2.children,
205
278
  isLoading = _ref2.isLoading;
206
- return /*#__PURE__*/jsxRuntime.jsx("span", {
207
- className: isLoading ? css.css({
208
- opacity: 0
209
- }) : undefined,
279
+ return /*#__PURE__*/jsxRuntime.jsx(box.Box, {
280
+ as: "span",
281
+ display: "inline-flex",
282
+ alignItems: "center",
283
+ justifyContent: "center",
284
+ opacity: isLoading ? 0 : undefined,
210
285
  children: children
211
286
  });
212
287
  }
@@ -220,9 +295,12 @@ function useButtonStyles(_ref) {
220
295
  var focusRingStyles = a11y.useFocusRing({
221
296
  tone: tone
222
297
  });
298
+ var disabledFocusRingStyles = a11y.useFocusRing({
299
+ tone: 'disabled'
300
+ });
223
301
  var variant = variants[prominence][tone];
224
302
  var isLarge = size === 'large';
225
- var transitionColours = {
303
+ var transitionColors = {
226
304
  transitionProperty: 'color, background-color, border-color, text-decoration-color',
227
305
  transitionTimingFunction: 'cubic-bezier(0.02, 1.505, 0.745, 1.235)',
228
306
  transitionDuration: "".concat(theme$1.animation.standard.duration, "ms")
@@ -242,27 +320,38 @@ function useButtonStyles(_ref) {
242
320
  position: 'relative',
243
321
  width: iconOnly ? mapTokens.size[size] : undefined,
244
322
  // interactions styles
245
- className: css.css(_objectSpread(_objectSpread({}, transitionColours), {}, {
246
- '&:hover': {
247
- borderColor: variant !== null && variant !== void 0 && variant.borderHover ? theme$1.border.color[variant.borderHover] : undefined,
248
- backgroundColor: variant !== null && variant !== void 0 && variant.backgroundHover ? theme$1.backgroundInteractions[variant.backgroundHover] : undefined,
249
- // Style button text when hovering
250
- '> *': _objectSpread(_objectSpread({}, transitionColours), {}, {
251
- color: variant !== null && variant !== void 0 && variant.textToneHover ? theme$1.color.foreground[variant.textToneHover] : undefined,
252
- stroke: variant !== null && variant !== void 0 && variant.textToneHover ? theme$1.color.foreground[variant.textToneHover] : undefined
253
- })
254
- },
255
- '&:active': {
256
- borderColor: variant !== null && variant !== void 0 && variant.borderActive ? theme$1.border.color[variant.borderActive] : undefined,
257
- backgroundColor: variant !== null && variant !== void 0 && variant.backgroundActive ? theme$1.backgroundInteractions[variant === null || variant === void 0 ? void 0 : variant.backgroundActive] : undefined,
258
- transform: 'scale(0.98)',
259
- // Style button text when it's active
260
- '> *': _objectSpread(_objectSpread({}, transitionColours), {}, {
261
- color: variant !== null && variant !== void 0 && variant.textToneActive ? theme$1.color.foreground[variant.textToneActive] : undefined,
262
- stroke: variant !== null && variant !== void 0 && variant.textToneActive ? theme$1.color.foreground[variant.textToneActive] : undefined
263
- })
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
264
345
  },
265
- ':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
+ '*': {
350
+ color: variant !== null && variant !== void 0 && variant.textToneDisabled ? theme$1.color.foreground[variant.textToneDisabled] : undefined,
351
+ stroke: variant !== null && variant !== void 0 && variant.textToneDisabled ? theme$1.color.foreground[variant.textToneDisabled] : undefined
352
+ },
353
+ ':focus': disabledFocusRingStyles
354
+ }
266
355
  }))
267
356
  };
268
357
  return buttonStyleProps;
@@ -274,13 +363,12 @@ var _excluded$1 = ["aria-controls", "aria-describedby", "aria-expanded", "data",
274
363
  * Buttons are used to initialize an action, their label should express what
275
364
  * action will occur when the user interacts with it.
276
365
  */
277
- var Button = /*#__PURE__*/react.forwardRef(function (_ref, ref) {
366
+ var Button = /*#__PURE__*/react.forwardRef(function (_ref, forwardedRef) {
278
367
  var ariaControls = _ref['aria-controls'],
279
368
  ariaDescribedBy = _ref['aria-describedby'],
280
369
  ariaExpanded = _ref['aria-expanded'],
281
370
  data = _ref.data,
282
- _ref$disabled = _ref.disabled,
283
- disabled = _ref$disabled === void 0 ? false : _ref$disabled,
371
+ disabled = _ref.disabled,
284
372
  id = _ref.id,
285
373
  _ref$loading = _ref.loading,
286
374
  loading = _ref$loading === void 0 ? false : _ref$loading,
@@ -291,8 +379,7 @@ var Button = /*#__PURE__*/react.forwardRef(function (_ref, ref) {
291
379
  size = _ref$size === void 0 ? 'medium' : _ref$size,
292
380
  _ref$tone = _ref.tone,
293
381
  tone = _ref$tone === void 0 ? 'primary' : _ref$tone,
294
- _ref$type = _ref.type,
295
- type = _ref$type === void 0 ? 'button' : _ref$type,
382
+ type = _ref.type,
296
383
  props = _objectWithoutProperties(_ref, _excluded$1);
297
384
 
298
385
  var iconOnly = Boolean(props.label);
@@ -305,24 +392,17 @@ var Button = /*#__PURE__*/react.forwardRef(function (_ref, ref) {
305
392
  var isDisabled = disabled || loading;
306
393
  var isLoading = loading && !disabled;
307
394
  var variant = variants[prominence][tone];
308
- /**
309
- * handle "disabled" behaviour w/o disabling buttons
310
- * @see https://axesslab.com/disabled-buttons-suck/
311
- */
312
-
313
- var handleClick = getPreventableClickHandler(onClick, isDisabled);
314
- return /*#__PURE__*/jsxRuntime.jsxs(box.Box, _objectSpread(_objectSpread(_objectSpread({
395
+ return /*#__PURE__*/jsxRuntime.jsxs(BaseButton, _objectSpread(_objectSpread({}, buttonStyleProps), {}, {
315
396
  "aria-controls": ariaControls,
316
397
  "aria-describedby": ariaDescribedBy,
317
- "aria-disabled": isDisabled,
318
398
  "aria-expanded": ariaExpanded,
319
399
  "aria-label": props.label,
320
- as: "button",
400
+ data: data,
401
+ disabled: isDisabled,
321
402
  id: id,
322
- onClick: handleClick,
323
- ref: ref,
324
- type: type
325
- }, buttonStyleProps), data ? internal.buildDataAttributes(data) : undefined), {}, {
403
+ onClick: onClick,
404
+ ref: forwardedRef,
405
+ type: type,
326
406
  children: [resolveButtonChildren(_objectSpread(_objectSpread({}, props), {}, {
327
407
  isLoading: isLoading,
328
408
  prominence: prominence,
@@ -334,21 +414,6 @@ var Button = /*#__PURE__*/react.forwardRef(function (_ref, ref) {
334
414
  }));
335
415
  });
336
416
  Button.displayName = 'Button';
337
- /**
338
- * Prevent click events when the component is "disabled".
339
- * Note: we don't want to actually disable a button element for several reasons.
340
- * One being because that would prohibit the use of tooltips.
341
- */
342
-
343
- function getPreventableClickHandler(onClick, disabled) {
344
- return function handleClick(event) {
345
- if (disabled) {
346
- event.preventDefault();
347
- } else {
348
- onClick === null || onClick === void 0 ? void 0 : onClick(event);
349
- }
350
- };
351
- }
352
417
 
353
418
  function Loading(_ref2) {
354
419
  var tone = _ref2.tone;
@@ -411,5 +476,6 @@ var ButtonLink = ts.forwardRefWithAs(function (_ref, ref) {
411
476
  }));
412
477
  });
413
478
 
479
+ exports.BaseButton = BaseButton;
414
480
  exports.Button = Button;
415
481
  exports.ButtonLink = ButtonLink;