@khanacademy/wonder-blocks-button 4.1.8 → 4.2.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.
- package/CHANGELOG.md +17 -0
- package/dist/es/index.js +201 -66
- package/dist/index.js +201 -68
- package/dist/themes/default.d.ts +112 -0
- package/dist/themes/khanmigo.d.ts +97 -0
- package/dist/themes/themed-button.d.ts +108 -0
- package/package.json +6 -6
- package/src/__tests__/__snapshots__/custom-snapshot.test.tsx.snap +180 -180
- package/src/components/button-core.tsx +126 -71
- package/src/components/button.tsx +6 -3
- package/src/themes/default.ts +133 -0
- package/src/themes/khanmigo.ts +35 -0
- package/src/themes/themed-button.tsx +42 -0
- package/tsconfig-build.json +1 -0
- package/tsconfig-build.tsbuildinfo +1 -1
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {StyleSheet} from "aphrodite";
|
|
2
|
+
import {CSSProperties, StyleSheet} from "aphrodite";
|
|
3
3
|
import {Link} from "react-router-dom";
|
|
4
4
|
import {__RouterContext} from "react-router";
|
|
5
5
|
|
|
6
6
|
import {LabelLarge, LabelSmall} from "@khanacademy/wonder-blocks-typography";
|
|
7
|
-
import Color, {
|
|
8
|
-
SemanticColor,
|
|
9
|
-
mix,
|
|
10
|
-
fade,
|
|
11
|
-
} from "@khanacademy/wonder-blocks-color";
|
|
12
7
|
import {addStyle} from "@khanacademy/wonder-blocks-core";
|
|
13
8
|
import {CircularSpinner} from "@khanacademy/wonder-blocks-progress-spinner";
|
|
14
9
|
import Icon from "@khanacademy/wonder-blocks-icon";
|
|
15
|
-
import Spacing from "@khanacademy/wonder-blocks-spacing";
|
|
16
10
|
import {isClientSideUrl} from "@khanacademy/wonder-blocks-clickable";
|
|
11
|
+
import {
|
|
12
|
+
ThemedStylesFn,
|
|
13
|
+
useScopedTheme,
|
|
14
|
+
useStyles,
|
|
15
|
+
} from "@khanacademy/wonder-blocks-theming";
|
|
17
16
|
|
|
18
17
|
import type {
|
|
19
18
|
ChildrenProps,
|
|
20
19
|
ClickableState,
|
|
21
20
|
} from "@khanacademy/wonder-blocks-clickable";
|
|
22
21
|
import type {SharedProps} from "./button";
|
|
22
|
+
import {ButtonThemeContext, ButtonThemeContract} from "../themes/themed-button";
|
|
23
23
|
|
|
24
24
|
type Props = SharedProps & ChildrenProps & ClickableState;
|
|
25
25
|
|
|
@@ -34,6 +34,9 @@ const ButtonCore: React.ForwardRefExoticComponent<
|
|
|
34
34
|
typeof Link | HTMLButtonElement | HTMLAnchorElement,
|
|
35
35
|
Props
|
|
36
36
|
>(function ButtonCore(props: Props, ref) {
|
|
37
|
+
const {theme, themeName} = useScopedTheme(ButtonThemeContext);
|
|
38
|
+
const sharedStyles = useStyles(themedSharedStyles, theme);
|
|
39
|
+
|
|
37
40
|
const renderInner = (router: any): React.ReactNode => {
|
|
38
41
|
const {
|
|
39
42
|
children,
|
|
@@ -57,18 +60,19 @@ const ButtonCore: React.ForwardRefExoticComponent<
|
|
|
57
60
|
...restProps
|
|
58
61
|
} = props;
|
|
59
62
|
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
?
|
|
63
|
-
:
|
|
64
|
-
|
|
65
|
-
const iconWidth = icon ? (size === "small" ? 16 : 24) + 8 : 0;
|
|
63
|
+
const iconWidth = icon
|
|
64
|
+
? size === "small"
|
|
65
|
+
? theme.size.width.medium
|
|
66
|
+
: theme.size.width.large
|
|
67
|
+
: 0;
|
|
66
68
|
const buttonStyles = _generateStyles(
|
|
67
|
-
|
|
69
|
+
color,
|
|
68
70
|
kind,
|
|
69
71
|
light,
|
|
70
72
|
iconWidth,
|
|
71
73
|
size,
|
|
74
|
+
theme,
|
|
75
|
+
themeName,
|
|
72
76
|
);
|
|
73
77
|
|
|
74
78
|
const disabled = spinner || disabledProp;
|
|
@@ -200,19 +204,19 @@ const ButtonCore: React.ForwardRefExoticComponent<
|
|
|
200
204
|
|
|
201
205
|
export default ButtonCore;
|
|
202
206
|
|
|
203
|
-
const
|
|
207
|
+
const themedSharedStyles: ThemedStylesFn<ButtonThemeContract> = (theme) => ({
|
|
204
208
|
shared: {
|
|
205
209
|
position: "relative",
|
|
206
210
|
display: "inline-flex",
|
|
207
211
|
alignItems: "center",
|
|
208
212
|
justifyContent: "center",
|
|
209
|
-
height:
|
|
213
|
+
height: theme.size.height.medium,
|
|
210
214
|
paddingTop: 0,
|
|
211
215
|
paddingBottom: 0,
|
|
212
|
-
paddingLeft:
|
|
213
|
-
paddingRight:
|
|
216
|
+
paddingLeft: theme.padding.large,
|
|
217
|
+
paddingRight: theme.padding.large,
|
|
214
218
|
border: "none",
|
|
215
|
-
borderRadius:
|
|
219
|
+
borderRadius: theme.border.radius.default,
|
|
216
220
|
cursor: "pointer",
|
|
217
221
|
outline: "none",
|
|
218
222
|
textDecoration: "none",
|
|
@@ -228,17 +232,18 @@ const sharedStyles = StyleSheet.create({
|
|
|
228
232
|
},
|
|
229
233
|
withIcon: {
|
|
230
234
|
// The left padding for the button with icon should have 4px less padding
|
|
231
|
-
paddingLeft:
|
|
235
|
+
paddingLeft: theme.padding.medium,
|
|
232
236
|
},
|
|
233
237
|
disabled: {
|
|
234
238
|
cursor: "auto",
|
|
235
239
|
},
|
|
236
240
|
small: {
|
|
237
|
-
|
|
241
|
+
borderRadius: theme.border.radius.small,
|
|
242
|
+
height: theme.size.height.small,
|
|
238
243
|
},
|
|
239
244
|
large: {
|
|
240
|
-
borderRadius:
|
|
241
|
-
height:
|
|
245
|
+
borderRadius: theme.border.radius.large,
|
|
246
|
+
height: theme.size.height.large,
|
|
242
247
|
},
|
|
243
248
|
text: {
|
|
244
249
|
alignItems: "center",
|
|
@@ -250,8 +255,8 @@ const sharedStyles = StyleSheet.create({
|
|
|
250
255
|
pointerEvents: "none", // fix Safari bug where the browser was eating mouse events
|
|
251
256
|
},
|
|
252
257
|
largeText: {
|
|
253
|
-
fontSize:
|
|
254
|
-
lineHeight:
|
|
258
|
+
fontSize: theme.font.size.large,
|
|
259
|
+
lineHeight: theme.font.lineHeight.large,
|
|
255
260
|
},
|
|
256
261
|
textWithFocus: {
|
|
257
262
|
position: "relative", // allows the tertiary button border to use the label width
|
|
@@ -263,36 +268,53 @@ const sharedStyles = StyleSheet.create({
|
|
|
263
268
|
position: "absolute",
|
|
264
269
|
},
|
|
265
270
|
icon: {
|
|
266
|
-
paddingRight:
|
|
271
|
+
paddingRight: theme.padding.small,
|
|
267
272
|
},
|
|
268
273
|
});
|
|
269
274
|
|
|
270
275
|
const styles: Record<string, any> = {};
|
|
271
276
|
|
|
272
277
|
const _generateStyles = (
|
|
273
|
-
|
|
278
|
+
buttonColor = "default",
|
|
274
279
|
kind: "primary" | "secondary" | "tertiary",
|
|
275
280
|
light: boolean,
|
|
276
281
|
iconWidth: number,
|
|
277
282
|
size: "large" | "medium" | "small",
|
|
283
|
+
theme: ButtonThemeContract,
|
|
284
|
+
themeName: string,
|
|
278
285
|
) => {
|
|
279
|
-
const
|
|
280
|
-
|
|
286
|
+
const color: string =
|
|
287
|
+
buttonColor === "destructive"
|
|
288
|
+
? theme.color.bg.critical.default
|
|
289
|
+
: theme.color.bg.action.default;
|
|
290
|
+
|
|
291
|
+
const buttonType = `${color}-${kind}-${light}-${iconWidth}-${size}-${themeName}`;
|
|
292
|
+
|
|
281
293
|
if (styles[buttonType]) {
|
|
282
294
|
return styles[buttonType];
|
|
283
295
|
}
|
|
284
296
|
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
297
|
+
const fadedColor =
|
|
298
|
+
buttonColor === "destructive"
|
|
299
|
+
? theme.color.bg.critical.inverse
|
|
300
|
+
: theme.color.bg.action.inverse;
|
|
301
|
+
const activeColor =
|
|
302
|
+
buttonColor === "destructive"
|
|
303
|
+
? theme.color.bg.critical.active
|
|
304
|
+
: theme.color.bg.action.active;
|
|
305
|
+
const padding =
|
|
306
|
+
size === "large" ? theme.padding.xLarge : theme.padding.large;
|
|
289
307
|
|
|
290
|
-
let newStyles: Record<string,
|
|
308
|
+
let newStyles: Record<string, CSSProperties> = {};
|
|
291
309
|
if (kind === "primary") {
|
|
310
|
+
const boxShadowInnerColor: string = light
|
|
311
|
+
? theme.color.bg.primary.inverse
|
|
312
|
+
: theme.color.bg.primary.default;
|
|
313
|
+
|
|
292
314
|
newStyles = {
|
|
293
315
|
default: {
|
|
294
|
-
background: light ?
|
|
295
|
-
color: light ? color :
|
|
316
|
+
background: light ? theme.color.bg.primary.default : color,
|
|
317
|
+
color: light ? color : theme.color.text.inverse,
|
|
296
318
|
paddingLeft: padding,
|
|
297
319
|
paddingRight: padding,
|
|
298
320
|
},
|
|
@@ -301,63 +323,88 @@ const _generateStyles = (
|
|
|
301
323
|
// a background of darkBlue for the light version. The inner
|
|
302
324
|
// box shadow/ring is also small enough for a slight variation
|
|
303
325
|
// in the background color not to matter too much.
|
|
304
|
-
boxShadow: `0 0 0 1px ${
|
|
305
|
-
light ?
|
|
326
|
+
boxShadow: `0 0 0 1px ${boxShadowInnerColor}, 0 0 0 3px ${
|
|
327
|
+
light ? theme.color.bg.primary.default : color
|
|
306
328
|
}`,
|
|
307
329
|
},
|
|
308
330
|
active: {
|
|
309
|
-
boxShadow: `0 0 0 1px ${
|
|
331
|
+
boxShadow: `0 0 0 1px ${boxShadowInnerColor}, 0 0 0 3px ${
|
|
310
332
|
light ? fadedColor : activeColor
|
|
311
333
|
}`,
|
|
312
334
|
background: light ? fadedColor : activeColor,
|
|
313
335
|
color: light ? activeColor : fadedColor,
|
|
314
336
|
},
|
|
315
337
|
disabled: {
|
|
316
|
-
background: light
|
|
317
|
-
|
|
338
|
+
background: light
|
|
339
|
+
? fadedColor
|
|
340
|
+
: theme.color.bg.primary.disabled,
|
|
341
|
+
color: light ? color : theme.color.text.primary.disabled,
|
|
318
342
|
cursor: "default",
|
|
319
343
|
":focus": {
|
|
320
344
|
boxShadow: `0 0 0 1px ${
|
|
321
|
-
light
|
|
322
|
-
|
|
345
|
+
light
|
|
346
|
+
? theme.color.bg.primary.disabled
|
|
347
|
+
: theme.color.bg.primary.default
|
|
348
|
+
}, 0 0 0 3px ${
|
|
349
|
+
light ? fadedColor : theme.color.bg.primary.disabled
|
|
350
|
+
}`,
|
|
323
351
|
},
|
|
324
352
|
},
|
|
325
353
|
};
|
|
326
354
|
} else if (kind === "secondary") {
|
|
355
|
+
const horizontalPadding = padding - (theme.border.width.focused - 1);
|
|
356
|
+
const secondaryBorderColor =
|
|
357
|
+
buttonColor === "destructive"
|
|
358
|
+
? theme.color.border.secondary.critical
|
|
359
|
+
: theme.color.border.secondary.action;
|
|
360
|
+
|
|
327
361
|
newStyles = {
|
|
328
362
|
default: {
|
|
329
|
-
background:
|
|
330
|
-
|
|
331
|
-
|
|
363
|
+
background: light
|
|
364
|
+
? theme.color.bg.secondary.inverse
|
|
365
|
+
: theme.color.bg.secondary.default,
|
|
366
|
+
color: light ? theme.color.text.inverse : color,
|
|
367
|
+
borderColor: light
|
|
368
|
+
? theme.color.border.secondary.inverse
|
|
369
|
+
: secondaryBorderColor,
|
|
332
370
|
borderStyle: "solid",
|
|
333
|
-
borderWidth:
|
|
371
|
+
borderWidth: theme.border.width.secondary,
|
|
334
372
|
paddingLeft: padding,
|
|
335
373
|
paddingRight: padding,
|
|
336
374
|
},
|
|
337
375
|
focus: {
|
|
338
|
-
background: light
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
376
|
+
background: light
|
|
377
|
+
? theme.color.bg.secondary.inverse
|
|
378
|
+
: theme.color.bg.secondary.focus,
|
|
379
|
+
borderColor: light ? theme.color.border.primary.inverse : color,
|
|
380
|
+
borderWidth: theme.border.width.focused,
|
|
381
|
+
paddingLeft: horizontalPadding,
|
|
382
|
+
paddingRight: horizontalPadding,
|
|
343
383
|
},
|
|
384
|
+
|
|
344
385
|
active: {
|
|
345
|
-
background: light
|
|
386
|
+
background: light
|
|
387
|
+
? activeColor
|
|
388
|
+
: theme.color.bg.secondary.active,
|
|
346
389
|
color: light ? fadedColor : activeColor,
|
|
347
390
|
borderColor: light ? fadedColor : activeColor,
|
|
348
|
-
borderWidth:
|
|
391
|
+
borderWidth: theme.border.width.focused,
|
|
349
392
|
// We need to reduce padding to offset the difference
|
|
350
393
|
// caused by the border becoming thicker on focus.
|
|
351
|
-
paddingLeft:
|
|
352
|
-
paddingRight:
|
|
394
|
+
paddingLeft: horizontalPadding,
|
|
395
|
+
paddingRight: horizontalPadding,
|
|
353
396
|
},
|
|
354
397
|
disabled: {
|
|
355
|
-
color: light
|
|
356
|
-
|
|
398
|
+
color: light
|
|
399
|
+
? theme.color.text.secondary.inverse
|
|
400
|
+
: theme.color.text.disabled,
|
|
401
|
+
borderColor: light ? fadedColor : theme.color.border.disabled,
|
|
357
402
|
cursor: "default",
|
|
358
403
|
":focus": {
|
|
359
|
-
borderColor: light
|
|
360
|
-
|
|
404
|
+
borderColor: light
|
|
405
|
+
? theme.color.border.secondary.inverse
|
|
406
|
+
: theme.color.border.disabled,
|
|
407
|
+
borderWidth: theme.border.width.disabled,
|
|
361
408
|
// We need to reduce padding to offset the difference
|
|
362
409
|
// caused by the border becoming thicker on focus.
|
|
363
410
|
paddingLeft: padding - 1,
|
|
@@ -369,7 +416,7 @@ const _generateStyles = (
|
|
|
369
416
|
newStyles = {
|
|
370
417
|
default: {
|
|
371
418
|
background: "none",
|
|
372
|
-
color: light ?
|
|
419
|
+
color: light ? theme.color.text.inverse : color,
|
|
373
420
|
paddingLeft: 0,
|
|
374
421
|
paddingRight: 0,
|
|
375
422
|
},
|
|
@@ -377,12 +424,12 @@ const _generateStyles = (
|
|
|
377
424
|
":after": {
|
|
378
425
|
content: "''",
|
|
379
426
|
position: "absolute",
|
|
380
|
-
height:
|
|
427
|
+
height: theme.size.height.tertiaryHover,
|
|
381
428
|
width: "100%",
|
|
382
429
|
right: 0,
|
|
383
430
|
bottom: 0,
|
|
384
|
-
background: light ?
|
|
385
|
-
borderRadius:
|
|
431
|
+
background: light ? theme.color.bg.tertiary.hover : color,
|
|
432
|
+
borderRadius: theme.border.radius.tertiary,
|
|
386
433
|
},
|
|
387
434
|
},
|
|
388
435
|
focus: {
|
|
@@ -392,12 +439,18 @@ const _generateStyles = (
|
|
|
392
439
|
// calculate the width/height and use absolute position to
|
|
393
440
|
// prevent other elements from being shifted around.
|
|
394
441
|
position: "absolute",
|
|
395
|
-
|
|
396
|
-
|
|
442
|
+
// Keeps the button at the same size when applying the
|
|
443
|
+
// borderWidth property, so we can apply the correct value
|
|
444
|
+
// per theme for each side (left and right).
|
|
445
|
+
width: `calc(100% + ${theme.border.width.focused * 2}px)`,
|
|
446
|
+
// Same as above, but for the height (top and bottom).
|
|
447
|
+
height: `calc(100% - ${theme.border.width.focused * 2}px)`,
|
|
397
448
|
borderStyle: "solid",
|
|
398
|
-
borderColor: light
|
|
399
|
-
|
|
400
|
-
|
|
449
|
+
borderColor: light
|
|
450
|
+
? theme.color.border.tertiary.inverse
|
|
451
|
+
: color,
|
|
452
|
+
borderWidth: theme.border.width.focused,
|
|
453
|
+
borderRadius: theme.border.radius.default,
|
|
401
454
|
},
|
|
402
455
|
},
|
|
403
456
|
active: {
|
|
@@ -408,12 +461,14 @@ const _generateStyles = (
|
|
|
408
461
|
},
|
|
409
462
|
},
|
|
410
463
|
disabled: {
|
|
411
|
-
color: light ? fadedColor :
|
|
464
|
+
color: light ? fadedColor : theme.color.text.disabled,
|
|
412
465
|
cursor: "default",
|
|
413
466
|
},
|
|
414
467
|
disabledFocus: {
|
|
415
468
|
":after": {
|
|
416
|
-
borderColor: light
|
|
469
|
+
borderColor: light
|
|
470
|
+
? theme.color.border.tertiary.inverse
|
|
471
|
+
: theme.color.border.disabled,
|
|
417
472
|
},
|
|
418
473
|
},
|
|
419
474
|
};
|
|
@@ -10,6 +10,7 @@ import type {AriaProps, StyleType} from "@khanacademy/wonder-blocks-core";
|
|
|
10
10
|
import type {IconAsset} from "@khanacademy/wonder-blocks-icon";
|
|
11
11
|
import {Link} from "react-router-dom";
|
|
12
12
|
import ButtonCore from "./button-core";
|
|
13
|
+
import ThemedButton from "../themes/themed-button";
|
|
13
14
|
|
|
14
15
|
export type SharedProps =
|
|
15
16
|
/**
|
|
@@ -287,9 +288,11 @@ const Button: React.ForwardRefExoticComponent<
|
|
|
287
288
|
};
|
|
288
289
|
|
|
289
290
|
return (
|
|
290
|
-
<
|
|
291
|
-
|
|
292
|
-
|
|
291
|
+
<ThemedButton>
|
|
292
|
+
<__RouterContext.Consumer>
|
|
293
|
+
{(router) => renderClickableBehavior(router)}
|
|
294
|
+
</__RouterContext.Consumer>
|
|
295
|
+
</ThemedButton>
|
|
293
296
|
);
|
|
294
297
|
});
|
|
295
298
|
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import {tokens} from "@khanacademy/wonder-blocks-theming";
|
|
2
|
+
|
|
3
|
+
const theme = {
|
|
4
|
+
color: {
|
|
5
|
+
bg: {
|
|
6
|
+
/**
|
|
7
|
+
* Color
|
|
8
|
+
*/
|
|
9
|
+
// color="default"
|
|
10
|
+
action: {
|
|
11
|
+
default: tokens.color.blue,
|
|
12
|
+
active: tokens.color.activeBlue,
|
|
13
|
+
inverse: tokens.color.fadedBlue,
|
|
14
|
+
},
|
|
15
|
+
// color="destructive"
|
|
16
|
+
critical: {
|
|
17
|
+
default: tokens.color.red,
|
|
18
|
+
active: tokens.color.activeRed,
|
|
19
|
+
inverse: tokens.color.fadedRed,
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Kind
|
|
24
|
+
*/
|
|
25
|
+
primary: {
|
|
26
|
+
default: tokens.color.white,
|
|
27
|
+
disabled: tokens.color.offBlack32,
|
|
28
|
+
// used in boxShadow
|
|
29
|
+
inverse: tokens.color.darkBlue,
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
secondary: {
|
|
33
|
+
default: "none",
|
|
34
|
+
inverse: "none",
|
|
35
|
+
focus: tokens.color.white,
|
|
36
|
+
active: tokens.color.fadedBlue,
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
tertiary: {
|
|
40
|
+
hover: tokens.color.white,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
text: {
|
|
44
|
+
/**
|
|
45
|
+
* Default
|
|
46
|
+
*/
|
|
47
|
+
// kind="secondary, tertiary", disabled=true, light=false
|
|
48
|
+
disabled: tokens.color.offBlack32,
|
|
49
|
+
// kind="primary", light=false | kind="secondary, tertiary", light=true
|
|
50
|
+
inverse: tokens.color.white,
|
|
51
|
+
/**
|
|
52
|
+
* Kind
|
|
53
|
+
*/
|
|
54
|
+
primary: {
|
|
55
|
+
disabled: tokens.color.white64,
|
|
56
|
+
},
|
|
57
|
+
secondary: {
|
|
58
|
+
inverse: tokens.color.white50,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
border: {
|
|
62
|
+
/**
|
|
63
|
+
* Default
|
|
64
|
+
*/
|
|
65
|
+
// kind="secondary", light=false | kind="tertiary", light=false
|
|
66
|
+
disabled: tokens.color.offBlack32,
|
|
67
|
+
/**
|
|
68
|
+
* Kind
|
|
69
|
+
*/
|
|
70
|
+
primary: {
|
|
71
|
+
inverse: tokens.color.white,
|
|
72
|
+
},
|
|
73
|
+
secondary: {
|
|
74
|
+
action: tokens.color.offBlack50,
|
|
75
|
+
critical: tokens.color.offBlack50,
|
|
76
|
+
inverse: tokens.color.white50,
|
|
77
|
+
},
|
|
78
|
+
tertiary: {
|
|
79
|
+
inverse: tokens.color.white,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
border: {
|
|
84
|
+
width: {
|
|
85
|
+
// secondary (resting)
|
|
86
|
+
secondary: tokens.border.width.hairline,
|
|
87
|
+
// secondary (resting, focus, active), tertiary (focus)
|
|
88
|
+
focused: tokens.border.width.thin,
|
|
89
|
+
// secondary (disabled)
|
|
90
|
+
disabled: tokens.border.width.thin,
|
|
91
|
+
},
|
|
92
|
+
radius: {
|
|
93
|
+
// default
|
|
94
|
+
default: tokens.border.radius.small_3,
|
|
95
|
+
// tertiary
|
|
96
|
+
tertiary: tokens.border.radius.xSmall_2,
|
|
97
|
+
// small button
|
|
98
|
+
small: tokens.border.radius.small_3,
|
|
99
|
+
// large button
|
|
100
|
+
large: tokens.border.radius.large_6,
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
size: {
|
|
104
|
+
width: {
|
|
105
|
+
medium: tokens.spacing.large_24,
|
|
106
|
+
large: tokens.spacing.xLarge_32,
|
|
107
|
+
},
|
|
108
|
+
height: {
|
|
109
|
+
tertiaryHover: tokens.spacing.xxxxSmall_2,
|
|
110
|
+
small: tokens.spacing.xLarge_32,
|
|
111
|
+
// NOTE: These height tokens are specific to this component.
|
|
112
|
+
medium: 40,
|
|
113
|
+
large: 56,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
padding: {
|
|
117
|
+
small: tokens.spacing.xSmall_8,
|
|
118
|
+
medium: tokens.spacing.small_12,
|
|
119
|
+
large: tokens.spacing.medium_16,
|
|
120
|
+
xLarge: tokens.spacing.xLarge_32,
|
|
121
|
+
},
|
|
122
|
+
font: {
|
|
123
|
+
size: {
|
|
124
|
+
// NOTE: This token is specific to this button size.
|
|
125
|
+
large: 18,
|
|
126
|
+
},
|
|
127
|
+
lineHeight: {
|
|
128
|
+
large: tokens.font.lineHeight.medium,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export default theme;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {mergeTheme, tokens} from "@khanacademy/wonder-blocks-theming";
|
|
2
|
+
import defaultTheme from "./default";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The overrides for the Khanmigo theme.
|
|
6
|
+
*/
|
|
7
|
+
const theme = mergeTheme(defaultTheme, {
|
|
8
|
+
color: {
|
|
9
|
+
bg: {
|
|
10
|
+
secondary: {
|
|
11
|
+
default: tokens.color.offWhite,
|
|
12
|
+
active: tokens.color.fadedBlue8,
|
|
13
|
+
focus: tokens.color.offWhite,
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
border: {
|
|
17
|
+
secondary: {
|
|
18
|
+
action: tokens.color.fadedBlue,
|
|
19
|
+
critical: tokens.color.fadedRed,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
border: {
|
|
24
|
+
radius: {
|
|
25
|
+
default: tokens.border.radius.xLarge_12,
|
|
26
|
+
small: tokens.border.radius.large_6,
|
|
27
|
+
large: tokens.border.radius.xLarge_12,
|
|
28
|
+
},
|
|
29
|
+
width: {
|
|
30
|
+
focused: tokens.border.width.hairline,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export default theme;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import {
|
|
3
|
+
createThemeContext,
|
|
4
|
+
ThemeSwitcherContext,
|
|
5
|
+
} from "@khanacademy/wonder-blocks-theming";
|
|
6
|
+
|
|
7
|
+
import defaultTheme from "./default";
|
|
8
|
+
import khanmigoTheme from "./khanmigo";
|
|
9
|
+
|
|
10
|
+
type Props = {
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The themes available to the Button component.
|
|
16
|
+
*/
|
|
17
|
+
const themes = {
|
|
18
|
+
default: defaultTheme,
|
|
19
|
+
khanmigo: khanmigoTheme,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type ButtonThemeContract = typeof defaultTheme;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The context that provides the theme to the Button component.
|
|
26
|
+
* This is generally consumed via the `useScopedTheme` hook.
|
|
27
|
+
*/
|
|
28
|
+
export const ButtonThemeContext = createThemeContext(defaultTheme);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* ThemedButton is a component that provides a theme to the <Button/> component.
|
|
32
|
+
*/
|
|
33
|
+
export default function ThemedButton(props: Props) {
|
|
34
|
+
const currentTheme = React.useContext(ThemeSwitcherContext);
|
|
35
|
+
|
|
36
|
+
const theme = themes[currentTheme as keyof typeof themes] || defaultTheme;
|
|
37
|
+
return (
|
|
38
|
+
<ButtonThemeContext.Provider value={theme}>
|
|
39
|
+
{props.children}
|
|
40
|
+
</ButtonThemeContext.Provider>
|
|
41
|
+
);
|
|
42
|
+
}
|
package/tsconfig-build.json
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
{"path": "../wonder-blocks-icon/tsconfig-build.json"},
|
|
13
13
|
{"path": "../wonder-blocks-progress-spinner/tsconfig-build.json"},
|
|
14
14
|
{"path": "../wonder-blocks-spacing/tsconfig-build.json"},
|
|
15
|
+
{"path": "../wonder-blocks-theming/tsconfig-build.json"},
|
|
15
16
|
{"path": "../wonder-blocks-typography/tsconfig-build.json"},
|
|
16
17
|
]
|
|
17
18
|
}
|