@khanacademy/wonder-blocks-button 4.1.9 → 4.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @khanacademy/wonder-blocks-button
2
2
 
3
+ ## 4.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - a194f5e2: - Fix default borderRadius value.
8
+ - Fix secondary pressed background color (to support action and destructive buttons).
9
+ - Change fontWeight for khanmigo theme (bold -> regular).
10
+
11
+ ## 4.2.0
12
+
13
+ ### Minor Changes
14
+
15
+ - 5725703a: Add theming support to the Button component
16
+
3
17
  ## 4.1.9
4
18
 
5
19
  ### Patch Changes
package/dist/es/index.js CHANGED
@@ -4,11 +4,10 @@ import { isClientSideUrl, getClickableBehavior } from '@khanacademy/wonder-block
4
4
  import { StyleSheet } from 'aphrodite';
5
5
  import { Link } from 'react-router-dom';
6
6
  import { LabelSmall, LabelLarge } from '@khanacademy/wonder-blocks-typography';
7
- import Color, { SemanticColor, mix, fade } from '@khanacademy/wonder-blocks-color';
8
7
  import { addStyle } from '@khanacademy/wonder-blocks-core';
9
8
  import { CircularSpinner } from '@khanacademy/wonder-blocks-progress-spinner';
10
9
  import Icon from '@khanacademy/wonder-blocks-icon';
11
- import Spacing from '@khanacademy/wonder-blocks-spacing';
10
+ import { tokens, mergeTheme, createThemeContext, ThemeSwitcherContext, useScopedTheme, useStyles } from '@khanacademy/wonder-blocks-theming';
12
11
 
13
12
  function _extends() {
14
13
  _extends = Object.assign ? Object.assign.bind() : function (target) {
@@ -38,11 +37,165 @@ function _objectWithoutPropertiesLoose(source, excluded) {
38
37
  return target;
39
38
  }
40
39
 
40
+ const theme$1 = {
41
+ color: {
42
+ bg: {
43
+ action: {
44
+ default: tokens.color.blue,
45
+ active: tokens.color.activeBlue,
46
+ inverse: tokens.color.fadedBlue
47
+ },
48
+ critical: {
49
+ default: tokens.color.red,
50
+ active: tokens.color.activeRed,
51
+ inverse: tokens.color.fadedRed
52
+ },
53
+ primary: {
54
+ default: tokens.color.white,
55
+ disabled: tokens.color.offBlack32,
56
+ inverse: tokens.color.darkBlue
57
+ },
58
+ secondary: {
59
+ default: "none",
60
+ inverse: "none",
61
+ focus: tokens.color.white,
62
+ active: {
63
+ action: tokens.color.fadedBlue,
64
+ critical: tokens.color.fadedRed
65
+ }
66
+ },
67
+ tertiary: {
68
+ hover: tokens.color.white
69
+ }
70
+ },
71
+ text: {
72
+ disabled: tokens.color.offBlack32,
73
+ inverse: tokens.color.white,
74
+ primary: {
75
+ disabled: tokens.color.white64
76
+ },
77
+ secondary: {
78
+ inverse: tokens.color.white50
79
+ }
80
+ },
81
+ border: {
82
+ disabled: tokens.color.offBlack32,
83
+ primary: {
84
+ inverse: tokens.color.white
85
+ },
86
+ secondary: {
87
+ action: tokens.color.offBlack50,
88
+ critical: tokens.color.offBlack50,
89
+ inverse: tokens.color.white50
90
+ },
91
+ tertiary: {
92
+ inverse: tokens.color.white
93
+ }
94
+ }
95
+ },
96
+ border: {
97
+ width: {
98
+ secondary: tokens.border.width.hairline,
99
+ focused: tokens.border.width.thin,
100
+ disabled: tokens.border.width.thin
101
+ },
102
+ radius: {
103
+ default: tokens.border.radius.medium_4,
104
+ tertiary: tokens.border.radius.xSmall_2,
105
+ small: tokens.border.radius.small_3,
106
+ large: tokens.border.radius.large_6
107
+ }
108
+ },
109
+ size: {
110
+ width: {
111
+ medium: tokens.spacing.large_24,
112
+ large: tokens.spacing.xLarge_32
113
+ },
114
+ height: {
115
+ tertiaryHover: tokens.spacing.xxxxSmall_2,
116
+ small: tokens.spacing.xLarge_32,
117
+ medium: 40,
118
+ large: 56
119
+ }
120
+ },
121
+ padding: {
122
+ small: tokens.spacing.xSmall_8,
123
+ medium: tokens.spacing.small_12,
124
+ large: tokens.spacing.medium_16,
125
+ xLarge: tokens.spacing.xLarge_32
126
+ },
127
+ font: {
128
+ size: {
129
+ large: 18
130
+ },
131
+ lineHeight: {
132
+ large: tokens.font.lineHeight.medium
133
+ },
134
+ weight: {
135
+ default: tokens.font.weight.bold
136
+ }
137
+ }
138
+ };
139
+
140
+ const theme = mergeTheme(theme$1, {
141
+ color: {
142
+ bg: {
143
+ secondary: {
144
+ default: tokens.color.offWhite,
145
+ active: {
146
+ action: tokens.color.fadedBlue8,
147
+ critical: tokens.color.fadedRed8
148
+ },
149
+ focus: tokens.color.offWhite
150
+ }
151
+ },
152
+ border: {
153
+ secondary: {
154
+ action: tokens.color.fadedBlue,
155
+ critical: tokens.color.fadedRed
156
+ }
157
+ }
158
+ },
159
+ border: {
160
+ radius: {
161
+ default: tokens.border.radius.xLarge_12,
162
+ small: tokens.border.radius.large_6,
163
+ large: tokens.border.radius.xLarge_12
164
+ },
165
+ width: {
166
+ focused: tokens.border.width.hairline
167
+ }
168
+ },
169
+ font: {
170
+ weight: {
171
+ default: tokens.font.weight.regular
172
+ }
173
+ }
174
+ });
175
+
176
+ const themes = {
177
+ default: theme$1,
178
+ khanmigo: theme
179
+ };
180
+ const ButtonThemeContext = createThemeContext(theme$1);
181
+ function ThemedButton(props) {
182
+ const currentTheme = React.useContext(ThemeSwitcherContext);
183
+ const theme = themes[currentTheme] || theme$1;
184
+ return React.createElement(ButtonThemeContext.Provider, {
185
+ value: theme
186
+ }, props.children);
187
+ }
188
+
41
189
  const _excluded$1 = ["children", "skipClientNav", "color", "disabled", "focused", "hovered", "href", "kind", "light", "pressed", "size", "style", "testId", "type", "spinner", "icon", "id", "waiting"];
42
190
  const StyledAnchor = addStyle("a");
43
191
  const StyledButton = addStyle("button");
44
192
  const StyledLink = addStyle(Link);
45
193
  const ButtonCore = React.forwardRef(function ButtonCore(props, ref) {
194
+ const {
195
+ theme,
196
+ themeName
197
+ } = useScopedTheme(ButtonThemeContext);
198
+ const sharedStyles = useStyles(themedSharedStyles, theme);
46
199
  const renderInner = router => {
47
200
  const {
48
201
  children,
@@ -64,9 +217,8 @@ const ButtonCore = React.forwardRef(function ButtonCore(props, ref) {
64
217
  id
65
218
  } = props,
66
219
  restProps = _objectWithoutPropertiesLoose(props, _excluded$1);
67
- const buttonColor = color === "destructive" ? SemanticColor.controlDestructive : SemanticColor.controlDefault;
68
- const iconWidth = icon ? (size === "small" ? 16 : 24) + 8 : 0;
69
- const buttonStyles = _generateStyles(buttonColor, kind, light, iconWidth, size);
220
+ const iconWidth = icon ? size === "small" ? theme.size.width.medium : theme.size.width.large : 0;
221
+ const buttonStyles = _generateStyles(color, kind, light, iconWidth, size, theme, themeName);
70
222
  const disabled = spinner || disabledProp;
71
223
  const defaultStyle = [sharedStyles.shared, disabled && sharedStyles.disabled, icon && sharedStyles.withIcon, buttonStyles.default, disabled && buttonStyles.disabled, kind !== "tertiary" && !disabled && (pressed ? buttonStyles.active : (hovered || focused) && buttonStyles.focus), kind === "tertiary" && !pressed && focused && [buttonStyles.focus, disabled && buttonStyles.disabledFocus], size === "small" && sharedStyles.small, size === "large" && sharedStyles.large];
72
224
  const commonProps = _extends({
@@ -118,19 +270,19 @@ const ButtonCore = React.forwardRef(function ButtonCore(props, ref) {
118
270
  };
119
271
  return React.createElement(__RouterContext.Consumer, null, router => renderInner(router));
120
272
  });
121
- const sharedStyles = StyleSheet.create({
273
+ const themedSharedStyles = theme => ({
122
274
  shared: {
123
275
  position: "relative",
124
276
  display: "inline-flex",
125
277
  alignItems: "center",
126
278
  justifyContent: "center",
127
- height: 40,
279
+ height: theme.size.height.medium,
128
280
  paddingTop: 0,
129
281
  paddingBottom: 0,
130
- paddingLeft: 16,
131
- paddingRight: 16,
282
+ paddingLeft: theme.padding.large,
283
+ paddingRight: theme.padding.large,
132
284
  border: "none",
133
- borderRadius: 4,
285
+ borderRadius: theme.border.radius.default,
134
286
  cursor: "pointer",
135
287
  outline: "none",
136
288
  textDecoration: "none",
@@ -142,21 +294,22 @@ const sharedStyles = StyleSheet.create({
142
294
  }
143
295
  },
144
296
  withIcon: {
145
- paddingLeft: 12
297
+ paddingLeft: theme.padding.medium
146
298
  },
147
299
  disabled: {
148
300
  cursor: "auto"
149
301
  },
150
302
  small: {
151
- height: 32
303
+ borderRadius: theme.border.radius.small,
304
+ height: theme.size.height.small
152
305
  },
153
306
  large: {
154
- borderRadius: Spacing.xxSmall_6,
155
- height: 56
307
+ borderRadius: theme.border.radius.large,
308
+ height: theme.size.height.large
156
309
  },
157
310
  text: {
158
311
  alignItems: "center",
159
- fontWeight: "bold",
312
+ fontWeight: theme.font.weight.default,
160
313
  whiteSpace: "nowrap",
161
314
  overflow: "hidden",
162
315
  textOverflow: "ellipsis",
@@ -164,8 +317,8 @@ const sharedStyles = StyleSheet.create({
164
317
  pointerEvents: "none"
165
318
  },
166
319
  largeText: {
167
- fontSize: 18,
168
- lineHeight: "20px"
320
+ fontSize: theme.font.size.large,
321
+ lineHeight: theme.font.lineHeight.large
169
322
  },
170
323
  textWithFocus: {
171
324
  position: "relative"
@@ -177,85 +330,82 @@ const sharedStyles = StyleSheet.create({
177
330
  position: "absolute"
178
331
  },
179
332
  icon: {
180
- paddingRight: Spacing.xSmall_8
333
+ paddingRight: theme.padding.small
181
334
  }
182
335
  });
183
336
  const styles = {};
184
- const _generateStyles = (color, kind, light, iconWidth, size) => {
185
- const buttonType = color + kind + light.toString() + iconWidth.toString() + size;
337
+ const _generateStyles = (buttonColor = "default", kind, light, iconWidth, size, theme, themeName) => {
338
+ const color = buttonColor === "destructive" ? theme.color.bg.critical.default : theme.color.bg.action.default;
339
+ const buttonType = `${color}-${kind}-${light}-${iconWidth}-${size}-${themeName}`;
186
340
  if (styles[buttonType]) {
187
341
  return styles[buttonType];
188
342
  }
189
- const {
190
- white,
191
- white50,
192
- white64,
193
- offBlack32,
194
- offBlack50,
195
- darkBlue
196
- } = Color;
197
- const fadedColor = mix(fade(color, 0.32), white);
198
- const activeColor = mix(offBlack32, color);
199
- const padding = size === "large" ? Spacing.xLarge_32 : Spacing.medium_16;
343
+ const fadedColor = buttonColor === "destructive" ? theme.color.bg.critical.inverse : theme.color.bg.action.inverse;
344
+ const activeColor = buttonColor === "destructive" ? theme.color.bg.critical.active : theme.color.bg.action.active;
345
+ const padding = size === "large" ? theme.padding.xLarge : theme.padding.large;
200
346
  let newStyles = {};
201
347
  if (kind === "primary") {
348
+ const boxShadowInnerColor = light ? theme.color.bg.primary.inverse : theme.color.bg.primary.default;
202
349
  newStyles = {
203
350
  default: {
204
- background: light ? white : color,
205
- color: light ? color : white,
351
+ background: light ? theme.color.bg.primary.default : color,
352
+ color: light ? color : theme.color.text.inverse,
206
353
  paddingLeft: padding,
207
354
  paddingRight: padding
208
355
  },
209
356
  focus: {
210
- boxShadow: `0 0 0 1px ${light ? darkBlue : white}, 0 0 0 3px ${light ? white : color}`
357
+ boxShadow: `0 0 0 1px ${boxShadowInnerColor}, 0 0 0 3px ${light ? theme.color.bg.primary.default : color}`
211
358
  },
212
359
  active: {
213
- boxShadow: `0 0 0 1px ${light ? darkBlue : white}, 0 0 0 3px ${light ? fadedColor : activeColor}`,
360
+ boxShadow: `0 0 0 1px ${boxShadowInnerColor}, 0 0 0 3px ${light ? fadedColor : activeColor}`,
214
361
  background: light ? fadedColor : activeColor,
215
362
  color: light ? activeColor : fadedColor
216
363
  },
217
364
  disabled: {
218
- background: light ? fadedColor : offBlack32,
219
- color: light ? color : white64,
365
+ background: light ? fadedColor : theme.color.bg.primary.disabled,
366
+ color: light ? color : theme.color.text.primary.disabled,
220
367
  cursor: "default",
221
368
  ":focus": {
222
- boxShadow: `0 0 0 1px ${light ? offBlack32 : white}, 0 0 0 3px ${light ? fadedColor : offBlack32}`
369
+ boxShadow: `0 0 0 1px ${light ? theme.color.bg.primary.disabled : theme.color.bg.primary.default}, 0 0 0 3px ${light ? fadedColor : theme.color.bg.primary.disabled}`
223
370
  }
224
371
  }
225
372
  };
226
373
  } else if (kind === "secondary") {
374
+ const horizontalPadding = padding - (theme.border.width.focused - 1);
375
+ const secondaryBorderColor = buttonColor === "destructive" ? theme.color.border.secondary.critical : theme.color.border.secondary.action;
376
+ const secondaryActiveColor = buttonColor === "destructive" ? theme.color.bg.secondary.active.critical : theme.color.bg.secondary.active.action;
227
377
  newStyles = {
228
378
  default: {
229
- background: "none",
230
- color: light ? white : color,
231
- borderColor: light ? white50 : offBlack50,
379
+ background: light ? theme.color.bg.secondary.inverse : theme.color.bg.secondary.default,
380
+ color: light ? theme.color.text.inverse : color,
381
+ borderColor: light ? theme.color.border.secondary.inverse : secondaryBorderColor,
232
382
  borderStyle: "solid",
233
- borderWidth: 1,
383
+ borderWidth: theme.border.width.secondary,
234
384
  paddingLeft: padding,
235
385
  paddingRight: padding
236
386
  },
237
387
  focus: {
238
- background: light ? "transparent" : white,
239
- borderColor: light ? white : color,
240
- borderWidth: 2,
241
- paddingLeft: padding - 1,
242
- paddingRight: padding - 1
388
+ background: light ? theme.color.bg.secondary.inverse : theme.color.bg.secondary.focus,
389
+ borderColor: light ? theme.color.border.primary.inverse : color,
390
+ borderWidth: theme.border.width.focused,
391
+ paddingLeft: horizontalPadding,
392
+ paddingRight: horizontalPadding
243
393
  },
244
394
  active: {
245
- background: light ? activeColor : fadedColor,
395
+ background: light ? activeColor : secondaryActiveColor,
246
396
  color: light ? fadedColor : activeColor,
247
397
  borderColor: light ? fadedColor : activeColor,
248
- borderWidth: 2,
249
- paddingLeft: padding - 1,
250
- paddingRight: padding - 1
398
+ borderWidth: theme.border.width.focused,
399
+ paddingLeft: horizontalPadding,
400
+ paddingRight: horizontalPadding
251
401
  },
252
402
  disabled: {
253
- color: light ? white50 : offBlack32,
254
- borderColor: light ? fadedColor : offBlack32,
403
+ color: light ? theme.color.text.secondary.inverse : theme.color.text.disabled,
404
+ borderColor: light ? fadedColor : theme.color.border.disabled,
255
405
  cursor: "default",
256
406
  ":focus": {
257
- borderColor: light ? white50 : offBlack32,
258
- borderWidth: 2,
407
+ borderColor: light ? theme.color.border.secondary.inverse : theme.color.border.disabled,
408
+ borderWidth: theme.border.width.disabled,
259
409
  paddingLeft: padding - 1,
260
410
  paddingRight: padding - 1
261
411
  }
@@ -265,7 +415,7 @@ const _generateStyles = (color, kind, light, iconWidth, size) => {
265
415
  newStyles = {
266
416
  default: {
267
417
  background: "none",
268
- color: light ? white : color,
418
+ color: light ? theme.color.text.inverse : color,
269
419
  paddingLeft: 0,
270
420
  paddingRight: 0
271
421
  },
@@ -273,24 +423,24 @@ const _generateStyles = (color, kind, light, iconWidth, size) => {
273
423
  ":after": {
274
424
  content: "''",
275
425
  position: "absolute",
276
- height: 2,
426
+ height: theme.size.height.tertiaryHover,
277
427
  width: "100%",
278
428
  right: 0,
279
429
  bottom: 0,
280
- background: light ? white : color,
281
- borderRadius: 2
430
+ background: light ? theme.color.bg.tertiary.hover : color,
431
+ borderRadius: theme.border.radius.tertiary
282
432
  }
283
433
  },
284
434
  focus: {
285
435
  ":after": {
286
436
  content: "''",
287
437
  position: "absolute",
288
- width: `calc(100% + ${Spacing.xxxSmall_4}px)`,
289
- height: `calc(100% - ${Spacing.xxxSmall_4}px)`,
438
+ width: `calc(100% + ${theme.border.width.focused * 2}px)`,
439
+ height: `calc(100% - ${theme.border.width.focused * 2}px)`,
290
440
  borderStyle: "solid",
291
- borderColor: light ? white : color,
292
- borderWidth: Spacing.xxxxSmall_2,
293
- borderRadius: Spacing.xxxSmall_4
441
+ borderColor: light ? theme.color.border.tertiary.inverse : color,
442
+ borderWidth: theme.border.width.focused,
443
+ borderRadius: theme.border.radius.default
294
444
  }
295
445
  },
296
446
  active: {
@@ -301,12 +451,12 @@ const _generateStyles = (color, kind, light, iconWidth, size) => {
301
451
  }
302
452
  },
303
453
  disabled: {
304
- color: light ? fadedColor : offBlack32,
454
+ color: light ? fadedColor : theme.color.text.disabled,
305
455
  cursor: "default"
306
456
  },
307
457
  disabledFocus: {
308
458
  ":after": {
309
- borderColor: light ? white50 : offBlack32
459
+ borderColor: light ? theme.color.border.tertiary.inverse : theme.color.border.disabled
310
460
  }
311
461
  }
312
462
  };
@@ -380,7 +530,7 @@ const Button = React.forwardRef(function Button(props, ref) {
380
530
  }, renderProp);
381
531
  }
382
532
  };
383
- return React.createElement(__RouterContext.Consumer, null, router => renderClickableBehavior(router));
533
+ return React.createElement(ThemedButton, null, React.createElement(__RouterContext.Consumer, null, router => renderClickableBehavior(router)));
384
534
  });
385
535
 
386
536
  export { Button as default };