@spark-web/button 0.0.0-snapshot-release-20220907020533

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.
@@ -0,0 +1,505 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var _objectSpread = require('@babel/runtime/helpers/objectSpread2');
6
+ var _objectWithoutProperties = require('@babel/runtime/helpers/objectWithoutProperties');
7
+ var box = require('@spark-web/box');
8
+ var utils = require('@spark-web/utils');
9
+ var react = require('react');
10
+ var jsxRuntime = require('react/jsx-runtime');
11
+ var _slicedToArray = require('@babel/runtime/helpers/slicedToArray');
12
+ var css = require('@emotion/css');
13
+ var a11y = require('@spark-web/a11y');
14
+ var spinner = require('@spark-web/spinner');
15
+ var text = require('@spark-web/text');
16
+ var theme = require('@spark-web/theme');
17
+ var link = require('@spark-web/link');
18
+ var ts = require('@spark-web/utils/ts');
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 // Hide aria-disabled attribute when button is not disabled
48
+ ,
49
+ "aria-disabled": disabled || undefined,
50
+ onClick: onClick,
51
+ type: type
52
+ }));
53
+ });
54
+ BaseButton.displayName = 'BaseButton';
55
+ /**
56
+ * handle "disabled" behaviour w/o disabling buttons
57
+ * @see https://axesslab.com/disabled-buttons-suck/
58
+ */
59
+
60
+ function getPreventableClickHandler(onClick, disabled) {
61
+ return function handleClick(event) {
62
+ if (disabled) {
63
+ event.preventDefault();
64
+ } else {
65
+ onClick === null || onClick === void 0 ? void 0 : onClick(event);
66
+ }
67
+ };
68
+ }
69
+
70
+ var highDisabledStyles = {
71
+ backgroundDisabled: 'disabled',
72
+ borderDisabled: 'fieldDisabled',
73
+ textToneDisabled: 'neutralInverted'
74
+ };
75
+ var highDisabledAltStyles = {
76
+ backgroundDisabled: 'neutral',
77
+ borderDisabled: 'standard',
78
+ textToneDisabled: 'placeholder'
79
+ };
80
+ var lowDisabledStyles = {
81
+ backgroundDisabled: 'inputDisabled',
82
+ textToneDisabled: 'disabled'
83
+ };
84
+ var lowDisabledAltStyles = {
85
+ backgroundDisabled: 'inputDisabled',
86
+ borderDisabled: 'fieldDisabled',
87
+ textToneDisabled: 'disabled'
88
+ };
89
+ var noneDisabledStyles = {
90
+ backgroundDisabled: 'neutral',
91
+ textToneDisabled: 'disabled'
92
+ };
93
+ var variants = {
94
+ high: {
95
+ primary: _objectSpread({
96
+ background: 'primary',
97
+ backgroundHover: 'primaryHover',
98
+ backgroundActive: 'primaryActive'
99
+ }, highDisabledStyles),
100
+ secondary: _objectSpread({
101
+ background: 'secondary',
102
+ backgroundHover: 'secondaryHover',
103
+ backgroundActive: 'secondaryActive'
104
+ }, highDisabledStyles),
105
+ neutral: _objectSpread({
106
+ background: 'neutral',
107
+ border: 'field',
108
+ backgroundHover: 'neutralHover',
109
+ backgroundActive: 'neutralActive'
110
+ }, highDisabledAltStyles),
111
+ positive: _objectSpread({
112
+ background: 'positive',
113
+ backgroundHover: 'positiveHover',
114
+ backgroundActive: 'positiveActive'
115
+ }, highDisabledStyles),
116
+ critical: _objectSpread({
117
+ background: 'critical',
118
+ backgroundHover: 'criticalHover',
119
+ backgroundActive: 'criticalActive'
120
+ }, highDisabledStyles),
121
+ caution: undefined,
122
+ info: undefined
123
+ },
124
+ low: {
125
+ primary: _objectSpread({
126
+ background: 'surface',
127
+ border: 'primary',
128
+ borderWidth: 'large',
129
+ textTone: 'primary',
130
+ backgroundHover: 'none',
131
+ borderHover: 'primaryHover',
132
+ textToneHover: 'primaryHover',
133
+ backgroundActive: 'none',
134
+ borderActive: 'primaryActive',
135
+ textToneActive: 'primaryActive'
136
+ }, lowDisabledAltStyles),
137
+ secondary: _objectSpread({
138
+ background: 'surface',
139
+ border: 'secondary',
140
+ borderWidth: 'large',
141
+ textTone: 'secondary',
142
+ backgroundHover: 'none',
143
+ borderHover: 'secondaryHover',
144
+ textToneHover: 'secondaryHover',
145
+ backgroundActive: 'none',
146
+ borderActive: 'secondaryActive',
147
+ textToneActive: 'secondaryActive'
148
+ }, lowDisabledAltStyles),
149
+ neutral: _objectSpread({
150
+ background: 'neutralLow',
151
+ backgroundHover: 'neutralLowHover',
152
+ backgroundActive: 'neutralLowActive'
153
+ }, lowDisabledStyles),
154
+ positive: _objectSpread({
155
+ background: 'positiveLow',
156
+ backgroundHover: 'positiveLowHover',
157
+ backgroundActive: 'positiveLowActive'
158
+ }, lowDisabledStyles),
159
+ caution: _objectSpread({
160
+ background: 'cautionLow',
161
+ backgroundHover: 'cautionLowHover',
162
+ backgroundActive: 'cautionLowActive'
163
+ }, lowDisabledStyles),
164
+ critical: _objectSpread({
165
+ background: 'criticalLow',
166
+ backgroundHover: 'criticalLowHover',
167
+ backgroundActive: 'criticalLowActive'
168
+ }, lowDisabledStyles),
169
+ info: _objectSpread({
170
+ background: 'infoLow',
171
+ backgroundHover: 'infoLowHover',
172
+ backgroundActive: 'infoLowActive'
173
+ }, lowDisabledStyles)
174
+ },
175
+ none: {
176
+ primary: _objectSpread({
177
+ background: 'surface',
178
+ textTone: 'primaryActive',
179
+ backgroundHover: 'primaryLowHover',
180
+ backgroundActive: 'primaryLowActive'
181
+ }, noneDisabledStyles),
182
+ secondary: _objectSpread({
183
+ background: 'surface',
184
+ textTone: 'secondaryActive',
185
+ backgroundHover: 'secondaryLowHover',
186
+ backgroundActive: 'secondaryLowActive'
187
+ }, noneDisabledStyles),
188
+ neutral: _objectSpread({
189
+ background: 'surface',
190
+ textTone: 'neutral',
191
+ backgroundHover: 'neutralLowHover',
192
+ backgroundActive: 'neutralLowActive'
193
+ }, noneDisabledStyles),
194
+ positive: _objectSpread({
195
+ background: 'surface',
196
+ textTone: 'positive',
197
+ backgroundHover: 'positiveLowHover',
198
+ backgroundActive: 'positiveLowActive'
199
+ }, noneDisabledStyles),
200
+ caution: _objectSpread({
201
+ background: 'surface',
202
+ textTone: 'caution',
203
+ backgroundHover: 'cautionLowHover',
204
+ backgroundActive: 'cautionLowActive'
205
+ }, noneDisabledStyles),
206
+ critical: _objectSpread({
207
+ background: 'surface',
208
+ textTone: 'critical',
209
+ backgroundHover: 'criticalLowHover',
210
+ backgroundActive: 'criticalLowActive'
211
+ }, noneDisabledStyles),
212
+ info: _objectSpread({
213
+ background: 'surface',
214
+ textTone: 'info',
215
+ backgroundHover: 'infoLowHover',
216
+ backgroundActive: 'infoLowActive'
217
+ }, noneDisabledStyles)
218
+ }
219
+ };
220
+ var mapTokens = {
221
+ fontSize: {
222
+ medium: 'small',
223
+ large: 'standard'
224
+ },
225
+ size: {
226
+ medium: 'medium',
227
+ large: 'large'
228
+ },
229
+ spacing: {
230
+ medium: 'medium',
231
+ large: 'xlarge'
232
+ }
233
+ };
234
+
235
+ var resolveButtonChildren = function resolveButtonChildren(_ref) {
236
+ var children = _ref.children,
237
+ isLoading = _ref.isLoading,
238
+ prominence = _ref.prominence,
239
+ size = _ref.size,
240
+ tone = _ref.tone;
241
+ var variant = variants[prominence][tone];
242
+ return react.Children.map(children, function (child) {
243
+ if (typeof child === 'string' || typeof child === 'number') {
244
+ return /*#__PURE__*/jsxRuntime.jsx(HiddenWhenLoading, {
245
+ isLoading: isLoading,
246
+ children: /*#__PURE__*/jsxRuntime.jsx(text.Text, {
247
+ as: "span",
248
+ baseline: false,
249
+ overflowStrategy: "nowrap",
250
+ weight: "semibold",
251
+ size: mapTokens.fontSize[size],
252
+ tone: variant === null || variant === void 0 ? void 0 : variant.textTone,
253
+ children: child
254
+ })
255
+ });
256
+ }
257
+
258
+ if ( /*#__PURE__*/react.isValidElement(child)) {
259
+ return /*#__PURE__*/jsxRuntime.jsx(HiddenWhenLoading, {
260
+ isLoading: isLoading,
261
+ children: /*#__PURE__*/react.cloneElement(child, {
262
+ // Dismiss buttons need to be `xxsmall`
263
+ // For everything else, we force them to be `xsmall`
264
+ size: child.props.size === 'xxsmall' ? child.props.size : 'xsmall',
265
+ // If the button is low prominence with a decorative tone we want to force
266
+ // the tone to be the same as the button
267
+ // We also don't want users to override the tone of the icon inside of the button
268
+ tone: variant === null || variant === void 0 ? void 0 : variant.textTone
269
+ })
270
+ });
271
+ }
272
+
273
+ return null;
274
+ });
275
+ };
276
+
277
+ function HiddenWhenLoading(_ref2) {
278
+ var children = _ref2.children,
279
+ isLoading = _ref2.isLoading;
280
+ return /*#__PURE__*/jsxRuntime.jsx(box.Box, {
281
+ as: "span",
282
+ display: "inline-flex",
283
+ alignItems: "center",
284
+ justifyContent: "center",
285
+ opacity: isLoading ? 0 : undefined,
286
+ children: children
287
+ });
288
+ }
289
+
290
+ /**
291
+ * useButtonStyles
292
+ *
293
+ * Custom hook for styling buttons and certain links.
294
+ * Returns a tuple where the first item is an object of props to spread onto the
295
+ * underlying `Box` component, and the second item is a CSS object that can be
296
+ * passed to Emotion's `css` function.
297
+ */
298
+
299
+ function useButtonStyles(_ref) {
300
+ var iconOnly = _ref.iconOnly,
301
+ prominence = _ref.prominence,
302
+ size = _ref.size,
303
+ tone = _ref.tone;
304
+ var theme$1 = theme.useTheme();
305
+ var focusRingStyles = a11y.useFocusRing({
306
+ tone: tone
307
+ });
308
+ var disabledFocusRingStyles = a11y.useFocusRing({
309
+ tone: 'disabled'
310
+ });
311
+ var variant = variants[prominence][tone];
312
+ var isLarge = size === 'large';
313
+ var transitionColors = {
314
+ transitionProperty: 'color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter',
315
+ transitionTimingFunction: theme$1.animation.standard.easing,
316
+ transitionDuration: "".concat(theme$1.animation.standard.duration, "ms")
317
+ };
318
+ return [{
319
+ alignItems: 'center',
320
+ background: variant === null || variant === void 0 ? void 0 : variant.background,
321
+ border: variant === null || variant === void 0 ? void 0 : variant.border,
322
+ borderWidth: variant === null || variant === void 0 ? void 0 : variant.borderWidth,
323
+ borderRadius: isLarge ? 'medium' : 'small',
324
+ cursor: 'pointer',
325
+ display: 'inline-flex',
326
+ gap: 'small',
327
+ height: mapTokens.size[size],
328
+ justifyContent: 'center',
329
+ paddingX: iconOnly ? undefined : mapTokens.spacing[size],
330
+ position: 'relative',
331
+ width: iconOnly ? mapTokens.size[size] : undefined
332
+ }, _objectSpread(_objectSpread({}, transitionColors), {}, {
333
+ // Styles for buttons that aren't disabled.
334
+ // Using the :not() pseudo-class so we don't have to undo the styles when
335
+ // the button is disabled.
336
+ '&:not([aria-disabled=true])': {
337
+ ':hover': {
338
+ borderColor: variant !== null && variant !== void 0 && variant.borderHover ? theme$1.border.color[variant.borderHover] : undefined,
339
+ backgroundColor: variant !== null && variant !== void 0 && variant.backgroundHover ? theme$1.backgroundInteractions[variant.backgroundHover] : undefined,
340
+ // Style button text when hovering
341
+ '> *': _objectSpread(_objectSpread({}, transitionColors), {}, {
342
+ color: variant !== null && variant !== void 0 && variant.textToneHover ? theme$1.color.foreground[variant.textToneHover] : undefined,
343
+ stroke: variant !== null && variant !== void 0 && variant.textToneHover ? theme$1.color.foreground[variant.textToneHover] : undefined
344
+ })
345
+ },
346
+ ':active': {
347
+ borderColor: variant !== null && variant !== void 0 && variant.borderActive ? theme$1.border.color[variant.borderActive] : undefined,
348
+ backgroundColor: variant !== null && variant !== void 0 && variant.backgroundActive ? theme$1.backgroundInteractions[variant === null || variant === void 0 ? void 0 : variant.backgroundActive] : undefined,
349
+ transform: 'scale(0.98)',
350
+ // Style button text when it's active
351
+ '> *': _objectSpread(_objectSpread({}, transitionColors), {}, {
352
+ color: variant !== null && variant !== void 0 && variant.textToneActive ? theme$1.color.foreground[variant.textToneActive] : undefined,
353
+ stroke: variant !== null && variant !== void 0 && variant.textToneActive ? theme$1.color.foreground[variant.textToneActive] : undefined
354
+ })
355
+ },
356
+ ':focus': focusRingStyles
357
+ },
358
+ '&[aria-disabled=true]': {
359
+ backgroundColor: variant !== null && variant !== void 0 && variant.backgroundDisabled ? theme$1.color.background[variant === null || variant === void 0 ? void 0 : variant.backgroundDisabled] : undefined,
360
+ borderColor: variant !== null && variant !== void 0 && variant.borderDisabled ? theme$1.border.color[variant.borderDisabled] : undefined,
361
+ cursor: 'default',
362
+ '*': {
363
+ color: variant !== null && variant !== void 0 && variant.textToneDisabled ? theme$1.color.foreground[variant.textToneDisabled] : undefined,
364
+ stroke: variant !== null && variant !== void 0 && variant.textToneDisabled ? theme$1.color.foreground[variant.textToneDisabled] : undefined
365
+ },
366
+ ':focus': disabledFocusRingStyles
367
+ }
368
+ })];
369
+ }
370
+
371
+ var _excluded$1 = ["aria-controls", "aria-describedby", "aria-expanded", "data", "disabled", "id", "loading", "onClick", "prominence", "size", "tone", "type"];
372
+
373
+ /**
374
+ * Buttons are used to initialize an action, their label should express what
375
+ * action will occur when the user interacts with it.
376
+ */
377
+ var Button = /*#__PURE__*/react.forwardRef(function (_ref, forwardedRef) {
378
+ var ariaControls = _ref['aria-controls'],
379
+ ariaDescribedBy = _ref['aria-describedby'],
380
+ ariaExpanded = _ref['aria-expanded'],
381
+ data = _ref.data,
382
+ disabled = _ref.disabled,
383
+ id = _ref.id,
384
+ _ref$loading = _ref.loading,
385
+ loading = _ref$loading === void 0 ? false : _ref$loading,
386
+ onClick = _ref.onClick,
387
+ _ref$prominence = _ref.prominence,
388
+ prominence = _ref$prominence === void 0 ? 'high' : _ref$prominence,
389
+ _ref$size = _ref.size,
390
+ size = _ref$size === void 0 ? 'medium' : _ref$size,
391
+ _ref$tone = _ref.tone,
392
+ tone = _ref$tone === void 0 ? 'primary' : _ref$tone,
393
+ type = _ref.type,
394
+ props = _objectWithoutProperties(_ref, _excluded$1);
395
+
396
+ var iconOnly = Boolean(props.label);
397
+
398
+ var _useButtonStyles = useButtonStyles({
399
+ iconOnly: iconOnly,
400
+ size: size,
401
+ tone: tone,
402
+ prominence: prominence
403
+ }),
404
+ _useButtonStyles2 = _slicedToArray(_useButtonStyles, 2),
405
+ boxProps = _useButtonStyles2[0],
406
+ buttonStyles = _useButtonStyles2[1];
407
+
408
+ var isDisabled = disabled || loading;
409
+ var isLoading = loading && !disabled;
410
+ var variant = variants[prominence][tone];
411
+ return /*#__PURE__*/jsxRuntime.jsxs(BaseButton, _objectSpread(_objectSpread({}, boxProps), {}, {
412
+ "aria-controls": ariaControls,
413
+ "aria-describedby": ariaDescribedBy,
414
+ "aria-expanded": ariaExpanded,
415
+ "aria-label": props.label,
416
+ className: css.css(buttonStyles),
417
+ data: data,
418
+ disabled: isDisabled,
419
+ id: id,
420
+ onClick: onClick,
421
+ ref: forwardedRef,
422
+ type: type,
423
+ children: [resolveButtonChildren(_objectSpread(_objectSpread({}, props), {}, {
424
+ isLoading: isLoading,
425
+ prominence: prominence,
426
+ size: size,
427
+ tone: tone
428
+ })), isLoading && /*#__PURE__*/jsxRuntime.jsx(Loading, {
429
+ tone: variant === null || variant === void 0 ? void 0 : variant.textTone
430
+ })]
431
+ }));
432
+ });
433
+ Button.displayName = 'Button';
434
+
435
+ function Loading(_ref2) {
436
+ var tone = _ref2.tone;
437
+ return /*#__PURE__*/jsxRuntime.jsxs(box.Box, {
438
+ as: "span",
439
+ position: "absolute",
440
+ top: 0,
441
+ bottom: 0,
442
+ left: 0,
443
+ right: 0,
444
+ display: "flex",
445
+ alignItems: "center",
446
+ justifyContent: "center",
447
+ children: [/*#__PURE__*/jsxRuntime.jsx(a11y.VisuallyHidden, {
448
+ children: "button loading indicator"
449
+ }), /*#__PURE__*/jsxRuntime.jsx(spinner.Spinner, {
450
+ size: "xsmall",
451
+ tone: tone
452
+ })]
453
+ });
454
+ }
455
+
456
+ var _excluded = ["data", "href", "id", "prominence", "size", "tone"];
457
+
458
+ /** The appearance of a `Button`, with the semantics of a link. */
459
+ var ButtonLink = ts.forwardRefWithAs(function (_ref, forwardedRef) {
460
+ var data = _ref.data,
461
+ href = _ref.href,
462
+ id = _ref.id,
463
+ _ref$prominence = _ref.prominence,
464
+ prominence = _ref$prominence === void 0 ? 'high' : _ref$prominence,
465
+ _ref$size = _ref.size,
466
+ size = _ref$size === void 0 ? 'medium' : _ref$size,
467
+ _ref$tone = _ref.tone,
468
+ tone = _ref$tone === void 0 ? 'primary' : _ref$tone,
469
+ consumerProps = _objectWithoutProperties(_ref, _excluded);
470
+
471
+ var LinkComponent = link.useLinkComponent(forwardedRef);
472
+ var iconOnly = Boolean(consumerProps.label);
473
+
474
+ var _useButtonStyles = useButtonStyles({
475
+ iconOnly: iconOnly,
476
+ prominence: prominence,
477
+ size: size,
478
+ tone: tone
479
+ }),
480
+ _useButtonStyles2 = _slicedToArray(_useButtonStyles, 2),
481
+ boxProps = _useButtonStyles2[0],
482
+ buttonStyles = _useButtonStyles2[1];
483
+
484
+ return /*#__PURE__*/jsxRuntime.jsx(box.Box, _objectSpread(_objectSpread({}, boxProps), {}, {
485
+ "aria-label": consumerProps.label,
486
+ as: LinkComponent,
487
+ asElement: "a",
488
+ className: css.css(buttonStyles),
489
+ data: data,
490
+ href: href,
491
+ id: id,
492
+ ref: forwardedRef,
493
+ children: resolveButtonChildren(_objectSpread(_objectSpread({}, consumerProps), {}, {
494
+ isLoading: false,
495
+ prominence: prominence,
496
+ size: size,
497
+ tone: tone
498
+ }))
499
+ }));
500
+ });
501
+
502
+ exports.BaseButton = BaseButton;
503
+ exports.Button = Button;
504
+ exports.ButtonLink = ButtonLink;
505
+ exports.useButtonStyles = useButtonStyles;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ if (process.env.NODE_ENV === "production") {
4
+ module.exports = require("./spark-web-button.cjs.prod.js");
5
+ } else {
6
+ module.exports = require("./spark-web-button.cjs.dev.js");
7
+ }