@khanacademy/wonder-blocks-switch 1.0.6 → 1.1.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 +18 -0
- package/dist/components/switch.d.ts +1 -1
- package/dist/es/index.js +176 -81
- package/dist/index.js +175 -84
- package/dist/themes/default.d.ts +62 -0
- package/dist/themes/khanmigo.d.ts +65 -0
- package/dist/themes/themed-switch.d.ts +80 -0
- package/package.json +2 -3
- package/src/components/__tests__/switch.test.tsx +72 -14
- package/src/components/switch.tsx +123 -99
- package/src/themes/default.ts +73 -0
- package/src/themes/khanmigo.ts +28 -0
- package/src/themes/themed-switch.tsx +42 -0
- package/tsconfig-build.json +1 -0
- package/tsconfig-build.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-switch
|
|
2
2
|
|
|
3
|
+
## 1.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 1f263385: Use `aria-disabled` instead of `disabled` for enabling focus on Screen Readers.
|
|
8
|
+
|
|
9
|
+
## 1.1.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- fc6cec0b: Add theming to Switch and new color tokens for theming
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies [5725703a]
|
|
18
|
+
- Updated dependencies [fc6cec0b]
|
|
19
|
+
- @khanacademy/wonder-blocks-theming@1.1.0
|
|
20
|
+
|
|
3
21
|
## 1.0.6
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import { AriaProps } from "@khanacademy/wonder-blocks-core";
|
|
3
3
|
declare const Switch: React.ForwardRefExoticComponent<Pick<AriaProps, "aria-describedby" | "aria-label" | "aria-labelledby"> & {
|
|
4
4
|
/**
|
|
5
|
-
* Whether this
|
|
5
|
+
* Whether this component is checked.
|
|
6
6
|
*/
|
|
7
7
|
checked: boolean;
|
|
8
8
|
/**
|
package/dist/es/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { StyleSheet } from 'aphrodite';
|
|
3
|
-
import { addStyle,
|
|
4
|
-
import
|
|
5
|
-
import Spacing from '@khanacademy/wonder-blocks-spacing';
|
|
3
|
+
import { addStyle, useUniqueIdWithMock, View } from '@khanacademy/wonder-blocks-core';
|
|
4
|
+
import { tokens, mergeTheme, createThemeContext, ThemeSwitcherContext, useScopedTheme, useStyles } from '@khanacademy/wonder-blocks-theming';
|
|
6
5
|
|
|
7
6
|
function _extends() {
|
|
8
7
|
_extends = Object.assign ? Object.assign.bind() : function (target) {
|
|
@@ -19,9 +18,105 @@ function _extends() {
|
|
|
19
18
|
return _extends.apply(this, arguments);
|
|
20
19
|
}
|
|
21
20
|
|
|
21
|
+
const theme$1 = {
|
|
22
|
+
color: {
|
|
23
|
+
bg: {
|
|
24
|
+
switch: {
|
|
25
|
+
off: tokens.color.offBlack50,
|
|
26
|
+
disabledOff: tokens.color.offBlack32,
|
|
27
|
+
activeOff: tokens.color.offBlack64,
|
|
28
|
+
on: tokens.color.blue,
|
|
29
|
+
disabledOn: tokens.color.fadedBlue,
|
|
30
|
+
activeOn: tokens.color.activeBlue
|
|
31
|
+
},
|
|
32
|
+
slider: {
|
|
33
|
+
on: tokens.color.white,
|
|
34
|
+
off: tokens.color.white
|
|
35
|
+
},
|
|
36
|
+
icon: {
|
|
37
|
+
on: tokens.color.blue,
|
|
38
|
+
disabledOn: tokens.color.fadedBlue,
|
|
39
|
+
off: tokens.color.offBlack50,
|
|
40
|
+
disabledOff: tokens.color.offBlack32
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
outline: {
|
|
44
|
+
default: tokens.color.blue
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
border: {
|
|
48
|
+
radius: {
|
|
49
|
+
small: tokens.spacing.small_12,
|
|
50
|
+
full: tokens.border.radius.full
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
size: {
|
|
54
|
+
height: {
|
|
55
|
+
none: 0,
|
|
56
|
+
medium: 20,
|
|
57
|
+
large: tokens.spacing.large_24
|
|
58
|
+
},
|
|
59
|
+
width: {
|
|
60
|
+
none: 0,
|
|
61
|
+
small: tokens.spacing.xxxxSmall_2,
|
|
62
|
+
medium: 20,
|
|
63
|
+
large: 40
|
|
64
|
+
},
|
|
65
|
+
offset: {
|
|
66
|
+
default: 1
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
spacing: {
|
|
70
|
+
slider: {
|
|
71
|
+
position: tokens.spacing.xxxxSmall_2
|
|
72
|
+
},
|
|
73
|
+
icon: {
|
|
74
|
+
position: tokens.spacing.xxxSmall_4
|
|
75
|
+
},
|
|
76
|
+
transform: {
|
|
77
|
+
default: `translateX(${tokens.spacing.medium_16}px)`,
|
|
78
|
+
transition: "transform 0.15s ease-in-out"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const theme = mergeTheme(theme$1, {
|
|
84
|
+
color: {
|
|
85
|
+
bg: {
|
|
86
|
+
switch: {
|
|
87
|
+
off: tokens.color.white50,
|
|
88
|
+
disabledOff: tokens.color.white32,
|
|
89
|
+
activeOff: tokens.color.white50,
|
|
90
|
+
disabledOn: tokens.color.activeBlue
|
|
91
|
+
},
|
|
92
|
+
slider: {
|
|
93
|
+
off: tokens.color.eggplant
|
|
94
|
+
},
|
|
95
|
+
icon: {
|
|
96
|
+
off: tokens.color.white,
|
|
97
|
+
disabledOff: tokens.color.white50,
|
|
98
|
+
disabledOn: tokens.color.activeBlue
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const themes = {
|
|
105
|
+
default: theme$1,
|
|
106
|
+
khanmigo: theme
|
|
107
|
+
};
|
|
108
|
+
const SwitchThemeContext = createThemeContext(theme$1);
|
|
109
|
+
function ThemedSwitch(props) {
|
|
110
|
+
const currentTheme = React.useContext(ThemeSwitcherContext);
|
|
111
|
+
const theme = themes[currentTheme] || theme$1;
|
|
112
|
+
return React.createElement(SwitchThemeContext.Provider, {
|
|
113
|
+
value: theme
|
|
114
|
+
}, props.children);
|
|
115
|
+
}
|
|
116
|
+
|
|
22
117
|
const StyledSpan = addStyle("span");
|
|
23
118
|
const StyledInput = addStyle("input");
|
|
24
|
-
const
|
|
119
|
+
const SwitchCore = React.forwardRef(function SwitchCore(props, ref) {
|
|
25
120
|
const {
|
|
26
121
|
"aria-label": ariaLabel,
|
|
27
122
|
"aria-labelledby": ariaLabelledBy,
|
|
@@ -33,64 +128,67 @@ const Switch = React.forwardRef(function Switch(props, ref) {
|
|
|
33
128
|
onChange,
|
|
34
129
|
testId
|
|
35
130
|
} = props;
|
|
131
|
+
const ids = useUniqueIdWithMock("labeled-field");
|
|
132
|
+
const uniqueId = id != null ? id : ids.get("labeled-field-id");
|
|
133
|
+
const {
|
|
134
|
+
theme,
|
|
135
|
+
themeName
|
|
136
|
+
} = useScopedTheme(SwitchThemeContext);
|
|
137
|
+
const sharedStyles = useStyles(themedSharedStyles, theme);
|
|
36
138
|
const handleClick = () => {
|
|
37
139
|
if (!disabled && onChange) {
|
|
38
140
|
onChange(!checked);
|
|
39
141
|
}
|
|
40
142
|
};
|
|
41
143
|
const handleChange = () => {};
|
|
42
|
-
const stateStyles = _generateStyles(checked,
|
|
144
|
+
const stateStyles = _generateStyles(checked, onChange !== undefined, disabled, theme, themeName);
|
|
43
145
|
let styledIcon;
|
|
44
146
|
if (icon) {
|
|
45
|
-
styledIcon = React.cloneElement(icon,
|
|
147
|
+
styledIcon = React.cloneElement(icon, {
|
|
46
148
|
size: "small",
|
|
47
149
|
style: [sharedStyles.icon, stateStyles.icon],
|
|
48
150
|
"aria-hidden": true
|
|
49
|
-
}
|
|
151
|
+
});
|
|
50
152
|
}
|
|
51
|
-
return React.createElement(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
style: sharedStyles.hidden,
|
|
71
|
-
type: "checkbox"
|
|
72
|
-
}), icon && styledIcon, React.createElement(StyledSpan, {
|
|
73
|
-
style: [sharedStyles.slider, stateStyles.slider]
|
|
74
|
-
}));
|
|
75
|
-
});
|
|
153
|
+
return React.createElement(View, {
|
|
154
|
+
onClick: handleClick,
|
|
155
|
+
style: [sharedStyles.switch, stateStyles.switch, disabled && sharedStyles.disabled],
|
|
156
|
+
testId: testId
|
|
157
|
+
}, React.createElement(StyledInput, {
|
|
158
|
+
"aria-describedby": ariaDescribedBy,
|
|
159
|
+
"aria-label": ariaLabel,
|
|
160
|
+
"aria-labelledby": ariaLabelledBy,
|
|
161
|
+
checked: checked,
|
|
162
|
+
"aria-disabled": disabled,
|
|
163
|
+
id: uniqueId,
|
|
164
|
+
onChange: handleChange,
|
|
165
|
+
ref: ref,
|
|
166
|
+
role: "switch",
|
|
167
|
+
style: sharedStyles.hidden,
|
|
168
|
+
type: "checkbox"
|
|
169
|
+
}), icon && styledIcon, React.createElement(StyledSpan, {
|
|
170
|
+
style: [sharedStyles.slider, stateStyles.slider]
|
|
171
|
+
}));
|
|
76
172
|
});
|
|
77
|
-
const
|
|
173
|
+
const themedSharedStyles = theme => ({
|
|
78
174
|
hidden: {
|
|
79
175
|
opacity: 0,
|
|
80
|
-
height:
|
|
81
|
-
width:
|
|
176
|
+
height: theme.size.height.none,
|
|
177
|
+
width: theme.size.width.none
|
|
82
178
|
},
|
|
83
179
|
switch: {
|
|
84
180
|
display: "inline-flex",
|
|
85
|
-
height:
|
|
86
|
-
width:
|
|
87
|
-
borderRadius:
|
|
181
|
+
height: theme.size.height.large,
|
|
182
|
+
width: theme.size.width.large,
|
|
183
|
+
borderRadius: theme.border.radius.small,
|
|
88
184
|
flexShrink: 0,
|
|
89
|
-
cursor: "pointer",
|
|
90
185
|
":hover": {
|
|
91
|
-
outlineOffset:
|
|
186
|
+
outlineOffset: theme.size.offset.default
|
|
92
187
|
},
|
|
93
|
-
|
|
188
|
+
":focus-within": {
|
|
189
|
+
outline: `solid ${theme.size.width.small}px ${theme.color.outline.default}`,
|
|
190
|
+
outlineOffset: theme.size.offset.default
|
|
191
|
+
}
|
|
94
192
|
},
|
|
95
193
|
disabled: {
|
|
96
194
|
cursor: "auto",
|
|
@@ -100,78 +198,75 @@ const sharedStyles = StyleSheet.create({
|
|
|
100
198
|
},
|
|
101
199
|
slider: {
|
|
102
200
|
position: "absolute",
|
|
103
|
-
top:
|
|
104
|
-
left:
|
|
105
|
-
height:
|
|
106
|
-
width:
|
|
107
|
-
borderRadius:
|
|
108
|
-
backgroundColor:
|
|
109
|
-
transition:
|
|
201
|
+
top: theme.spacing.slider.position,
|
|
202
|
+
left: theme.spacing.slider.position,
|
|
203
|
+
height: theme.size.height.medium,
|
|
204
|
+
width: theme.size.width.medium,
|
|
205
|
+
borderRadius: theme.border.radius.full,
|
|
206
|
+
backgroundColor: theme.color.bg.slider.on,
|
|
207
|
+
transition: theme.spacing.transform.transition
|
|
110
208
|
},
|
|
111
209
|
icon: {
|
|
112
210
|
position: "absolute",
|
|
113
|
-
top:
|
|
114
|
-
left:
|
|
211
|
+
top: theme.spacing.icon.position,
|
|
212
|
+
left: theme.spacing.icon.position,
|
|
115
213
|
zIndex: 1,
|
|
116
|
-
transition:
|
|
117
|
-
transitionProperty: "transform, color"
|
|
214
|
+
transition: theme.spacing.transform.transition
|
|
118
215
|
}
|
|
119
216
|
});
|
|
120
217
|
const styles = {};
|
|
121
|
-
const _generateStyles = (checked, disabled,
|
|
122
|
-
const checkedStyle = `${checked}-${disabled}-${
|
|
218
|
+
const _generateStyles = (checked, clickable, disabled, theme, themeName) => {
|
|
219
|
+
const checkedStyle = `${checked}-${clickable}-${disabled}-${themeName}`;
|
|
123
220
|
if (styles[checkedStyle]) {
|
|
124
221
|
return styles[checkedStyle];
|
|
125
222
|
}
|
|
126
223
|
let newStyles = {};
|
|
127
|
-
const
|
|
128
|
-
|
|
224
|
+
const sharedSwitchStyles = {
|
|
225
|
+
cursor: clickable ? "pointer" : "auto",
|
|
226
|
+
":hover": {
|
|
227
|
+
outline: clickable ? `solid ${theme.size.width.small}px ${theme.color.outline.default}` : "none"
|
|
228
|
+
}
|
|
229
|
+
};
|
|
129
230
|
if (checked) {
|
|
130
231
|
newStyles = {
|
|
131
|
-
switch: {
|
|
132
|
-
backgroundColor: disabled ?
|
|
232
|
+
switch: _extends({
|
|
233
|
+
backgroundColor: disabled ? theme.color.bg.switch.disabledOn : theme.color.bg.switch.on,
|
|
133
234
|
":active": {
|
|
134
|
-
backgroundColor: !disabled && clickable
|
|
135
|
-
},
|
|
136
|
-
":focus-within": {
|
|
137
|
-
outline: `solid ${Spacing.xxxxSmall_2}px ${Color.blue}`,
|
|
138
|
-
outlineOffset: 1
|
|
139
|
-
},
|
|
140
|
-
":hover": {
|
|
141
|
-
outline: clickable ? `solid ${Spacing.xxxxSmall_2}px ${Color.blue}` : "none"
|
|
235
|
+
backgroundColor: !disabled && clickable ? theme.color.bg.switch.activeOn : undefined
|
|
142
236
|
}
|
|
143
|
-
},
|
|
237
|
+
}, sharedSwitchStyles),
|
|
144
238
|
slider: {
|
|
145
|
-
transform:
|
|
239
|
+
transform: theme.spacing.transform.default
|
|
146
240
|
},
|
|
147
241
|
icon: {
|
|
148
|
-
color: disabled ?
|
|
149
|
-
transform:
|
|
242
|
+
color: disabled ? theme.color.bg.icon.disabledOn : theme.color.bg.icon.on,
|
|
243
|
+
transform: theme.spacing.transform.default
|
|
150
244
|
}
|
|
151
245
|
};
|
|
152
246
|
} else {
|
|
153
247
|
newStyles = {
|
|
154
|
-
switch: {
|
|
155
|
-
backgroundColor: disabled ?
|
|
248
|
+
switch: _extends({
|
|
249
|
+
backgroundColor: disabled ? theme.color.bg.switch.disabledOff : theme.color.bg.switch.off,
|
|
156
250
|
":active": {
|
|
157
|
-
backgroundColor: !disabled && clickable
|
|
158
|
-
},
|
|
159
|
-
":focus-within": {
|
|
160
|
-
outline: `solid ${Spacing.xxxxSmall_2}px ${Color.blue}`,
|
|
161
|
-
outlineOffset: 1
|
|
162
|
-
},
|
|
163
|
-
":hover": {
|
|
164
|
-
outline: clickable ? `solid ${Spacing.xxxxSmall_2}px ${Color.blue}` : "none"
|
|
251
|
+
backgroundColor: !disabled && clickable ? theme.color.bg.switch.activeOff : undefined
|
|
165
252
|
}
|
|
253
|
+
}, sharedSwitchStyles),
|
|
254
|
+
slider: {
|
|
255
|
+
backgroundColor: theme.color.bg.slider.off
|
|
166
256
|
},
|
|
167
257
|
icon: {
|
|
168
|
-
color: disabled ?
|
|
258
|
+
color: disabled ? theme.color.bg.icon.disabledOff : theme.color.bg.icon.off
|
|
169
259
|
}
|
|
170
260
|
};
|
|
171
261
|
}
|
|
172
262
|
styles[checkedStyle] = StyleSheet.create(newStyles);
|
|
173
263
|
return styles[checkedStyle];
|
|
174
264
|
};
|
|
265
|
+
const Switch = React.forwardRef(function Switch(props, ref) {
|
|
266
|
+
return React.createElement(ThemedSwitch, null, React.createElement(SwitchCore, _extends({}, props, {
|
|
267
|
+
ref: ref
|
|
268
|
+
})));
|
|
269
|
+
});
|
|
175
270
|
Switch.displayName = "Switch";
|
|
176
271
|
|
|
177
272
|
export { Switch as default };
|