@khanacademy/math-input 0.3.2 → 0.5.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.
Files changed (176) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +1 -1
  3. package/{build/math-input.css → dist/es/index.css} +0 -150
  4. package/dist/es/index.js +2 -0
  5. package/dist/es/index.js.map +1 -0
  6. package/dist/index.css +586 -0
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.js +2 -0
  9. package/dist/index.js.flow +2 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/strings.js +71 -0
  12. package/index.html +20 -0
  13. package/less/echo.less +56 -0
  14. package/less/main.less +5 -0
  15. package/less/overrides.less +129 -0
  16. package/less/popover.less +22 -0
  17. package/less/tabbar.less +6 -0
  18. package/package.json +38 -70
  19. package/src/actions/index.js +57 -0
  20. package/src/components/__tests__/gesture-state-machine_test.js +437 -0
  21. package/src/components/__tests__/node-manager_test.js +89 -0
  22. package/src/components/__tests__/two-page-keypad_test.js +42 -0
  23. package/src/components/app.js +73 -0
  24. package/src/components/common-style.js +47 -0
  25. package/src/components/compute-layout-parameters.js +157 -0
  26. package/src/components/corner-decal.js +56 -0
  27. package/src/components/echo-manager.js +160 -0
  28. package/src/components/empty-keypad-button.js +49 -0
  29. package/src/components/expression-keypad.js +323 -0
  30. package/src/components/fraction-keypad.js +176 -0
  31. package/src/components/gesture-manager.js +226 -0
  32. package/src/components/gesture-state-machine.js +283 -0
  33. package/src/components/icon.js +74 -0
  34. package/src/components/iconography/arrow.js +22 -0
  35. package/src/components/iconography/backspace.js +29 -0
  36. package/src/components/iconography/cdot.js +29 -0
  37. package/src/components/iconography/cos.js +30 -0
  38. package/src/components/iconography/cube-root.js +36 -0
  39. package/src/components/iconography/dismiss.js +25 -0
  40. package/src/components/iconography/divide.js +34 -0
  41. package/src/components/iconography/down.js +16 -0
  42. package/src/components/iconography/equal.js +33 -0
  43. package/src/components/iconography/exp-2.js +29 -0
  44. package/src/components/iconography/exp-3.js +29 -0
  45. package/src/components/iconography/exp.js +29 -0
  46. package/src/components/iconography/frac.js +44 -0
  47. package/src/components/iconography/geq.js +33 -0
  48. package/src/components/iconography/gt.js +33 -0
  49. package/src/components/iconography/index.js +45 -0
  50. package/src/components/iconography/jump-into-numerator.js +41 -0
  51. package/src/components/iconography/jump-out-base.js +30 -0
  52. package/src/components/iconography/jump-out-denominator.js +41 -0
  53. package/src/components/iconography/jump-out-exponent.js +30 -0
  54. package/src/components/iconography/jump-out-numerator.js +41 -0
  55. package/src/components/iconography/jump-out-parentheses.js +33 -0
  56. package/src/components/iconography/left-paren.js +33 -0
  57. package/src/components/iconography/left.js +16 -0
  58. package/src/components/iconography/leq.js +33 -0
  59. package/src/components/iconography/ln.js +29 -0
  60. package/src/components/iconography/log-n.js +29 -0
  61. package/src/components/iconography/log.js +29 -0
  62. package/src/components/iconography/lt.js +33 -0
  63. package/src/components/iconography/minus.js +32 -0
  64. package/src/components/iconography/neq.js +33 -0
  65. package/src/components/iconography/parens.js +33 -0
  66. package/src/components/iconography/percent.js +49 -0
  67. package/src/components/iconography/period.js +26 -0
  68. package/src/components/iconography/plus.js +32 -0
  69. package/src/components/iconography/radical.js +36 -0
  70. package/src/components/iconography/right-paren.js +33 -0
  71. package/src/components/iconography/right.js +16 -0
  72. package/src/components/iconography/sin.js +30 -0
  73. package/src/components/iconography/sqrt.js +32 -0
  74. package/src/components/iconography/tan.js +30 -0
  75. package/src/components/iconography/times.js +33 -0
  76. package/src/components/iconography/up.js +16 -0
  77. package/src/components/input/__tests__/context-tracking_test.js +177 -0
  78. package/src/components/input/__tests__/math-wrapper.jsx +33 -0
  79. package/src/components/input/__tests__/mathquill_test.js +747 -0
  80. package/src/components/input/cursor-contexts.js +29 -0
  81. package/src/components/input/cursor-handle.js +137 -0
  82. package/src/components/input/drag-listener.js +75 -0
  83. package/src/components/input/math-input.js +924 -0
  84. package/src/components/input/math-wrapper.js +959 -0
  85. package/src/components/input/scroll-into-view.js +72 -0
  86. package/src/components/keypad/button-assets.js +492 -0
  87. package/src/components/keypad/button.js +106 -0
  88. package/src/components/keypad/button.stories.js +27 -0
  89. package/src/components/keypad/index.js +64 -0
  90. package/src/components/keypad/keypad-page-items.js +106 -0
  91. package/src/components/keypad/keypad-pages.stories.js +32 -0
  92. package/src/components/keypad/keypad.stories.js +35 -0
  93. package/src/components/keypad/numeric-input-page.js +100 -0
  94. package/src/components/keypad/pre-algebra-page.js +98 -0
  95. package/src/components/keypad/trigonometry-page.js +90 -0
  96. package/src/components/keypad-button.js +366 -0
  97. package/src/components/keypad-container.js +303 -0
  98. package/src/components/keypad.js +154 -0
  99. package/src/components/many-keypad-button.js +44 -0
  100. package/src/components/math-icon.js +65 -0
  101. package/src/components/multi-symbol-grid.js +182 -0
  102. package/src/components/multi-symbol-popover.js +59 -0
  103. package/src/components/navigation-pad.js +139 -0
  104. package/src/components/node-manager.js +129 -0
  105. package/src/components/popover-manager.js +76 -0
  106. package/src/components/popover-state-machine.js +173 -0
  107. package/src/components/prop-types.js +82 -0
  108. package/src/components/provided-keypad.js +99 -0
  109. package/src/components/styles.js +38 -0
  110. package/src/components/svg-icon.js +25 -0
  111. package/src/components/tabbar/__tests__/tabbar_test.js +65 -0
  112. package/src/components/tabbar/icons.js +69 -0
  113. package/src/components/tabbar/item.js +138 -0
  114. package/src/components/tabbar/tabbar.js +61 -0
  115. package/src/components/tabbar/tabbar.stories.js +60 -0
  116. package/src/components/tabbar/types.js +3 -0
  117. package/src/components/text-icon.js +52 -0
  118. package/src/components/touchable-keypad-button.js +146 -0
  119. package/src/components/two-page-keypad.js +99 -0
  120. package/src/components/velocity-tracker.js +76 -0
  121. package/src/components/z-indexes.js +9 -0
  122. package/src/consts.js +74 -0
  123. package/src/data/key-configs.js +349 -0
  124. package/src/data/keys.js +72 -0
  125. package/src/demo.js +8 -0
  126. package/src/fake-react-native-web/index.js +12 -0
  127. package/src/fake-react-native-web/text.js +56 -0
  128. package/src/fake-react-native-web/view.js +91 -0
  129. package/src/index.js +13 -0
  130. package/src/native-app.js +84 -0
  131. package/src/store/index.js +505 -0
  132. package/src/utils.js +18 -0
  133. package/tools/svg-to-react/convert.py +111 -0
  134. package/tools/svg-to-react/icons/math-keypad-icon-0.svg +32 -0
  135. package/tools/svg-to-react/icons/math-keypad-icon-1.svg +32 -0
  136. package/tools/svg-to-react/icons/math-keypad-icon-2.svg +32 -0
  137. package/tools/svg-to-react/icons/math-keypad-icon-3.svg +32 -0
  138. package/tools/svg-to-react/icons/math-keypad-icon-4.svg +32 -0
  139. package/tools/svg-to-react/icons/math-keypad-icon-5.svg +32 -0
  140. package/tools/svg-to-react/icons/math-keypad-icon-6.svg +32 -0
  141. package/tools/svg-to-react/icons/math-keypad-icon-7.svg +32 -0
  142. package/tools/svg-to-react/icons/math-keypad-icon-8.svg +32 -0
  143. package/tools/svg-to-react/icons/math-keypad-icon-9.svg +32 -0
  144. package/tools/svg-to-react/icons/math-keypad-icon-addition.svg +34 -0
  145. package/tools/svg-to-react/icons/math-keypad-icon-cos.svg +38 -0
  146. package/tools/svg-to-react/icons/math-keypad-icon-delete.svg +36 -0
  147. package/tools/svg-to-react/icons/math-keypad-icon-dismiss.svg +36 -0
  148. package/tools/svg-to-react/icons/math-keypad-icon-division.svg +36 -0
  149. package/tools/svg-to-react/icons/math-keypad-icon-equals-not.svg +50 -0
  150. package/tools/svg-to-react/icons/math-keypad-icon-equals.svg +48 -0
  151. package/tools/svg-to-react/icons/math-keypad-icon-exponent-2.svg +38 -0
  152. package/tools/svg-to-react/icons/math-keypad-icon-exponent-3.svg +38 -0
  153. package/tools/svg-to-react/icons/math-keypad-icon-exponent.svg +38 -0
  154. package/tools/svg-to-react/icons/math-keypad-icon-fraction.svg +42 -0
  155. package/tools/svg-to-react/icons/math-keypad-icon-greater-than.svg +46 -0
  156. package/tools/svg-to-react/icons/math-keypad-icon-jump-out-base.svg +44 -0
  157. package/tools/svg-to-react/icons/math-keypad-icon-jump-out-denominator.svg +48 -0
  158. package/tools/svg-to-react/icons/math-keypad-icon-jump-out-exponent.svg +44 -0
  159. package/tools/svg-to-react/icons/math-keypad-icon-jump-out-parentheses.svg +44 -0
  160. package/tools/svg-to-react/icons/math-keypad-icon-less-than.svg +46 -0
  161. package/tools/svg-to-react/icons/math-keypad-icon-log-10.svg +36 -0
  162. package/tools/svg-to-react/icons/math-keypad-icon-log-e.svg +36 -0
  163. package/tools/svg-to-react/icons/math-keypad-icon-log.svg +38 -0
  164. package/tools/svg-to-react/icons/math-keypad-icon-multiplication-cross.svg +40 -0
  165. package/tools/svg-to-react/icons/math-keypad-icon-multiplication-dot.svg +38 -0
  166. package/tools/svg-to-react/icons/math-keypad-icon-percent.svg +42 -0
  167. package/tools/svg-to-react/icons/math-keypad-icon-radical-2.svg +36 -0
  168. package/tools/svg-to-react/icons/math-keypad-icon-radical-3.svg +38 -0
  169. package/tools/svg-to-react/icons/math-keypad-icon-radical.svg +38 -0
  170. package/tools/svg-to-react/icons/math-keypad-icon-radix-character.svg +32 -0
  171. package/tools/svg-to-react/icons/math-keypad-icon-sin.svg +38 -0
  172. package/tools/svg-to-react/icons/math-keypad-icon-subtraction.svg +32 -0
  173. package/tools/svg-to-react/icons/math-keypad-icon-tan.svg +38 -0
  174. package/tools/svg-to-react/symbol_map.py +41 -0
  175. package/LICENSE.txt +0 -21
  176. package/build/math-input.js +0 -1
@@ -0,0 +1,76 @@
1
+ /**
2
+ * A system for tracking gesture velocity in a single dimension.
3
+ *
4
+ * Velocity is computed by smoothing linearly over the gestures that have
5
+ * occurred in the last 100 milliseconds.
6
+ */
7
+
8
+ import now from "performance-now";
9
+
10
+ class VelocityTracker {
11
+ constructor(options) {
12
+ this.options = {
13
+ velocityTimeout: 100,
14
+ ...options,
15
+ };
16
+ this._events = [];
17
+ }
18
+
19
+ /**
20
+ * Pushes an event with the given displacement onto the event buffer,
21
+ * associating it with a timestamp. Note that, as this method computes the
22
+ * timestamp for the event at calltime, it should be called immediately
23
+ * after the event occurs.
24
+ *
25
+ * @param {number} x - the cumulative displacement of the event
26
+ */
27
+ push(x) {
28
+ this._events.push({
29
+ x,
30
+ t: now(),
31
+ });
32
+ }
33
+
34
+ /**
35
+ * Compute the velocity with respect to the events that have been tracked
36
+ * by the system. Velocity is computed by smoothing linearly over recent
37
+ * displacement values.
38
+ *
39
+ * Note that, for performance reasons, a call to `getVelocity` will clear
40
+ * out the event buffer. As such, repeated calls will not return the same
41
+ * value (in particular, a second call in quick succession will return 0).
42
+ *
43
+ * @returns {number} the velocity associated with the tracker
44
+ */
45
+ getVelocity() {
46
+ const events = this._getEvents();
47
+
48
+ if (events.length < 2) {
49
+ return 0;
50
+ } else {
51
+ const current = events[events.length - 1];
52
+ const first = events[0];
53
+ const dt = current.t - first.t;
54
+ return (current.x - first.x) / dt;
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Filter the tracked events to exclude any events that occurred too far in
60
+ * the past, and reset the event buffer.
61
+ *
62
+ * @returns {number[]} an array of displacements corresponding to events
63
+ * that occurred in the past `velocityTimeout`
64
+ * milliseconds
65
+ */
66
+ _getEvents() {
67
+ const threshold = now() - this.options.velocityTimeout;
68
+ const recentEvents = this._events.filter((event) => {
69
+ return event.t > threshold;
70
+ });
71
+ this._events = [];
72
+ return recentEvents;
73
+ }
74
+ }
75
+
76
+ export default VelocityTracker;
@@ -0,0 +1,9 @@
1
+ // @flow
2
+ /**
3
+ * This file contains all of the z-index values used throughout the math-input
4
+ * component and its children.
5
+ */
6
+
7
+ export const popover = 1;
8
+ export const echo = 2;
9
+ export const keypad = 1060;
package/src/consts.js ADDED
@@ -0,0 +1,74 @@
1
+ // @flow
2
+ /**
3
+ * Constants that are shared between multiple files.
4
+ */
5
+
6
+ export const KeypadTypes = {
7
+ FRACTION: "FRACTION",
8
+ EXPRESSION: "EXPRESSION",
9
+ };
10
+
11
+ export const KeyTypes = {
12
+ EMPTY: "EMPTY",
13
+ // For numerals, variables, and any other characters that themselves
14
+ // compose 'values'.
15
+ VALUE: "VALUE",
16
+ // For buttons that insert or adjust math in an input.
17
+ OPERATOR: "OPERATOR",
18
+ // For buttons that move the cursor in an input (including via
19
+ // deletion).
20
+ INPUT_NAVIGATION: "INPUT_NAVIGATION",
21
+ // For buttons that modify the broader keypad state (e.g., by changing
22
+ // the visible pane).
23
+ KEYPAD_NAVIGATION: "KEYPAD_NAVIGATION",
24
+ // For buttons that house multiple buttons and have no action
25
+ // themselves.
26
+ MANY: "MANY",
27
+ // For the echo animation that appears on press.
28
+ ECHO: "ECHO",
29
+ };
30
+
31
+ export const DeviceOrientations = {
32
+ LANDSCAPE: "LANDSCAPE",
33
+ PORTRAIT: "PORTRAIT",
34
+ };
35
+
36
+ export const DeviceTypes = {
37
+ PHONE: "PHONE",
38
+ TABLET: "TABLET",
39
+ };
40
+
41
+ export const LayoutModes = {
42
+ FULLSCREEN: "FULLSCREEN",
43
+ COMPACT: "COMPACT",
44
+ };
45
+
46
+ export const BorderDirections = {
47
+ LEFT: ("LEFT": "LEFT"),
48
+ BOTTOM: ("BOTTOM": "BOTTOM"),
49
+ };
50
+ export const BorderStyles: {|
51
+ [style: string]: $ReadOnlyArray<$Values<typeof BorderDirections>>,
52
+ |} = {
53
+ LEFT: ["LEFT"],
54
+ BOTTOM: ["BOTTOM"],
55
+ ALL: ["LEFT", "BOTTOM"],
56
+ NONE: [],
57
+ };
58
+
59
+ export const IconTypes = {
60
+ MATH: "MATH",
61
+ SVG: "SVG",
62
+ TEXT: "TEXT",
63
+ };
64
+
65
+ export const DecimalSeparators = {
66
+ COMMA: "COMMA",
67
+ PERIOD: "PERIOD",
68
+ };
69
+
70
+ export const EchoAnimationTypes = {
71
+ SLIDE_AND_FADE: "SLIDE_AND_FADE",
72
+ FADE_ONLY: "FADE_ONLY",
73
+ LONG_FADE_ONLY: "LONG_FADE_ONLY",
74
+ };
@@ -0,0 +1,349 @@
1
+ // @flow
2
+ /**
3
+ * This file contains configuration settings for the buttons in the keypad.
4
+ */
5
+ import * as i18n from "@khanacademy/wonder-blocks-i18n";
6
+
7
+ import {DecimalSeparators, IconTypes, KeyTypes} from "../consts.js";
8
+ import {decimalSeparator} from "../utils.js";
9
+
10
+ import Keys from "./keys.js";
11
+
12
+ export type KeyConfig = {
13
+ id: string,
14
+ type: string,
15
+ ariaLabel: string,
16
+ };
17
+ const KeyConfigs: Object = {
18
+ // Basic math keys.
19
+ [Keys.PLUS]: {
20
+ type: KeyTypes.OPERATOR,
21
+ // I18N: A label for a plus sign.
22
+ ariaLabel: i18n._("Plus"),
23
+ },
24
+ [Keys.MINUS]: {
25
+ type: KeyTypes.OPERATOR,
26
+ // I18N: A label for a minus sign.
27
+ ariaLabel: i18n._("Minus"),
28
+ },
29
+ [Keys.NEGATIVE]: {
30
+ type: KeyTypes.VALUE,
31
+ // I18N: A label for a minus sign.
32
+ ariaLabel: i18n._("Negative"),
33
+ },
34
+ [Keys.TIMES]: {
35
+ type: KeyTypes.OPERATOR,
36
+ // I18N: A label for a multiplication sign (represented with an 'x').
37
+ ariaLabel: i18n._("Multiply"),
38
+ },
39
+ [Keys.DIVIDE]: {
40
+ type: KeyTypes.OPERATOR,
41
+ // I18N: A label for a division sign.
42
+ ariaLabel: i18n._("Divide"),
43
+ },
44
+ [Keys.DECIMAL]: {
45
+ type: KeyTypes.VALUE,
46
+ // I18N: A label for a decimal symbol.
47
+ ariaLabel: i18n._("Decimal"),
48
+ icon:
49
+ decimalSeparator === DecimalSeparators.COMMA
50
+ ? {
51
+ // TODO(charlie): Get an SVG icon for the comma, or verify with
52
+ // design that the text-rendered version is acceptable.
53
+ type: IconTypes.TEXT,
54
+ data: ",",
55
+ }
56
+ : {
57
+ type: IconTypes.SVG,
58
+ data: Keys.PERIOD,
59
+ },
60
+ },
61
+ [Keys.PERCENT]: {
62
+ type: KeyTypes.OPERATOR,
63
+ // I18N: A label for a percent sign.
64
+ ariaLabel: i18n._("Percent"),
65
+ },
66
+ [Keys.CDOT]: {
67
+ type: KeyTypes.OPERATOR,
68
+ // I18N: A label for a multiplication sign (represented as a dot).
69
+ ariaLabel: i18n._("Multiply"),
70
+ },
71
+ [Keys.EQUAL]: {
72
+ type: KeyTypes.OPERATOR,
73
+ ariaLabel: i18n._("Equals sign"),
74
+ },
75
+ [Keys.NEQ]: {
76
+ type: KeyTypes.OPERATOR,
77
+ ariaLabel: i18n._("Not-equals sign"),
78
+ },
79
+ [Keys.GT]: {
80
+ type: KeyTypes.OPERATOR,
81
+ // I18N: A label for a 'greater than' sign (represented as '>').
82
+ ariaLabel: i18n._("Greater than sign"),
83
+ },
84
+ [Keys.LT]: {
85
+ type: KeyTypes.OPERATOR,
86
+ // I18N: A label for a 'less than' sign (represented as '<').
87
+ ariaLabel: i18n._("Less than sign"),
88
+ },
89
+ [Keys.GEQ]: {
90
+ type: KeyTypes.OPERATOR,
91
+ ariaLabel: i18n._("Greater than or equal to sign"),
92
+ },
93
+ [Keys.LEQ]: {
94
+ type: KeyTypes.OPERATOR,
95
+ ariaLabel: i18n._("Less than or equal to sign"),
96
+ },
97
+ // mobile native
98
+ [Keys.FRAC_INCLUSIVE]: {
99
+ type: KeyTypes.OPERATOR,
100
+ // I18N: A label for a button that creates a new fraction and puts the
101
+ // current expression in the numerator of that fraction.
102
+ ariaLabel: i18n._("Fraction, with current expression in numerator"),
103
+ },
104
+ // mobile native
105
+ [Keys.FRAC_EXCLUSIVE]: {
106
+ type: KeyTypes.OPERATOR,
107
+ // I18N: A label for a button that creates a new fraction next to the
108
+ // cursor.
109
+ ariaLabel: i18n._("Fraction, excluding the current expression"),
110
+ },
111
+ // mobile web
112
+ [Keys.FRAC]: {
113
+ type: KeyTypes.OPERATOR,
114
+ // I18N: A label for a button that creates a new fraction next to the
115
+ // cursor.
116
+ ariaLabel: i18n._("Fraction, excluding the current expression"),
117
+ },
118
+ [Keys.EXP]: {
119
+ type: KeyTypes.OPERATOR,
120
+ // I18N: A label for a button that will allow the user to input a custom
121
+ // exponent.
122
+ ariaLabel: i18n._("Custom exponent"),
123
+ },
124
+ [Keys.EXP_2]: {
125
+ type: KeyTypes.OPERATOR,
126
+ // I18N: A label for a button that will square (take to the second
127
+ // power) some math.
128
+ ariaLabel: i18n._("Square"),
129
+ },
130
+ [Keys.EXP_3]: {
131
+ type: KeyTypes.OPERATOR,
132
+ // I18N: A label for a button that will cube (take to the third power)
133
+ // some math.
134
+ ariaLabel: i18n._("Cube"),
135
+ },
136
+ [Keys.SQRT]: {
137
+ type: KeyTypes.OPERATOR,
138
+ ariaLabel: i18n._("Square root"),
139
+ },
140
+ [Keys.CUBE_ROOT]: {
141
+ type: KeyTypes.OPERATOR,
142
+ ariaLabel: i18n._("Cube root"),
143
+ },
144
+ [Keys.RADICAL]: {
145
+ type: KeyTypes.OPERATOR,
146
+ ariaLabel: i18n._("Radical with custom root"),
147
+ },
148
+ [Keys.LEFT_PAREN]: {
149
+ type: KeyTypes.OPERATOR,
150
+ ariaLabel: i18n._("Left parenthesis"),
151
+ },
152
+ [Keys.RIGHT_PAREN]: {
153
+ type: KeyTypes.OPERATOR,
154
+ ariaLabel: i18n._("Right parenthesis"),
155
+ },
156
+ [Keys.LN]: {
157
+ type: KeyTypes.OPERATOR,
158
+ ariaLabel: i18n._("Natural logarithm"),
159
+ },
160
+ [Keys.LOG]: {
161
+ type: KeyTypes.OPERATOR,
162
+ ariaLabel: i18n._("Logarithm with base 10"),
163
+ },
164
+ [Keys.LOG_N]: {
165
+ type: KeyTypes.OPERATOR,
166
+ ariaLabel: i18n._("Logarithm with custom base"),
167
+ },
168
+ [Keys.SIN]: {
169
+ type: KeyTypes.OPERATOR,
170
+ ariaLabel: i18n._("Sine"),
171
+ },
172
+ [Keys.COS]: {
173
+ type: KeyTypes.OPERATOR,
174
+ ariaLabel: i18n._("Cosine"),
175
+ },
176
+ [Keys.TAN]: {
177
+ type: KeyTypes.OPERATOR,
178
+ ariaLabel: i18n._("Tangent"),
179
+ },
180
+ [Keys.PI]: {
181
+ type: KeyTypes.VALUE,
182
+ ariaLabel: i18n._("Pi"),
183
+ icon: {
184
+ type: IconTypes.MATH,
185
+ data: "\\pi",
186
+ },
187
+ },
188
+ [Keys.THETA]: {
189
+ type: KeyTypes.VALUE,
190
+ ariaLabel: i18n._("Theta"),
191
+ icon: {
192
+ type: IconTypes.MATH,
193
+ data: "\\theta",
194
+ },
195
+ },
196
+ [Keys.NOOP]: {
197
+ type: KeyTypes.EMPTY,
198
+ },
199
+
200
+ // Input navigation keys.
201
+ [Keys.UP]: {
202
+ type: KeyTypes.INPUT_NAVIGATION,
203
+ ariaLabel: i18n._("Up arrow"),
204
+ },
205
+ [Keys.RIGHT]: {
206
+ type: KeyTypes.INPUT_NAVIGATION,
207
+ ariaLabel: i18n._("Right arrow"),
208
+ },
209
+ [Keys.DOWN]: {
210
+ type: KeyTypes.INPUT_NAVIGATION,
211
+ ariaLabel: i18n._("Down arrow"),
212
+ },
213
+ [Keys.LEFT]: {
214
+ type: KeyTypes.INPUT_NAVIGATION,
215
+ ariaLabel: i18n._("Left arrow"),
216
+ },
217
+ [Keys.JUMP_OUT_PARENTHESES]: {
218
+ type: KeyTypes.INPUT_NAVIGATION,
219
+ ariaLabel: i18n._("Navigate right out of a set of parentheses"),
220
+ },
221
+ [Keys.JUMP_OUT_EXPONENT]: {
222
+ type: KeyTypes.INPUT_NAVIGATION,
223
+ ariaLabel: i18n._("Navigate right out of an exponent"),
224
+ },
225
+ [Keys.JUMP_OUT_BASE]: {
226
+ type: KeyTypes.INPUT_NAVIGATION,
227
+ ariaLabel: i18n._("Navigate right out of a base"),
228
+ },
229
+ [Keys.JUMP_INTO_NUMERATOR]: {
230
+ type: KeyTypes.INPUT_NAVIGATION,
231
+ ariaLabel: i18n._("Navigate right into the numerator of a fraction"),
232
+ },
233
+ [Keys.JUMP_OUT_NUMERATOR]: {
234
+ type: KeyTypes.INPUT_NAVIGATION,
235
+ ariaLabel: i18n._(
236
+ "Navigate right out of the numerator and into the denominator",
237
+ ),
238
+ },
239
+ [Keys.JUMP_OUT_DENOMINATOR]: {
240
+ type: KeyTypes.INPUT_NAVIGATION,
241
+ ariaLabel: i18n._(
242
+ "Navigate right out of the denominator of a fraction",
243
+ ),
244
+ },
245
+ [Keys.BACKSPACE]: {
246
+ type: KeyTypes.INPUT_NAVIGATION,
247
+ // I18N: A label for a button that will delete some input.
248
+ ariaLabel: i18n._("Delete"),
249
+ },
250
+
251
+ // Keypad navigation keys.
252
+ [Keys.DISMISS]: {
253
+ type: KeyTypes.KEYPAD_NAVIGATION,
254
+ // I18N: A label for a button that will dismiss/hide a keypad.
255
+ ariaLabel: i18n._("Dismiss"),
256
+ },
257
+ };
258
+
259
+ // Add in any multi-function buttons. By default, these keys will mix in any
260
+ // configuration settings from their default child key (i.e., the first key in
261
+ // the `childKeyIds` array).
262
+ // TODO(charlie): Make the multi-function button's long-press interaction
263
+ // accessible.
264
+ // NOTE(kevinb): This is only used in the mobile native app.
265
+ KeyConfigs[Keys.FRAC_MULTI] = {
266
+ childKeyIds: [Keys.FRAC_INCLUSIVE, Keys.FRAC_EXCLUSIVE],
267
+ };
268
+
269
+ // TODO(charlie): Use the numeral color for the 'Many' key.
270
+ KeyConfigs[Keys.MANY] = {
271
+ type: KeyTypes.MANY,
272
+ // childKeyIds will be configured by the client.
273
+ };
274
+
275
+ // Add in every numeral.
276
+ const NUMBERS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
277
+ for (const num of NUMBERS) {
278
+ // TODO(charlie): Consider removing the SVG icons that we have for the
279
+ // numeral keys. They can be rendered just as easily with text (though that
280
+ // would mean that we'd be using text beyond the variable key).
281
+ const textRepresentation = `${num}`;
282
+ KeyConfigs[`NUM_${num}`] = {
283
+ type: KeyTypes.VALUE,
284
+ ariaLabel: textRepresentation,
285
+ icon: {
286
+ type: IconTypes.TEXT,
287
+ data: textRepresentation,
288
+ },
289
+ };
290
+ }
291
+
292
+ // Add in every variable.
293
+ const LETTERS = [
294
+ "A",
295
+ "B",
296
+ "C",
297
+ "D",
298
+ "E",
299
+ "F",
300
+ "G",
301
+ "H",
302
+ "I",
303
+ "J",
304
+ "K",
305
+ "L",
306
+ "M",
307
+ "N",
308
+ "O",
309
+ "P",
310
+ "Q",
311
+ "R",
312
+ "S",
313
+ "T",
314
+ "U",
315
+ "V",
316
+ "W",
317
+ "X",
318
+ "Y",
319
+ "Z",
320
+ ];
321
+ for (const letter of LETTERS) {
322
+ const lowerCaseVariable = letter.toLowerCase();
323
+ const upperCaseVariable = letter.toUpperCase();
324
+
325
+ for (const textRepresentation of [lowerCaseVariable, upperCaseVariable]) {
326
+ KeyConfigs[textRepresentation] = {
327
+ type: KeyTypes.VALUE,
328
+ ariaLabel: textRepresentation,
329
+ icon: {
330
+ type: IconTypes.MATH,
331
+ data: textRepresentation,
332
+ },
333
+ };
334
+ }
335
+ }
336
+
337
+ for (const key of Object.keys(KeyConfigs)) {
338
+ KeyConfigs[key] = {
339
+ id: key,
340
+ // Default to an SVG icon indexed by the key name.
341
+ icon: {
342
+ type: IconTypes.SVG,
343
+ data: key,
344
+ },
345
+ ...KeyConfigs[key],
346
+ };
347
+ }
348
+
349
+ export default KeyConfigs;
@@ -0,0 +1,72 @@
1
+ // @flow
2
+ /**
3
+ * This file contains constants for keypad buttons that aren't single
4
+ * alphanumeric characters.
5
+ */
6
+
7
+ // TODO(charlie): There's duplication between this file and key-configs.js.
8
+ // We should clean it up by removing this file and requiring clients to use the
9
+ // `id` field on the key configurations.
10
+ const Keys = {
11
+ PLUS: "PLUS",
12
+ MINUS: "MINUS",
13
+ NEGATIVE: "NEGATIVE",
14
+ TIMES: "TIMES",
15
+ DIVIDE: "DIVIDE",
16
+ DECIMAL: "DECIMAL",
17
+ PERIOD: "PERIOD",
18
+ PERCENT: "PERCENT",
19
+ CDOT: "CDOT",
20
+ EQUAL: "EQUAL",
21
+ NEQ: "NEQ",
22
+ GT: "GT",
23
+ LT: "LT",
24
+ GEQ: "GEQ",
25
+ LEQ: "LEQ",
26
+ FRAC_INCLUSIVE: "FRAC_INCLUSIVE", // mobile native only
27
+ FRAC_EXCLUSIVE: "FRAC_EXCLUSIVE", // mobile native only
28
+ FRAC: "FRAC",
29
+ EXP: "EXP",
30
+ EXP_2: "EXP_2",
31
+ EXP_3: "EXP_3",
32
+ SQRT: "SQRT",
33
+ CUBE_ROOT: "CUBE_ROOT",
34
+ RADICAL: "RADICAL",
35
+ LEFT_PAREN: "LEFT_PAREN",
36
+ RIGHT_PAREN: "RIGHT_PAREN",
37
+ LN: "LN",
38
+ LOG: "LOG",
39
+ LOG_N: "LOG_N",
40
+ SIN: "SIN",
41
+ COS: "COS",
42
+ TAN: "TAN",
43
+
44
+ // TODO(charlie): Add in additional Greek letters.
45
+ PI: "PI",
46
+ THETA: "THETA",
47
+
48
+ UP: "UP",
49
+ RIGHT: "RIGHT",
50
+ DOWN: "DOWN",
51
+ LEFT: "LEFT",
52
+ BACKSPACE: "BACKSPACE",
53
+ DISMISS: "DISMISS",
54
+
55
+ JUMP_OUT_PARENTHESES: "JUMP_OUT_PARENTHESES",
56
+ JUMP_OUT_EXPONENT: "JUMP_OUT_EXPONENT",
57
+ JUMP_OUT_BASE: "JUMP_OUT_BASE",
58
+ JUMP_INTO_NUMERATOR: "JUMP_INTO_NUMERATOR",
59
+ JUMP_OUT_NUMERATOR: "JUMP_OUT_NUMERATOR",
60
+ JUMP_OUT_DENOMINATOR: "JUMP_OUT_DENOMINATOR",
61
+
62
+ NOOP: "NOOP",
63
+
64
+ // Multi-functional keys.
65
+ FRAC_MULTI: "FRAC_MULTI", // mobile native only
66
+
67
+ // A custom key that captures an arbitrary number of symbols but has no
68
+ // 'default' symbol or action.
69
+ MANY: "MANY",
70
+ };
71
+
72
+ export default Keys;
package/src/demo.js ADDED
@@ -0,0 +1,8 @@
1
+ import * as React from "react";
2
+ import ReactDOM from "react-dom";
3
+
4
+ import App from "./components/app.js";
5
+
6
+ import "../less/main.less";
7
+
8
+ ReactDOM.render(<App />, document.getElementById("root"));
@@ -0,0 +1,12 @@
1
+ // @flow
2
+ /**
3
+ * This file exports components View and Text which wrap <div> and <span>.
4
+ * We do this so that someday in the future we can use these components with
5
+ * React Native.
6
+ *
7
+ * Inspired by https://github.com/necolas/react-native-web. We use aphrodite
8
+ * StyleSheets instead.
9
+ */
10
+
11
+ export {default as Text} from "./text.js";
12
+ export {default as View} from "./view.js";
@@ -0,0 +1,56 @@
1
+ // @flow
2
+
3
+ import {StyleSheet, css} from "aphrodite";
4
+ import * as React from "react";
5
+
6
+ import type {CSSProperties} from "aphrodite";
7
+
8
+ type Props = {|
9
+ children: React.Node,
10
+ // The `dynamicStyle` prop is provided for animating dynamic
11
+ // properties, as creating Aphrodite StyleSheets in animation loops is
12
+ // expensive. `dynamicStyle` should be a raw style object, rather than
13
+ // a StyleSheet.
14
+ dynamicStyle?: CSSProperties,
15
+ numberOfLines?: number,
16
+ style?: CSSProperties,
17
+ |};
18
+
19
+ class Text extends React.Component<Props> {
20
+ render(): React.Element<"span"> {
21
+ const {numberOfLines, style} = this.props;
22
+
23
+ const className = css(
24
+ styles.initial,
25
+ ...(Array.isArray(style) ? style : [style]),
26
+ numberOfLines === 1 && styles.singleLineStyle,
27
+ );
28
+
29
+ return (
30
+ <span className={className} style={this.props.dynamicStyle}>
31
+ {this.props.children}
32
+ </span>
33
+ );
34
+ }
35
+ }
36
+
37
+ // https://github.com/necolas/react-native-web/blob/master/src/components/Text/index.js
38
+ const styles = StyleSheet.create({
39
+ initial: {
40
+ color: "inherit",
41
+ display: "inline",
42
+ font: "inherit",
43
+ margin: 0,
44
+ padding: 0,
45
+ textDecorationLine: "none",
46
+ wordWrap: "break-word",
47
+ },
48
+ singleLineStyle: {
49
+ maxWidth: "100%",
50
+ overflow: "hidden",
51
+ textOverflow: "ellipsis",
52
+ whiteSpace: "nowrap",
53
+ },
54
+ });
55
+
56
+ export default Text;