@pie-lib/math-input 8.1.1-next.3 → 8.1.1-next.57

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 (185) hide show
  1. package/CHANGELOG.json +17 -0
  2. package/CHANGELOG.md +1172 -0
  3. package/LICENSE.md +5 -0
  4. package/lib/horizontal-keypad.js +96 -0
  5. package/lib/horizontal-keypad.js.map +1 -0
  6. package/lib/index.js +69 -0
  7. package/lib/index.js.map +1 -0
  8. package/lib/keypad/index.js +413 -0
  9. package/lib/keypad/index.js.map +1 -0
  10. package/lib/keypad/keys-layout.js +22 -0
  11. package/lib/keypad/keys-layout.js.map +1 -0
  12. package/lib/keys/basic-operators.js +33 -0
  13. package/lib/keys/basic-operators.js.map +1 -0
  14. package/lib/keys/chars.js +12 -0
  15. package/lib/keys/chars.js.map +1 -0
  16. package/lib/keys/comparison.js +39 -0
  17. package/lib/keys/comparison.js.map +1 -0
  18. package/lib/keys/constants.js +37 -0
  19. package/lib/keys/constants.js.map +1 -0
  20. package/lib/keys/digits.js +46 -0
  21. package/lib/keys/digits.js.map +1 -0
  22. package/lib/keys/edit.js +14 -0
  23. package/lib/keys/edit.js.map +1 -0
  24. package/lib/keys/exponent.js +30 -0
  25. package/lib/keys/exponent.js.map +1 -0
  26. package/lib/keys/fractions.js +29 -0
  27. package/lib/keys/fractions.js.map +1 -0
  28. package/lib/keys/geometry.js +140 -0
  29. package/lib/keys/geometry.js.map +1 -0
  30. package/lib/keys/grades.js +259 -0
  31. package/lib/keys/grades.js.map +1 -0
  32. package/lib/keys/index.js +35 -0
  33. package/lib/keys/index.js.map +1 -0
  34. package/lib/keys/log.js +27 -0
  35. package/lib/keys/log.js.map +1 -0
  36. package/lib/keys/logic.js +19 -0
  37. package/lib/keys/logic.js.map +1 -0
  38. package/lib/keys/matrices.js +19 -0
  39. package/lib/keys/matrices.js.map +1 -0
  40. package/lib/keys/misc.js +62 -0
  41. package/lib/keys/misc.js.map +1 -0
  42. package/lib/keys/navigation.js +20 -0
  43. package/lib/keys/navigation.js.map +1 -0
  44. package/lib/keys/operators.js +15 -0
  45. package/lib/keys/operators.js.map +1 -0
  46. package/lib/keys/statistics.js +40 -0
  47. package/lib/keys/statistics.js.map +1 -0
  48. package/lib/keys/sub-sup.js +19 -0
  49. package/lib/keys/sub-sup.js.map +1 -0
  50. package/lib/keys/trigonometry.js +45 -0
  51. package/lib/keys/trigonometry.js.map +1 -0
  52. package/lib/keys/utils.js +87 -0
  53. package/lib/keys/utils.js.map +1 -0
  54. package/lib/keys/vars.js +24 -0
  55. package/lib/keys/vars.js.map +1 -0
  56. package/lib/math-input.js +141 -0
  57. package/lib/math-input.js.map +1 -0
  58. package/lib/mq/common-mq-styles.js +102 -0
  59. package/lib/mq/common-mq-styles.js.map +1 -0
  60. package/lib/mq/custom-elements.js +20 -0
  61. package/lib/mq/custom-elements.js.map +1 -0
  62. package/lib/mq/index.js +28 -0
  63. package/lib/mq/index.js.map +1 -0
  64. package/lib/mq/input.js +186 -0
  65. package/lib/mq/input.js.map +1 -0
  66. package/lib/mq/mathquill-instance.js +52 -0
  67. package/lib/mq/mathquill-instance.js.map +1 -0
  68. package/lib/mq/static.js +301 -0
  69. package/lib/mq/static.js.map +1 -0
  70. package/lib/updateSpans.js +19 -0
  71. package/lib/updateSpans.js.map +1 -0
  72. package/package.json +18 -33
  73. package/src/__tests__/horizontal-keypad.test.jsx +463 -0
  74. package/src/__tests__/index.test.js +247 -0
  75. package/src/__tests__/math-input-test.jsx +45 -0
  76. package/src/__tests__/updateSpans.test.js +297 -0
  77. package/src/horizontal-keypad.jsx +69 -0
  78. package/src/index.jsx +28 -0
  79. package/src/keypad/__tests__/index.test.jsx +25 -0
  80. package/src/keypad/__tests__/keys-layout.test.js +14 -0
  81. package/src/keypad/index.jsx +439 -0
  82. package/src/keypad/keys-layout.js +16 -0
  83. package/src/keys/__tests__/utils.test.js +57 -0
  84. package/src/keys/basic-operators.js +32 -0
  85. package/src/keys/chars.js +5 -0
  86. package/src/keys/comparison.js +28 -0
  87. package/src/keys/constants.js +35 -0
  88. package/src/keys/digits.js +40 -0
  89. package/src/keys/edit.js +3 -0
  90. package/src/keys/exponent.js +28 -0
  91. package/src/keys/fractions.js +26 -0
  92. package/src/keys/geometry.js +144 -0
  93. package/src/keys/grades.js +367 -0
  94. package/src/keys/index.js +20 -0
  95. package/src/keys/log.js +22 -0
  96. package/src/keys/logic.js +15 -0
  97. package/src/keys/matrices.js +15 -0
  98. package/src/keys/misc.js +65 -0
  99. package/src/keys/navigation.js +8 -0
  100. package/src/keys/operators.js +10 -0
  101. package/src/keys/statistics.js +38 -0
  102. package/src/keys/sub-sup.js +15 -0
  103. package/src/keys/trigonometry.js +15 -0
  104. package/src/keys/utils.js +66 -0
  105. package/src/keys/vars.js +19 -0
  106. package/src/math-input.jsx +119 -0
  107. package/src/mq/__tests__/custom-elements.test.js +342 -0
  108. package/src/mq/__tests__/input.test.jsx +40 -0
  109. package/src/mq/__tests__/mathquill-instance.test.js +67 -0
  110. package/src/mq/__tests__/static.test.jsx +33 -0
  111. package/src/mq/common-mq-styles.js +109 -0
  112. package/src/mq/custom-elements.js +11 -0
  113. package/src/mq/index.js +5 -0
  114. package/src/mq/input.jsx +166 -0
  115. package/src/mq/mathquill-instance.js +45 -0
  116. package/src/mq/static.jsx +290 -0
  117. package/src/updateSpans.js +15 -0
  118. package/dist/_virtual/_rolldown/runtime.js +0 -11
  119. package/dist/horizontal-keypad.d.ts +0 -31
  120. package/dist/horizontal-keypad.js +0 -57
  121. package/dist/index.d.ts +0 -18
  122. package/dist/index.js +0 -19
  123. package/dist/keypad/accessible-keypad.d.ts +0 -37
  124. package/dist/keypad/accessible-keypad.js +0 -614
  125. package/dist/keypad/index.d.ts +0 -2
  126. package/dist/keypad/keys-layout.d.ts +0 -15
  127. package/dist/keypad/keys-layout.js +0 -5
  128. package/dist/keypad/model.d.ts +0 -28
  129. package/dist/keypad/model.js +0 -4
  130. package/dist/keys/basic-operators.d.ts +0 -13
  131. package/dist/keys/basic-operators.js +0 -30
  132. package/dist/keys/chars.d.ts +0 -13
  133. package/dist/keys/comparison.d.ts +0 -12
  134. package/dist/keys/comparison.js +0 -32
  135. package/dist/keys/constants.d.ts +0 -12
  136. package/dist/keys/constants.js +0 -35
  137. package/dist/keys/digits.d.ts +0 -23
  138. package/dist/keys/digits.js +0 -34
  139. package/dist/keys/edit.d.ts +0 -14
  140. package/dist/keys/edit.js +0 -9
  141. package/dist/keys/exponent.d.ts +0 -12
  142. package/dist/keys/exponent.js +0 -28
  143. package/dist/keys/fractions.d.ts +0 -11
  144. package/dist/keys/fractions.js +0 -27
  145. package/dist/keys/geometry.d.ts +0 -31
  146. package/dist/keys/geometry.js +0 -127
  147. package/dist/keys/grades.d.ts +0 -17
  148. package/dist/keys/grades.js +0 -414
  149. package/dist/keys/index.d.ts +0 -14
  150. package/dist/keys/index.js +0 -50
  151. package/dist/keys/log.d.ts +0 -11
  152. package/dist/keys/log.js +0 -25
  153. package/dist/keys/logic.d.ts +0 -10
  154. package/dist/keys/logic.js +0 -13
  155. package/dist/keys/matrices.d.ts +0 -10
  156. package/dist/keys/matrices.js +0 -17
  157. package/dist/keys/misc.d.ts +0 -18
  158. package/dist/keys/misc.js +0 -60
  159. package/dist/keys/navigation.d.ts +0 -10
  160. package/dist/keys/navigation.js +0 -13
  161. package/dist/keys/operators.d.ts +0 -9
  162. package/dist/keys/operators.js +0 -11
  163. package/dist/keys/statistics.d.ts +0 -13
  164. package/dist/keys/statistics.js +0 -38
  165. package/dist/keys/sub-sup.d.ts +0 -10
  166. package/dist/keys/sub-sup.js +0 -17
  167. package/dist/keys/trigonometry.d.ts +0 -14
  168. package/dist/keys/trigonometry.js +0 -43
  169. package/dist/keys/utils.d.ts +0 -13
  170. package/dist/keys/utils.js +0 -24
  171. package/dist/keys/vars.d.ts +0 -11
  172. package/dist/keys/vars.js +0 -22
  173. package/dist/math-input.d.ts +0 -30
  174. package/dist/mq/common-mq-styles.d.ts +0 -225
  175. package/dist/mq/common-mq-styles.js +0 -54
  176. package/dist/mq/custom-elements.d.ts +0 -10
  177. package/dist/mq/custom-elements.js +0 -10
  178. package/dist/mq/index.d.ts +0 -12
  179. package/dist/mq/index.js +0 -12
  180. package/dist/mq/input.d.ts +0 -35
  181. package/dist/mq/input.js +0 -83
  182. package/dist/mq/static.d.ts +0 -43
  183. package/dist/mq/static.js +0 -142
  184. package/dist/updateSpans.d.ts +0 -10
  185. package/dist/updateSpans.js +0 -9
@@ -0,0 +1,25 @@
1
+ import * as React from 'react';
2
+ import { render } from '@testing-library/react';
3
+ import { KeyPad } from '../index';
4
+
5
+ describe('Keypad', () => {
6
+ const onChange = jest.fn();
7
+ const defaultProps = {
8
+ classes: {},
9
+ className: 'className',
10
+ onChange,
11
+ onPress: jest.fn(),
12
+ };
13
+
14
+ beforeEach(() => {
15
+ onChange.mockClear();
16
+ });
17
+
18
+ describe('rendering', () => {
19
+ it('renders with default props', () => {
20
+ const { container } = render(<KeyPad {...defaultProps} />);
21
+ expect(container.firstChild).toBeInTheDocument();
22
+ expect(container.firstChild).toHaveClass('className');
23
+ });
24
+ });
25
+ });
@@ -0,0 +1,14 @@
1
+ import { sortKeys } from '../keys-layout';
2
+
3
+ describe('keys-layout', () => {
4
+ describe('sortKeys', () => {
5
+ it('pads the rows', () => {
6
+ const result = sortKeys([[1, 2, 3]]);
7
+ expect(result).toEqual([
8
+ [1, undefined, undefined, undefined, undefined],
9
+ [2, undefined, undefined, undefined, undefined],
10
+ [3, undefined, undefined, undefined, undefined],
11
+ ]);
12
+ });
13
+ });
14
+ });
@@ -0,0 +1,439 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import Button from '@mui/material/Button';
4
+ import IconButton from '@mui/material/IconButton';
5
+ import { styled } from '@mui/material/styles';
6
+ import debug from 'debug';
7
+ import { flatten } from 'lodash-es';
8
+ import { color } from '@pie-lib/render-ui';
9
+
10
+ import * as mq from '../mq';
11
+ import { baseSet } from '../keys';
12
+ import { commonMqKeyboardStyles } from '../mq/common-mq-styles';
13
+ import { sortKeys } from './keys-layout';
14
+
15
+ const log = debug('pie-lib:math-inline:keypad');
16
+
17
+ const LatexButtonContent = styled(mq.Static)(({ latex }) => {
18
+ const baseStyles = {
19
+ pointerEvents: 'none',
20
+ textTransform: 'none !important',
21
+ color: color.text(),
22
+ '& .mq-scaled.mq-sqrt-prefix': {
23
+ transform: 'scale(1, 0.9) !important',
24
+ },
25
+ '& .mq-sup-only .mq-sup': {
26
+ marginBottom: '0.9px !important',
27
+ },
28
+ '& .mq-empty': {
29
+ backgroundColor: `${color.keypadEmptyPlaceholder()} !important`,
30
+ },
31
+ '& .mq-overline .mq-overline-inner': {
32
+ borderTop: '2px solid black',
33
+ },
34
+ '& .mq-non-leaf.mq-overline': {
35
+ borderTop: 'none !important',
36
+ },
37
+ '& .mq-overarrow': {
38
+ width: '30px',
39
+ marginTop: '0 !important',
40
+ borderTop: '2px solid black',
41
+ fontFamily: 'Roboto, Helvetica, Arial, sans-serif !important',
42
+ '&.mq-arrow-both': {
43
+ top: '0px !important',
44
+ '& *': {
45
+ lineHeight: '1 !important',
46
+ borderTop: 'none !important',
47
+ },
48
+ '&:before': {
49
+ fontSize: '80%',
50
+ left: 'calc(-13%) !important',
51
+ top: '-0.31em !important',
52
+ },
53
+ '&:after': {
54
+ fontSize: '80% !important',
55
+ right: 'calc(-13%) !important',
56
+ top: '-1.5em',
57
+ },
58
+ '&.mq-empty:before': {
59
+ fontSize: '80%',
60
+ left: 'calc(-13%)',
61
+ top: '-0.26em',
62
+ },
63
+ '&.mq-empty:after': {
64
+ fontSize: '80%',
65
+ right: 'calc(-13%)',
66
+ top: '-0.26em',
67
+ },
68
+ '&.mq-empty': {
69
+ minHeight: '1.4em',
70
+ },
71
+ },
72
+ '&.mq-arrow-right:before': {
73
+ fontSize: '80%',
74
+ right: 'calc(-13%) !important',
75
+ top: '-0.26em !important',
76
+ },
77
+ '& .mq-overarrow-inner': {
78
+ border: 'none !important',
79
+ },
80
+ '& .mq-overarrow-inner .mq-overarrow-inner-right': {
81
+ display: 'none !important',
82
+ },
83
+ },
84
+ '& .mq-root-block': {
85
+ padding: '5px !important',
86
+ },
87
+ '& .mq-overarrow.mq-arrow-both.mq-empty:after': {
88
+ right: '-6px',
89
+ fontSize: '80% !important',
90
+ top: '-3px',
91
+ },
92
+ '& .mq-overarrow.mq-arrow-right.mq-empty:before': {
93
+ right: '-5px',
94
+ fontSize: '80% !important',
95
+ top: '-3px',
96
+ },
97
+ '& .mq-overarrow.mq-arrow-both.mq-empty:before': {
98
+ left: '-6px',
99
+ fontSize: '80% !important',
100
+ top: '-3px',
101
+ },
102
+ '& .mq-longdiv-inner': {
103
+ borderTop: '1px solid !important',
104
+ paddingTop: '1.5px !important',
105
+ },
106
+ '& .mq-parallelogram': {
107
+ lineHeight: 0.85,
108
+ },
109
+ '& .mq-overarc': {
110
+ borderTop: '2px solid black !important',
111
+ '& .mq-overline': {
112
+ borderTop: 'none !important',
113
+ },
114
+ '& .mq-overline-inner': {
115
+ borderTop: 'none !important',
116
+ paddingTop: '0 !important',
117
+ },
118
+ },
119
+ };
120
+
121
+ // Add specific styles based on latex content
122
+ if (latex === '\\parallel') {
123
+ return {
124
+ ...baseStyles,
125
+ fontStyle: 'italic !important',
126
+ };
127
+ }
128
+
129
+ if (latex === '\\overleftrightarrow{\\overline{}}') {
130
+ return {
131
+ ...baseStyles,
132
+ '& .mq-overarrow.mq-arrow-both': {
133
+ '& .mq-overline-inner': {
134
+ borderTop: 'none !important',
135
+ paddingTop: '0 !important',
136
+ },
137
+ '&:after': {
138
+ position: 'absolute !important',
139
+ top: '0px !important',
140
+ },
141
+ },
142
+ };
143
+ }
144
+
145
+ return baseStyles;
146
+ });
147
+
148
+ LatexButtonContent.propTypes = {
149
+ latex: PropTypes.string.isRequired,
150
+ };
151
+
152
+ // LatexButton component removed - LatexButtonContent is used directly instead
153
+
154
+ const createCustomLayout = (layoutObj) => {
155
+ if (layoutObj) {
156
+ return {
157
+ gridTemplateColumns: `repeat(${layoutObj.columns}, minmax(min-content, 150px))`,
158
+ gridTemplateRows: `repeat(${layoutObj.rows}, minmax(40px, 60px))`,
159
+ gridAutoFlow: 'initial',
160
+ };
161
+ }
162
+
163
+ return {};
164
+ };
165
+
166
+ const KeyPadContainer = styled('div')(() => ({
167
+ ...commonMqKeyboardStyles,
168
+ width: '100%',
169
+ display: 'grid',
170
+ gridTemplateRows: 'repeat(5, minmax(40px, 60px))',
171
+ gridRowGap: '0px',
172
+ gridColumnGap: '0px',
173
+ gridAutoFlow: 'column',
174
+ '&.character': {
175
+ textTransform: 'initial !important',
176
+ gridTemplateRows: 'repeat(5, minmax(40px, 50px)) !important',
177
+ },
178
+ '&.language': {
179
+ gridTemplateRows: 'repeat(4, minmax(40px, 50px)) !important',
180
+ '& *': {
181
+ fontFamily: 'Roboto, Helvetica, Arial, sans-serif !important',
182
+ },
183
+ },
184
+ }));
185
+
186
+ const StyledButton = styled(Button)(({ category, isDelete, isComma, isDot }) => ({
187
+ minWidth: 'auto',
188
+ textTransform: 'none',
189
+ fontSize: isComma || isDot ? '200% !important' : '140% !important',
190
+ lineHeight: isComma || isDot ? '100%' : 'normal',
191
+ color: color.text(),
192
+
193
+ backgroundColor:
194
+ category === 'operators'
195
+ ? color.keypadButtonOperator()
196
+ : color.keypadButton(),
197
+ '&:hover': {
198
+ backgroundColor:
199
+ category === 'operators'
200
+ ? color.keypadButtonOperatorHover()
201
+ : color.keypadButtonHover()
202
+ },
203
+ borderRadius: 0,
204
+ ...(isDelete && {
205
+ fontFamily: 'Roboto, Helvetica, Arial, sans-serif !important',
206
+ }),
207
+ }));
208
+
209
+ const StyledLatexButtonWrapper = styled(Button)(({ category }) => ({
210
+ textTransform: 'none',
211
+ padding: 0,
212
+ margin: 0,
213
+ fontSize: '110% !important',
214
+ minWidth: 'auto',
215
+ borderRadius: 0,
216
+ backgroundColor:
217
+ category === 'operators'
218
+ ? color.keypadButtonOperator()
219
+ : color.keypadButton(),
220
+ '&:hover': {
221
+ backgroundColor:
222
+ category === 'operators'
223
+ ? color.keypadButtonOperatorHover()
224
+ : color.keypadButtonHover(),
225
+ },
226
+ }));
227
+
228
+ const StyledIconButton = styled(IconButton)(({ category }) => ({
229
+ minWidth: 'auto',
230
+ backgroundColor:
231
+ category === 'operators'
232
+ ? color.keypadButtonOperator()
233
+ : color.keypadButton(),
234
+ '&:hover': {
235
+ backgroundColor:
236
+ category === 'operators'
237
+ ? color.keypadButtonOperatorHover()
238
+ : color.keypadButtonHover(),
239
+ },
240
+ borderRadius: 0,
241
+ '& .icon': {
242
+ height: '30px',
243
+ },
244
+ }));
245
+
246
+ export class KeyPad extends React.Component {
247
+ static propTypes = {
248
+ className: PropTypes.string,
249
+ controlledKeypadMode: PropTypes.bool,
250
+ baseSet: PropTypes.array,
251
+ additionalKeys: PropTypes.array,
252
+ layoutForKeyPad: PropTypes.object,
253
+ onPress: PropTypes.func.isRequired,
254
+ onFocus: PropTypes.func,
255
+ noDecimal: PropTypes.bool,
256
+ setKeypadInteraction: PropTypes.func,
257
+ mode: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
258
+ };
259
+ static defaultProps = {
260
+ baseSet: baseSet,
261
+ noDecimal: false,
262
+ };
263
+
264
+ constructor(props) {
265
+ super(props);
266
+ this.keypadRef = React.createRef();
267
+ }
268
+
269
+ componentDidMount() {
270
+ const keyPadElement = this.keypadRef?.current;
271
+ const mainContainer = keyPadElement?.closest('.main-container');
272
+ const currentToolbar = keyPadElement?.closest('.pie-toolbar');
273
+
274
+ // need only for math keyboard so we need also controlledKeypadMode
275
+ if (this.props.controlledKeypadMode && mainContainer && currentToolbar) {
276
+ const mainContainerPosition = mainContainer.getBoundingClientRect();
277
+ const currentToolbarPosition = currentToolbar.getBoundingClientRect();
278
+ const difference =
279
+ mainContainerPosition.top +
280
+ mainContainerPosition.height -
281
+ (currentToolbarPosition.top + currentToolbarPosition.height);
282
+ if (difference < 0) {
283
+ const totalHeight = mainContainerPosition.height + mainContainerPosition.top - difference;
284
+ // increase the height of the main container if keyboard needs it
285
+ if (mainContainer) {
286
+ mainContainer.style.height = `${totalHeight}px`;
287
+ }
288
+ }
289
+ }
290
+
291
+ if (keyPadElement) {
292
+ keyPadElement.addEventListener('touchstart', this.handleKeypadInteraction, true);
293
+ keyPadElement.addEventListener('mousedown', this.handleKeypadInteraction, true);
294
+ }
295
+ }
296
+
297
+ componentWillUnmount() {
298
+ const keyPadElement = this.keypadRef?.current;
299
+ // need only for math keyboard
300
+ if (this.props.controlledKeypadMode && keyPadElement) {
301
+ const mainContainer = keyPadElement.closest('.main-container');
302
+
303
+ if (mainContainer) {
304
+ mainContainer.style.height = 'unset';
305
+ }
306
+ }
307
+
308
+ if (keyPadElement) {
309
+ keyPadElement.removeEventListener('touchstart', this.handleKeypadInteraction, true);
310
+ keyPadElement.removeEventListener('mousedown', this.handleKeypadInteraction, true);
311
+ }
312
+ }
313
+
314
+ handleKeypadInteraction = () => {
315
+ // Check if the setKeypadInteraction prop is available, which is used for both
316
+ // the language keypad and the special characters keypad
317
+ if (this.props.setKeypadInteraction) {
318
+ this.props.setKeypadInteraction(true);
319
+ }
320
+ };
321
+
322
+ buttonClick = (key) => {
323
+ log('[buttonClick]', key);
324
+ const { onPress } = this.props;
325
+
326
+ onPress(key);
327
+ };
328
+
329
+ flowKeys = (base, extras) => {
330
+ const transposed = [...sortKeys(base), ...sortKeys(extras)];
331
+ return flatten(transposed);
332
+ };
333
+
334
+ keyIsNotAllowed = (key) => {
335
+ const { noDecimal } = this.props;
336
+
337
+ if (((key.write === '.' && key.label === '.') || (key.write === ',' && key.label === ',')) && noDecimal) {
338
+ return true;
339
+ }
340
+
341
+ return false;
342
+ };
343
+
344
+ render() {
345
+ const { className, baseSet, additionalKeys, layoutForKeyPad, onFocus, mode } = this.props;
346
+
347
+ const noBaseSet = ['non-negative-integers', 'integers', 'decimals', 'fractions', 'item-authoring', 'language'];
348
+
349
+ const keysWithoutBaseSet = noBaseSet.includes(mode);
350
+ const allKeys = keysWithoutBaseSet
351
+ ? this.flowKeys([], additionalKeys || [])
352
+ : this.flowKeys(baseSet, additionalKeys || []);
353
+
354
+ const shift = allKeys.length % 5 ? 1 : 0;
355
+ const style = {
356
+ gridTemplateColumns: `repeat(${Math.floor(allKeys.length / 5) + shift}, minmax(min-content, 150px))`,
357
+ ...createCustomLayout(layoutForKeyPad),
358
+ };
359
+
360
+ return (
361
+ <KeyPadContainer
362
+ ref={this.keypadRef}
363
+ className={[className, mode].filter(Boolean).join(' ')}
364
+ style={style}
365
+ onFocus={onFocus}
366
+ >
367
+ {allKeys.map((k, index) => {
368
+ const onClick = this.buttonClick.bind(this, k);
369
+
370
+ if (!k) {
371
+ return <span key={`empty-${index}`} />;
372
+ }
373
+
374
+ const common = {
375
+ onClick,
376
+ disabled: this.keyIsNotAllowed(k),
377
+ key: `${k.label || k.latex || k.command}-${index}`,
378
+ ...(k.actions || {}),
379
+ ...(k.extraProps || {}),
380
+ };
381
+
382
+ if (k.latex) {
383
+ return (
384
+ <StyledLatexButtonWrapper
385
+ key={common.key}
386
+ onClick={common.onClick}
387
+ disabled={common.disabled}
388
+ category={!keysWithoutBaseSet ? k.category : undefined}
389
+ aria-label={k.ariaLabel ? k.ariaLabel : k.name || k.label}
390
+ {...(k.actions || {})}
391
+ {...(k.extraProps || {})}
392
+ >
393
+ <LatexButtonContent latex={k.latex} />
394
+ </StyledLatexButtonWrapper>
395
+ );
396
+ }
397
+
398
+ if (k.label) {
399
+ return (
400
+ <StyledButton
401
+ key={common.key}
402
+ onClick={common.onClick}
403
+ disabled={common.disabled}
404
+ category={!keysWithoutBaseSet ? k.category : undefined}
405
+ isDelete={k.label === '⌫'}
406
+ isComma={k.label === ','}
407
+ isDot={k.label === '.'}
408
+ aria-label={k.ariaLabel ? k.ariaLabel : k.name || k.label}
409
+ {...(k.actions || {})}
410
+ {...(k.extraProps || {})}
411
+ >
412
+ {k.label}
413
+ </StyledButton>
414
+ );
415
+ } else {
416
+ const Icon = k.icon ? k.icon : 'div';
417
+
418
+ return (
419
+ <StyledIconButton
420
+ key={common.key}
421
+ tabIndex={'-1'}
422
+ onClick={common.onClick}
423
+ disabled={common.disabled}
424
+ category={!keysWithoutBaseSet ? k.category : undefined}
425
+ size="large"
426
+ {...(k.actions || {})}
427
+ {...(k.extraProps || {})}
428
+ >
429
+ <Icon className="icon" />
430
+ </StyledIconButton>
431
+ );
432
+ }
433
+ })}
434
+ </KeyPadContainer>
435
+ );
436
+ }
437
+ }
438
+
439
+ export default KeyPad;
@@ -0,0 +1,16 @@
1
+ import { times, zip } from 'lodash-es';
2
+
3
+ /**
4
+ * Sort additional keys.
5
+ *
6
+ * Expects an array of rows.
7
+ * @param {} keys
8
+ */
9
+ export const sortKeys = (keys) => {
10
+ // add any missing rows
11
+ times(5 - keys.length, () => keys.push([]));
12
+
13
+ const out = zip.apply(null, keys);
14
+
15
+ return out;
16
+ };
@@ -0,0 +1,57 @@
1
+ import { extendKeySet } from '../utils';
2
+ import { gradeSets, keysForGrade } from '../grades';
3
+ import * as comparison from '../../keys/comparison';
4
+
5
+ describe('utils', () => {
6
+ const base = [[comparison.lessThan]];
7
+ describe('extendKeySet', () => {
8
+ it('removes duplicates by latex key', () => {
9
+ const extras = [{ name: 'foo', latex: '<', write: '<', label: 'less than' }];
10
+ const result = extendKeySet(base, extras);
11
+ const resultTwo = extendKeySet(base, []);
12
+ expect(result).toEqual(resultTwo);
13
+ });
14
+
15
+ it('removes duplicates by name key', () => {
16
+ const extras = [{ name: 'Less than', latex: 'this is latex', write: '<', label: '<' }];
17
+ const result = extendKeySet(base, extras);
18
+ const resultTwo = extendKeySet(base, []);
19
+ expect(result).toEqual(resultTwo);
20
+ });
21
+ });
22
+
23
+ describe('keysForGrade', () => {
24
+ it.each`
25
+ key | expected
26
+ ${'1'} | ${[]}
27
+ ${'2'} | ${[]}
28
+ ${'3'} | ${gradeSets[0].set}
29
+ ${'4'} | ${gradeSets[0].set}
30
+ ${'5'} | ${gradeSets[0].set}
31
+ ${'6'} | ${gradeSets[1].set}
32
+ ${'7'} | ${gradeSets[1].set}
33
+ ${'8'} | ${gradeSets[2].set}
34
+ ${'9'} | ${gradeSets[2].set}
35
+ ${'HS'} | ${gradeSets[2].set}
36
+ ${'non-negative-integers'} | ${gradeSets[3].set}
37
+ ${'integers'} | ${gradeSets[4].set}
38
+ ${'decimals'} | ${gradeSets[5].set}
39
+ ${'fractions'} | ${gradeSets[6].set}
40
+ ${'geometry'} | ${gradeSets[7].set}
41
+ ${'advanced-algebra'} | ${gradeSets[8].set}
42
+ ${'statistics'} | ${gradeSets[9].set}
43
+ ${'something else'} | ${gradeSets[2].set}
44
+ ${undefined} | ${[]}
45
+ ${null} | ${[]}
46
+ ${0} | ${[]}
47
+ ${'0'} | ${[]}
48
+ `('$key => $expected', ({ key, expected }) => {
49
+ expect(keysForGrade(key)).toEqual(expected);
50
+
51
+ const n = parseInt(key, 10);
52
+ if (!isNaN(n)) {
53
+ expect(keysForGrade(n)).toEqual(expected);
54
+ }
55
+ });
56
+ });
57
+ });
@@ -0,0 +1,32 @@
1
+ import { DIVIDE, MULTIPLY } from './chars';
2
+ import { mkSet } from './utils';
3
+
4
+ const set = mkSet('operators');
5
+
6
+ export const equals = set({
7
+ write: '=',
8
+ label: '=',
9
+ });
10
+
11
+ export const plus = set({
12
+ write: '+',
13
+ label: '+',
14
+ });
15
+
16
+ export const minus = set({
17
+ write: '−',
18
+ label: '−',
19
+ });
20
+
21
+ export const divide = set({
22
+ name: 'divide',
23
+ label: DIVIDE,
24
+ command: '\\divide',
25
+ otherNotation: '\\div',
26
+ });
27
+
28
+ export const multiply = set({
29
+ name: 'multiply',
30
+ label: MULTIPLY,
31
+ command: '\\times',
32
+ });
@@ -0,0 +1,5 @@
1
+ export const DELETE = '\u232B';
2
+ export const LEFT_ARROW = '◀';
3
+ export const RIGHT_ARROW = '▶';
4
+ export const DIVIDE = '\u00F7';
5
+ export const MULTIPLY = '\u00D7';
@@ -0,0 +1,28 @@
1
+ const set = (o) => ({ ...o, category: 'comparison' });
2
+
3
+ export const lessThan = set({
4
+ name: 'Less than',
5
+ latex: '<',
6
+ command: '\\lt',
7
+ });
8
+
9
+ export const greaterThan = set({
10
+ name: 'Greater than',
11
+ latex: '>',
12
+ command: '\\gt',
13
+ });
14
+
15
+ export const lessThanEqual = set({
16
+ name: 'Less than or equal',
17
+ latex: '\\le',
18
+ symbol: '<=',
19
+ command: '\\le',
20
+ ariaLabel: 'less than or equal to',
21
+ });
22
+
23
+ export const greaterThanEqual = set({
24
+ name: 'Greater than or equal',
25
+ symbol: '>=',
26
+ command: '\\ge',
27
+ latex: '\\ge',
28
+ });
@@ -0,0 +1,35 @@
1
+ import { mkSet } from './utils';
2
+
3
+ const set = mkSet('constants');
4
+
5
+ export const pi = set({
6
+ name: 'Pi',
7
+ label: 'π',
8
+ latex: '\\pi',
9
+ command: '\\pi',
10
+ category: 'constants',
11
+ });
12
+
13
+ export const eulers = set({
14
+ name: 'Eulers',
15
+ label: 'e',
16
+ latex: 'e',
17
+ command: 'e',
18
+ category: 'constants',
19
+ });
20
+
21
+ export const infinity = set({
22
+ name: 'Infinity',
23
+ label: '\\infty',
24
+ latex: '\\infty',
25
+ command: '\\infty',
26
+ category: 'constants',
27
+ });
28
+
29
+ export const halfInfinity = set({
30
+ name: 'Half Infinity',
31
+ label: '\\propto',
32
+ latex: '\\propto',
33
+ command: '\\propto',
34
+ category: 'constants',
35
+ });
@@ -0,0 +1,40 @@
1
+ import { times } from 'lodash-es';
2
+
3
+ const digitMap = {
4
+ 0: 'zero',
5
+ 1: 'one',
6
+ 2: 'two',
7
+ 3: 'three',
8
+ 4: 'four',
9
+ 5: 'five',
10
+ 6: 'six',
11
+ 7: 'seven',
12
+ 8: 'eight',
13
+ 9: 'nine',
14
+ };
15
+
16
+ const comma = { name: 'comma', label: ',', write: ',', category: 'digit' };
17
+
18
+ const decimalPoint = {
19
+ name: 'decimal-point',
20
+ label: '.',
21
+ write: '.',
22
+ category: 'digit',
23
+ };
24
+
25
+ export default times(10, String)
26
+ .map((n) => {
27
+ return {
28
+ name: digitMap[n],
29
+ write: n,
30
+ label: n,
31
+ category: 'digit',
32
+ };
33
+ })
34
+ .reduce(
35
+ (acc, o) => {
36
+ acc[o.name] = o;
37
+ return acc;
38
+ },
39
+ { comma, decimalPoint },
40
+ );
@@ -0,0 +1,3 @@
1
+ import { DELETE } from './chars';
2
+
3
+ export const del = { label: DELETE, category: 'edit', keystroke: 'Backspace', ariaLabel: 'Delete' };