@khanacademy/math-input 10.0.1 → 10.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 +14 -0
- package/dist/components/keypad/keypad.d.ts +1 -0
- package/dist/components/keypad/mobile-keypad.d.ts +8 -0
- package/dist/components/keypad/navigation-button.d.ts +8 -0
- package/dist/components/keypad/navigation-pad.d.ts +6 -0
- package/dist/components/keypad/shared-keys.d.ts +2 -3
- package/dist/components/keypad/utils.d.ts +1 -0
- package/dist/components/keypad-legacy/two-page-keypad.d.ts +2 -2
- package/dist/components/tabbar/icons.d.ts +2 -2
- package/dist/components/tabbar/index.d.ts +0 -1
- package/dist/components/tabbar/item.d.ts +2 -2
- package/dist/components/tabbar/tabbar.d.ts +4 -4
- package/dist/es/index.js +485 -190
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +489 -190
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/keypad/__tests__/__snapshots__/keypad.test.tsx.snap +1855 -0
- package/src/components/keypad/__tests__/keypad.test.tsx +94 -0
- package/src/components/keypad/button-assets.tsx +233 -160
- package/src/components/keypad/keypad-button.tsx +1 -1
- package/src/components/keypad/keypad.stories.tsx +4 -0
- package/src/components/keypad/keypad.tsx +87 -58
- package/src/components/keypad/mobile-keypad.tsx +59 -4
- package/src/components/keypad/navigation-button.tsx +127 -0
- package/src/components/keypad/navigation-pad.stories.tsx +25 -0
- package/src/components/keypad/navigation-pad.tsx +67 -0
- package/src/components/keypad/shared-keys.tsx +2 -3
- package/src/components/keypad/utils.ts +4 -0
- package/src/components/keypad-legacy/keypad-button.tsx +2 -1
- package/src/components/keypad-legacy/two-page-keypad.tsx +2 -2
- package/src/components/tabbar/icons.tsx +2 -2
- package/src/components/tabbar/index.ts +0 -1
- package/src/components/tabbar/item.tsx +2 -2
- package/src/components/tabbar/tabbar.stories.tsx +3 -3
- package/src/components/tabbar/tabbar.tsx +4 -4
- package/src/types.ts +8 -0
- package/tsconfig-build.tsbuildinfo +1 -1
- package/dist/components/tabbar/types.d.ts +0 -1
- package/src/components/tabbar/types.ts +0 -7
|
@@ -73,7 +73,7 @@ const styles = StyleSheet.create({
|
|
|
73
73
|
display: "flex",
|
|
74
74
|
justifyContent: "center",
|
|
75
75
|
alignItems: "center",
|
|
76
|
-
boxShadow:
|
|
76
|
+
boxShadow: `0px 1px 0px ${Color.offBlack32}`,
|
|
77
77
|
boxSizing: "border-box",
|
|
78
78
|
background: Color.white,
|
|
79
79
|
borderRadius: 4,
|
|
@@ -31,6 +31,7 @@ export default {
|
|
|
31
31
|
preAlgebra: false,
|
|
32
32
|
trigonometry: false,
|
|
33
33
|
sendEvent: () => {},
|
|
34
|
+
onAnalyticsEvent: async () => {},
|
|
34
35
|
},
|
|
35
36
|
argTypes: {
|
|
36
37
|
advancedRelations: {
|
|
@@ -115,4 +116,7 @@ Everything.args = {
|
|
|
115
116
|
multiplicationDot: false,
|
|
116
117
|
preAlgebra: true,
|
|
117
118
|
trigonometry: true,
|
|
119
|
+
expandedView: true,
|
|
120
|
+
showDismiss: true,
|
|
121
|
+
extraKeys: ["a", "b", "c"],
|
|
118
122
|
};
|
|
@@ -11,22 +11,23 @@ import FractionsPage from "./keypad-pages/fractions-page";
|
|
|
11
11
|
import GeometryPage from "./keypad-pages/geometry-page";
|
|
12
12
|
import NumbersPage from "./keypad-pages/numbers-page";
|
|
13
13
|
import OperatorsPage from "./keypad-pages/operators-page";
|
|
14
|
+
import NavigationPad from "./navigation-pad";
|
|
14
15
|
import SharedKeys from "./shared-keys";
|
|
16
|
+
import {expandedViewThreshold} from "./utils";
|
|
15
17
|
|
|
16
18
|
import type Key from "../../data/keys";
|
|
17
|
-
import type {ClickKeyCallback} from "../../types";
|
|
19
|
+
import type {ClickKeyCallback, KeypadPageType} from "../../types";
|
|
18
20
|
import type {CursorContext} from "../input/cursor-contexts";
|
|
19
|
-
import type {TabbarItemType} from "../tabbar";
|
|
20
21
|
import type {AnalyticsEventHandlerFn} from "@khanacademy/perseus-core";
|
|
21
22
|
|
|
22
23
|
export type Props = {
|
|
23
24
|
extraKeys: ReadonlyArray<Key>;
|
|
24
25
|
cursorContext?: typeof CursorContext[keyof typeof CursorContext];
|
|
25
26
|
showDismiss?: boolean;
|
|
27
|
+
expandedView?: boolean;
|
|
26
28
|
|
|
27
29
|
multiplicationDot?: boolean;
|
|
28
30
|
divisionKey?: boolean;
|
|
29
|
-
|
|
30
31
|
trigonometry?: boolean;
|
|
31
32
|
preAlgebra?: boolean;
|
|
32
33
|
logarithms?: boolean;
|
|
@@ -42,13 +43,13 @@ const defaultProps = {
|
|
|
42
43
|
extraKeys: [],
|
|
43
44
|
};
|
|
44
45
|
|
|
45
|
-
function getAvailableTabs(props: Props): ReadonlyArray<
|
|
46
|
+
function getAvailableTabs(props: Props): ReadonlyArray<KeypadPageType> {
|
|
46
47
|
// We don't want to show any available tabs on the fractions keypad
|
|
47
48
|
if (props.fractionsOnly) {
|
|
48
49
|
return [];
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
const tabs: Array<
|
|
52
|
+
const tabs: Array<KeypadPageType> = ["Numbers"];
|
|
52
53
|
if (
|
|
53
54
|
// OperatorsButtonSets
|
|
54
55
|
props.preAlgebra ||
|
|
@@ -77,7 +78,7 @@ export default function Keypad(props: Props) {
|
|
|
77
78
|
// Otherwise, we want to default to the Numbers page
|
|
78
79
|
const defaultSelectedPage = props.fractionsOnly ? "Fractions" : "Numbers";
|
|
79
80
|
const [selectedPage, setSelectedPage] =
|
|
80
|
-
React.useState<
|
|
81
|
+
React.useState<KeypadPageType>(defaultSelectedPage);
|
|
81
82
|
const [isMounted, setIsMounted] = React.useState<boolean>(false);
|
|
82
83
|
|
|
83
84
|
// We don't want any tabs available on mobile fractions keypad
|
|
@@ -96,6 +97,7 @@ export default function Keypad(props: Props) {
|
|
|
96
97
|
showDismiss,
|
|
97
98
|
onAnalyticsEvent,
|
|
98
99
|
fractionsOnly,
|
|
100
|
+
expandedView,
|
|
99
101
|
} = props;
|
|
100
102
|
|
|
101
103
|
// Use a different grid for our fraction keypad
|
|
@@ -128,58 +130,70 @@ export default function Keypad(props: Props) {
|
|
|
128
130
|
}, [onAnalyticsEvent, isMounted]);
|
|
129
131
|
|
|
130
132
|
return (
|
|
131
|
-
<View>
|
|
132
|
-
<Tabbar
|
|
133
|
-
items={availableTabs}
|
|
134
|
-
selectedItem={selectedPage}
|
|
135
|
-
onSelectItem={(tabbarItem: TabbarItemType) => {
|
|
136
|
-
setSelectedPage(tabbarItem);
|
|
137
|
-
}}
|
|
138
|
-
style={styles.tabbar}
|
|
139
|
-
onClickClose={
|
|
140
|
-
showDismiss ? () => onClickKey("DISMISS") : undefined
|
|
141
|
-
}
|
|
142
|
-
/>
|
|
143
|
-
|
|
133
|
+
<View style={expandedView ? styles.keypadOuterContainer : null}>
|
|
144
134
|
<View
|
|
145
|
-
style={[
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
135
|
+
style={[
|
|
136
|
+
styles.wrapper,
|
|
137
|
+
expandedView ? styles.expandedWrapper : null,
|
|
138
|
+
]}
|
|
149
139
|
>
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
140
|
+
<Tabbar
|
|
141
|
+
items={availableTabs}
|
|
142
|
+
selectedItem={selectedPage}
|
|
143
|
+
onSelectItem={(newSelectedPage: KeypadPageType) => {
|
|
144
|
+
setSelectedPage(newSelectedPage);
|
|
145
|
+
}}
|
|
146
|
+
onClickClose={
|
|
147
|
+
showDismiss ? () => onClickKey("DISMISS") : undefined
|
|
148
|
+
}
|
|
149
|
+
/>
|
|
150
|
+
|
|
151
|
+
<View style={styles.keypadInnerContainer}>
|
|
152
|
+
<View
|
|
153
|
+
style={[styles.keypadGrid, gridStyle]}
|
|
154
|
+
role="grid"
|
|
155
|
+
tabIndex={0}
|
|
156
|
+
aria-label="Keypad"
|
|
157
|
+
>
|
|
158
|
+
{selectedPage === "Fractions" && (
|
|
159
|
+
<FractionsPage
|
|
160
|
+
onClickKey={onClickKey}
|
|
161
|
+
cursorContext={cursorContext}
|
|
162
|
+
/>
|
|
163
|
+
)}
|
|
164
|
+
{selectedPage === "Numbers" && (
|
|
165
|
+
<NumbersPage onClickKey={onClickKey} />
|
|
166
|
+
)}
|
|
167
|
+
{selectedPage === "Extras" && (
|
|
168
|
+
<ExtrasPage
|
|
169
|
+
onClickKey={onClickKey}
|
|
170
|
+
extraKeys={extraKeys}
|
|
171
|
+
/>
|
|
172
|
+
)}
|
|
173
|
+
{selectedPage === "Operators" && (
|
|
174
|
+
<OperatorsPage
|
|
175
|
+
onClickKey={onClickKey}
|
|
176
|
+
preAlgebra={preAlgebra}
|
|
177
|
+
logarithms={logarithms}
|
|
178
|
+
basicRelations={basicRelations}
|
|
179
|
+
advancedRelations={advancedRelations}
|
|
180
|
+
/>
|
|
181
|
+
)}
|
|
182
|
+
{selectedPage === "Geometry" && (
|
|
183
|
+
<GeometryPage onClickKey={onClickKey} />
|
|
184
|
+
)}
|
|
185
|
+
{!fractionsOnly && (
|
|
186
|
+
<SharedKeys
|
|
187
|
+
onClickKey={onClickKey}
|
|
188
|
+
cursorContext={cursorContext}
|
|
189
|
+
multiplicationDot={multiplicationDot}
|
|
190
|
+
divisionKey={divisionKey}
|
|
191
|
+
selectedPage={selectedPage}
|
|
192
|
+
/>
|
|
193
|
+
)}
|
|
194
|
+
</View>
|
|
195
|
+
{expandedView && <NavigationPad onClickKey={onClickKey} />}
|
|
196
|
+
</View>
|
|
183
197
|
</View>
|
|
184
198
|
</View>
|
|
185
199
|
);
|
|
@@ -188,13 +202,28 @@ export default function Keypad(props: Props) {
|
|
|
188
202
|
Keypad.defaultProps = defaultProps;
|
|
189
203
|
|
|
190
204
|
const styles = StyleSheet.create({
|
|
191
|
-
|
|
205
|
+
keypadOuterContainer: {
|
|
206
|
+
display: "flex",
|
|
207
|
+
alignItems: "center",
|
|
208
|
+
},
|
|
209
|
+
wrapper: {
|
|
192
210
|
background: Color.white,
|
|
193
211
|
},
|
|
212
|
+
expandedWrapper: {
|
|
213
|
+
borderWidth: "1px 1px 0 1px",
|
|
214
|
+
borderColor: Color.offBlack32,
|
|
215
|
+
maxWidth: expandedViewThreshold,
|
|
216
|
+
borderRadius: "3px 3px 0 0",
|
|
217
|
+
},
|
|
218
|
+
keypadInnerContainer: {
|
|
219
|
+
display: "flex",
|
|
220
|
+
flexDirection: "row",
|
|
221
|
+
backgroundColor: "#DBDCDD",
|
|
222
|
+
},
|
|
194
223
|
keypadGrid: {
|
|
195
224
|
display: "grid",
|
|
196
225
|
gridTemplateRows: "repeat(4, 1fr)",
|
|
197
|
-
|
|
226
|
+
flex: 1,
|
|
198
227
|
},
|
|
199
228
|
expressionGrid: {
|
|
200
229
|
gridTemplateColumns: "repeat(6, 1fr)",
|
|
@@ -4,6 +4,8 @@ import ReactDOM from "react-dom";
|
|
|
4
4
|
|
|
5
5
|
import {View} from "../../fake-react-native-web/index";
|
|
6
6
|
|
|
7
|
+
import {expandedViewThreshold} from "./utils";
|
|
8
|
+
|
|
7
9
|
import type Key from "../../data/keys";
|
|
8
10
|
import type {
|
|
9
11
|
Cursor,
|
|
@@ -34,18 +36,68 @@ type Props = {
|
|
|
34
36
|
|
|
35
37
|
type State = {
|
|
36
38
|
active: boolean;
|
|
39
|
+
containerWidth: number;
|
|
37
40
|
keypadConfig?: KeypadConfiguration;
|
|
38
41
|
keyHandler?: KeyHandler;
|
|
39
42
|
cursor?: Cursor;
|
|
40
43
|
};
|
|
41
44
|
|
|
42
45
|
class MobileKeypad extends React.Component<Props, State> implements KeypadAPI {
|
|
46
|
+
_containerRef = React.createRef<HTMLDivElement>();
|
|
47
|
+
_containerResizeObserver: ResizeObserver | null = null;
|
|
48
|
+
_throttleResize = false;
|
|
43
49
|
hasMounted = false;
|
|
44
50
|
|
|
45
51
|
state: State = {
|
|
52
|
+
containerWidth: 0,
|
|
46
53
|
active: false,
|
|
47
54
|
};
|
|
48
55
|
|
|
56
|
+
componentDidMount() {
|
|
57
|
+
this._resize();
|
|
58
|
+
|
|
59
|
+
window.addEventListener("resize", this._throttleResizeHandler);
|
|
60
|
+
window.addEventListener(
|
|
61
|
+
"orientationchange",
|
|
62
|
+
this._throttleResizeHandler,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
this._containerResizeObserver = new ResizeObserver(
|
|
66
|
+
this._throttleResizeHandler,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (this._containerRef.current) {
|
|
70
|
+
this._containerResizeObserver.observe(this._containerRef.current);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
componentWillUnMount() {
|
|
75
|
+
window.removeEventListener("resize", this._throttleResizeHandler);
|
|
76
|
+
window.removeEventListener(
|
|
77
|
+
"orientationchange",
|
|
78
|
+
this._throttleResizeHandler,
|
|
79
|
+
);
|
|
80
|
+
this._containerResizeObserver?.disconnect();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
_resize = () => {
|
|
84
|
+
const containerWidth = this._containerRef.current?.clientWidth || 0;
|
|
85
|
+
this.setState({containerWidth});
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
_throttleResizeHandler = () => {
|
|
89
|
+
if (this._throttleResize) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
this._throttleResize = true;
|
|
94
|
+
|
|
95
|
+
setTimeout(() => {
|
|
96
|
+
this._resize();
|
|
97
|
+
this._throttleResize = false;
|
|
98
|
+
}, 100);
|
|
99
|
+
};
|
|
100
|
+
|
|
49
101
|
activate: () => void = () => {
|
|
50
102
|
this.setState({active: true});
|
|
51
103
|
};
|
|
@@ -98,12 +150,12 @@ class MobileKeypad extends React.Component<Props, State> implements KeypadAPI {
|
|
|
98
150
|
|
|
99
151
|
render(): React.ReactNode {
|
|
100
152
|
const {style} = this.props;
|
|
101
|
-
const {active, cursor, keypadConfig} = this.state;
|
|
153
|
+
const {active, containerWidth, cursor, keypadConfig} = this.state;
|
|
102
154
|
|
|
103
155
|
const containerStyle = [
|
|
104
156
|
// internal styles
|
|
105
157
|
styles.keypadContainer,
|
|
106
|
-
active
|
|
158
|
+
active && styles.activeKeypadContainer,
|
|
107
159
|
// styles passed as props
|
|
108
160
|
...(Array.isArray(style) ? style : [style]),
|
|
109
161
|
];
|
|
@@ -113,6 +165,7 @@ class MobileKeypad extends React.Component<Props, State> implements KeypadAPI {
|
|
|
113
165
|
return (
|
|
114
166
|
<View
|
|
115
167
|
style={containerStyle}
|
|
168
|
+
forwardRef={this._containerRef}
|
|
116
169
|
ref={(element) => {
|
|
117
170
|
if (!this.hasMounted && element) {
|
|
118
171
|
// TODO(matthewc)[LC-1081]: clean up this weird
|
|
@@ -150,6 +203,7 @@ class MobileKeypad extends React.Component<Props, State> implements KeypadAPI {
|
|
|
150
203
|
logarithms={isExpression}
|
|
151
204
|
basicRelations={isExpression}
|
|
152
205
|
advancedRelations={isExpression}
|
|
206
|
+
expandedView={containerWidth > expandedViewThreshold}
|
|
153
207
|
showDismiss
|
|
154
208
|
/>
|
|
155
209
|
</View>
|
|
@@ -163,13 +217,14 @@ const styles = StyleSheet.create({
|
|
|
163
217
|
left: 0,
|
|
164
218
|
right: 0,
|
|
165
219
|
position: "fixed",
|
|
220
|
+
transitionProperty: "all",
|
|
166
221
|
transition: `200ms ease-out`,
|
|
167
|
-
|
|
222
|
+
visibility: "hidden",
|
|
168
223
|
transform: "translate3d(0, 100%, 0)",
|
|
169
224
|
},
|
|
170
|
-
|
|
171
225
|
activeKeypadContainer: {
|
|
172
226
|
transform: "translate3d(0, 0, 0)",
|
|
227
|
+
visibility: "visible",
|
|
173
228
|
},
|
|
174
229
|
});
|
|
175
230
|
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import Clickable from "@khanacademy/wonder-blocks-clickable";
|
|
2
|
+
import Color from "@khanacademy/wonder-blocks-color";
|
|
3
|
+
import {View} from "@khanacademy/wonder-blocks-core";
|
|
4
|
+
import {StyleSheet} from "aphrodite";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
|
|
7
|
+
import ButtonAsset from "./button-assets";
|
|
8
|
+
|
|
9
|
+
import type Key from "../../data/keys";
|
|
10
|
+
import type {KeyConfig, ClickKeyCallback} from "../../types";
|
|
11
|
+
|
|
12
|
+
export type KeypadButtonProps = {
|
|
13
|
+
// 0 indexed [x, y] position in keypad CSS grid
|
|
14
|
+
coord: readonly [number, number];
|
|
15
|
+
keyConfig: KeyConfig;
|
|
16
|
+
onClickKey: ClickKeyCallback;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function getStyles(key: Key) {
|
|
20
|
+
switch (key) {
|
|
21
|
+
case "UP":
|
|
22
|
+
return styles.up;
|
|
23
|
+
case "RIGHT":
|
|
24
|
+
return styles.right;
|
|
25
|
+
case "DOWN":
|
|
26
|
+
return styles.down;
|
|
27
|
+
case "LEFT":
|
|
28
|
+
return styles.left;
|
|
29
|
+
default:
|
|
30
|
+
throw new Error(`Invalid key: ${key}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default function NavigationButton({
|
|
35
|
+
coord,
|
|
36
|
+
keyConfig,
|
|
37
|
+
onClickKey,
|
|
38
|
+
}: KeypadButtonProps) {
|
|
39
|
+
const key = keyConfig.id;
|
|
40
|
+
const directionalStyles = getStyles(key);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<View
|
|
44
|
+
style={{
|
|
45
|
+
gridColumn: coord[0] + 1,
|
|
46
|
+
gridRow: coord[1] + 1,
|
|
47
|
+
}}
|
|
48
|
+
>
|
|
49
|
+
<Clickable
|
|
50
|
+
onClick={(e) => onClickKey(keyConfig.id, e)}
|
|
51
|
+
style={styles.clickable}
|
|
52
|
+
aria-label={keyConfig.ariaLabel}
|
|
53
|
+
>
|
|
54
|
+
{({hovered, focused, pressed}) => (
|
|
55
|
+
<View style={styles.outerBoxBase}>
|
|
56
|
+
<View
|
|
57
|
+
style={[
|
|
58
|
+
styles.base,
|
|
59
|
+
directionalStyles,
|
|
60
|
+
hovered && styles.hovered,
|
|
61
|
+
focused && styles.focused,
|
|
62
|
+
pressed && styles.pressed,
|
|
63
|
+
]}
|
|
64
|
+
>
|
|
65
|
+
<ButtonAsset id={keyConfig.id} />
|
|
66
|
+
</View>
|
|
67
|
+
</View>
|
|
68
|
+
)}
|
|
69
|
+
</Clickable>
|
|
70
|
+
</View>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const borderRadiusPx = 4;
|
|
75
|
+
|
|
76
|
+
const styles = StyleSheet.create({
|
|
77
|
+
clickable: {
|
|
78
|
+
width: "100%",
|
|
79
|
+
height: "100%",
|
|
80
|
+
|
|
81
|
+
":focus": {
|
|
82
|
+
outline: `none`,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
outerBoxBase: {
|
|
86
|
+
height: "100%",
|
|
87
|
+
width: "100%",
|
|
88
|
+
},
|
|
89
|
+
base: {
|
|
90
|
+
boxShadow: `0px 1px 0px ${Color.offBlack32}`,
|
|
91
|
+
display: "flex",
|
|
92
|
+
justifyContent: "center",
|
|
93
|
+
alignItems: "center",
|
|
94
|
+
background: Color.white,
|
|
95
|
+
borderWidth: 2,
|
|
96
|
+
borderColor: Color.white,
|
|
97
|
+
},
|
|
98
|
+
up: {
|
|
99
|
+
borderTopLeftRadius: borderRadiusPx,
|
|
100
|
+
borderTopRightRadius: borderRadiusPx,
|
|
101
|
+
},
|
|
102
|
+
right: {
|
|
103
|
+
borderTopRightRadius: borderRadiusPx,
|
|
104
|
+
borderBottomRightRadius: borderRadiusPx,
|
|
105
|
+
},
|
|
106
|
+
down: {
|
|
107
|
+
borderBottomLeftRadius: borderRadiusPx,
|
|
108
|
+
borderBottomRightRadius: borderRadiusPx,
|
|
109
|
+
},
|
|
110
|
+
left: {
|
|
111
|
+
borderTopLeftRadius: borderRadiusPx,
|
|
112
|
+
borderBottomLeftRadius: borderRadiusPx,
|
|
113
|
+
},
|
|
114
|
+
hovered: {
|
|
115
|
+
borderColor: Color.blue,
|
|
116
|
+
boxShadow: "none",
|
|
117
|
+
},
|
|
118
|
+
focused: {
|
|
119
|
+
borderColor: Color.blue,
|
|
120
|
+
boxShadow: "none",
|
|
121
|
+
},
|
|
122
|
+
pressed: {
|
|
123
|
+
border: "2px solid #1B50B3",
|
|
124
|
+
background: `linear-gradient(0deg, rgba(24, 101, 242, 0.32), rgba(24, 101, 242, 0.32)), ${Color.white}`,
|
|
125
|
+
boxShadow: "none",
|
|
126
|
+
},
|
|
127
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import NavigationPad from "./navigation-pad";
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title: "MathInput v2 Navigation Pad",
|
|
7
|
+
parameters: {
|
|
8
|
+
backgrounds: {
|
|
9
|
+
default: "light background",
|
|
10
|
+
values: [
|
|
11
|
+
// We want a slightly darker default bg so that we can
|
|
12
|
+
// see the top of the keypad when it is open
|
|
13
|
+
{name: "light background", value: "lightgrey", default: true},
|
|
14
|
+
],
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function basic() {
|
|
20
|
+
return (
|
|
21
|
+
<div style={{padding: 50}}>
|
|
22
|
+
<NavigationPad onClickKey={() => {}} />
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import Color from "@khanacademy/wonder-blocks-color";
|
|
2
|
+
import {View} from "@khanacademy/wonder-blocks-core";
|
|
3
|
+
import {StyleSheet} from "aphrodite";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
import Keys from "../../data/key-configs";
|
|
7
|
+
|
|
8
|
+
import NavigationButton from "./navigation-button";
|
|
9
|
+
|
|
10
|
+
import type {ClickKeyCallback} from "../../types";
|
|
11
|
+
|
|
12
|
+
export type Props = {
|
|
13
|
+
onClickKey: ClickKeyCallback;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default function NavigationPad(props: Props) {
|
|
17
|
+
const {onClickKey} = props;
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<View style={styles.container}>
|
|
21
|
+
<View style={styles.grid}>
|
|
22
|
+
<NavigationButton
|
|
23
|
+
keyConfig={Keys.UP}
|
|
24
|
+
onClickKey={onClickKey}
|
|
25
|
+
coord={[1, 0]}
|
|
26
|
+
/>
|
|
27
|
+
<NavigationButton
|
|
28
|
+
keyConfig={Keys.RIGHT}
|
|
29
|
+
onClickKey={onClickKey}
|
|
30
|
+
coord={[2, 1]}
|
|
31
|
+
/>
|
|
32
|
+
<NavigationButton
|
|
33
|
+
keyConfig={Keys.DOWN}
|
|
34
|
+
onClickKey={onClickKey}
|
|
35
|
+
coord={[1, 2]}
|
|
36
|
+
/>
|
|
37
|
+
<NavigationButton
|
|
38
|
+
keyConfig={Keys.LEFT}
|
|
39
|
+
onClickKey={onClickKey}
|
|
40
|
+
coord={[0, 1]}
|
|
41
|
+
/>
|
|
42
|
+
<View style={styles.spacer} />
|
|
43
|
+
</View>
|
|
44
|
+
</View>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const styles = StyleSheet.create({
|
|
49
|
+
container: {
|
|
50
|
+
display: "flex",
|
|
51
|
+
alignItems: "center",
|
|
52
|
+
justifyContent: "center",
|
|
53
|
+
padding: "0 1.5rem",
|
|
54
|
+
},
|
|
55
|
+
grid: {
|
|
56
|
+
width: 140,
|
|
57
|
+
height: 140,
|
|
58
|
+
display: "grid",
|
|
59
|
+
gridTemplateColumns: "repeat(3, 1fr)",
|
|
60
|
+
gridTemplateRows: "repeat(3, 1fr)",
|
|
61
|
+
},
|
|
62
|
+
spacer: {
|
|
63
|
+
gridColumn: 2,
|
|
64
|
+
gridRow: 2,
|
|
65
|
+
background: Color.white,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
@@ -5,13 +5,12 @@ import Keys from "../../data/key-configs";
|
|
|
5
5
|
import {KeypadButton} from "./keypad-button";
|
|
6
6
|
import {getCursorContextConfig} from "./utils";
|
|
7
7
|
|
|
8
|
-
import type {ClickKeyCallback} from "../../types";
|
|
8
|
+
import type {ClickKeyCallback, KeypadPageType} from "../../types";
|
|
9
9
|
import type {CursorContext} from "../input/cursor-contexts";
|
|
10
|
-
import type {TabbarItemType} from "../tabbar";
|
|
11
10
|
|
|
12
11
|
type Props = {
|
|
13
12
|
onClickKey: ClickKeyCallback;
|
|
14
|
-
selectedPage:
|
|
13
|
+
selectedPage: KeypadPageType;
|
|
15
14
|
cursorContext?: typeof CursorContext[keyof typeof CursorContext];
|
|
16
15
|
multiplicationDot?: boolean;
|
|
17
16
|
divisionKey?: boolean;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import Keys from "../../data/key-configs";
|
|
2
2
|
import {CursorContext} from "../input/cursor-contexts";
|
|
3
3
|
|
|
4
|
+
// This is just a magic number, I just played around with it
|
|
5
|
+
// until the transition from expanded -> regular -> expanded felt natural
|
|
6
|
+
export const expandedViewThreshold = 500;
|
|
7
|
+
|
|
4
8
|
// This is a helper function that returns the correct context for the cursor
|
|
5
9
|
// based on the cursorContext prop. It is used in the keypad to determine
|
|
6
10
|
// which key to render as the "jump out" key.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* A component that renders a keypad button.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import Color from "@khanacademy/wonder-blocks-color";
|
|
5
6
|
import {StyleSheet, css} from "aphrodite";
|
|
6
7
|
import * as React from "react";
|
|
7
8
|
import {connect} from "react-redux";
|
|
@@ -313,7 +314,7 @@ const styles = StyleSheet.create({
|
|
|
313
314
|
backgroundColor: wonderBlocksBlue,
|
|
314
315
|
},
|
|
315
316
|
light: {
|
|
316
|
-
backgroundColor:
|
|
317
|
+
backgroundColor: Color.offBlack32,
|
|
317
318
|
},
|
|
318
319
|
|
|
319
320
|
iconWrapper: {
|
|
@@ -19,7 +19,7 @@ import Tabbar from "../tabbar";
|
|
|
19
19
|
import Keypad from "./keypad";
|
|
20
20
|
import Styles from "./styles";
|
|
21
21
|
|
|
22
|
-
import type {
|
|
22
|
+
import type {KeypadPageType} from "../../types";
|
|
23
23
|
import type {State as ReduxState} from "./store/types";
|
|
24
24
|
|
|
25
25
|
const {column, row, fullWidth} = Styles;
|
|
@@ -34,7 +34,7 @@ interface Props extends ReduxProps {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
type State = {
|
|
37
|
-
selectedPage:
|
|
37
|
+
selectedPage: KeypadPageType;
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
class TwoPageKeypad extends React.Component<Props, State> {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type {KeypadPageType} from "../../types";
|
|
4
4
|
|
|
5
5
|
type Props = {
|
|
6
6
|
tintColor: string;
|
|
7
|
-
type:
|
|
7
|
+
type: KeypadPageType;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
const IconAsset = function ({tintColor, type}: Props): React.ReactElement {
|
|
@@ -6,7 +6,7 @@ import * as React from "react";
|
|
|
6
6
|
|
|
7
7
|
import IconAsset from "./icons";
|
|
8
8
|
|
|
9
|
-
import type {
|
|
9
|
+
import type {KeypadPageType} from "../../types";
|
|
10
10
|
|
|
11
11
|
const styles = StyleSheet.create({
|
|
12
12
|
base: {
|
|
@@ -78,7 +78,7 @@ export type ItemState = "active" | "inactive" | "disabled";
|
|
|
78
78
|
type Props = {
|
|
79
79
|
onClick: () => void;
|
|
80
80
|
itemState: ItemState;
|
|
81
|
-
itemType:
|
|
81
|
+
itemType: KeypadPageType;
|
|
82
82
|
};
|
|
83
83
|
|
|
84
84
|
class TabbarItem extends React.Component<Props> {
|