@pie-lib/math-input 0.1.0 → 0.1.1-next.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 (106) hide show
  1. package/dist/horizontal-keypad.d.ts +1 -2
  2. package/dist/index.d.ts +0 -1
  3. package/dist/keypad/accessible-keypad.d.ts +2 -3
  4. package/dist/keypad/index.d.ts +0 -1
  5. package/dist/keypad/keys-layout.d.ts +0 -1
  6. package/dist/keypad/model.d.ts +0 -1
  7. package/dist/keys/basic-operators.d.ts +0 -1
  8. package/dist/keys/chars.d.ts +0 -1
  9. package/dist/keys/comparison.d.ts +0 -1
  10. package/dist/keys/constants.d.ts +0 -1
  11. package/dist/keys/digits.d.ts +0 -1
  12. package/dist/keys/edit.d.ts +0 -1
  13. package/dist/keys/exponent.d.ts +0 -1
  14. package/dist/keys/fractions.d.ts +0 -1
  15. package/dist/keys/geometry.d.ts +0 -1
  16. package/dist/keys/grades.d.ts +0 -1
  17. package/dist/keys/index.d.ts +0 -1
  18. package/dist/keys/log.d.ts +0 -1
  19. package/dist/keys/logic.d.ts +0 -1
  20. package/dist/keys/matrices.d.ts +0 -1
  21. package/dist/keys/misc.d.ts +0 -1
  22. package/dist/keys/navigation.d.ts +0 -1
  23. package/dist/keys/operators.d.ts +0 -1
  24. package/dist/keys/statistics.d.ts +0 -1
  25. package/dist/keys/sub-sup.d.ts +0 -1
  26. package/dist/keys/trigonometry.d.ts +0 -1
  27. package/dist/keys/utils.d.ts +0 -1
  28. package/dist/keys/vars.d.ts +0 -1
  29. package/dist/math-input.d.ts +1 -2
  30. package/dist/mq/common-mq-styles.d.ts +0 -1
  31. package/dist/mq/custom-elements.d.ts +0 -1
  32. package/dist/mq/index.d.ts +0 -1
  33. package/dist/mq/input.d.ts +1 -2
  34. package/dist/mq/static.d.ts +1 -2
  35. package/dist/updateSpans.d.ts +0 -1
  36. package/package.json +10 -4
  37. package/dist/horizontal-keypad.d.ts.map +0 -1
  38. package/dist/index.d.ts.map +0 -1
  39. package/dist/keypad/accessible-keypad.d.ts.map +0 -1
  40. package/dist/keypad/index.d.ts.map +0 -1
  41. package/dist/keypad/keys-layout.d.ts.map +0 -1
  42. package/dist/keypad/model.d.ts.map +0 -1
  43. package/dist/keys/basic-operators.d.ts.map +0 -1
  44. package/dist/keys/chars.d.ts.map +0 -1
  45. package/dist/keys/comparison.d.ts.map +0 -1
  46. package/dist/keys/constants.d.ts.map +0 -1
  47. package/dist/keys/digits.d.ts.map +0 -1
  48. package/dist/keys/edit.d.ts.map +0 -1
  49. package/dist/keys/exponent.d.ts.map +0 -1
  50. package/dist/keys/fractions.d.ts.map +0 -1
  51. package/dist/keys/geometry.d.ts.map +0 -1
  52. package/dist/keys/grades.d.ts.map +0 -1
  53. package/dist/keys/index.d.ts.map +0 -1
  54. package/dist/keys/log.d.ts.map +0 -1
  55. package/dist/keys/logic.d.ts.map +0 -1
  56. package/dist/keys/matrices.d.ts.map +0 -1
  57. package/dist/keys/misc.d.ts.map +0 -1
  58. package/dist/keys/navigation.d.ts.map +0 -1
  59. package/dist/keys/operators.d.ts.map +0 -1
  60. package/dist/keys/statistics.d.ts.map +0 -1
  61. package/dist/keys/sub-sup.d.ts.map +0 -1
  62. package/dist/keys/trigonometry.d.ts.map +0 -1
  63. package/dist/keys/utils.d.ts.map +0 -1
  64. package/dist/keys/vars.d.ts.map +0 -1
  65. package/dist/math-input.d.ts.map +0 -1
  66. package/dist/mq/common-mq-styles.d.ts.map +0 -1
  67. package/dist/mq/custom-elements.d.ts.map +0 -1
  68. package/dist/mq/index.d.ts.map +0 -1
  69. package/dist/mq/input.d.ts.map +0 -1
  70. package/dist/mq/static.d.ts.map +0 -1
  71. package/dist/updateSpans.d.ts.map +0 -1
  72. package/src/horizontal-keypad.tsx +0 -82
  73. package/src/index.tsx +0 -27
  74. package/src/keypad/accessible-keypad.tsx +0 -731
  75. package/src/keypad/index.tsx +0 -3
  76. package/src/keypad/keys-layout.ts +0 -26
  77. package/src/keypad/model.ts +0 -149
  78. package/src/keys/basic-operators.ts +0 -42
  79. package/src/keys/chars.ts +0 -15
  80. package/src/keys/comparison.ts +0 -38
  81. package/src/keys/constants.ts +0 -45
  82. package/src/keys/digits.ts +0 -50
  83. package/src/keys/edit.ts +0 -13
  84. package/src/keys/exponent.ts +0 -38
  85. package/src/keys/fractions.ts +0 -36
  86. package/src/keys/geometry.ts +0 -154
  87. package/src/keys/grades.ts +0 -377
  88. package/src/keys/index.ts +0 -30
  89. package/src/keys/log.ts +0 -32
  90. package/src/keys/logic.ts +0 -25
  91. package/src/keys/matrices.ts +0 -25
  92. package/src/keys/misc.ts +0 -75
  93. package/src/keys/navigation.ts +0 -18
  94. package/src/keys/operators.ts +0 -20
  95. package/src/keys/statistics.ts +0 -48
  96. package/src/keys/sub-sup.ts +0 -25
  97. package/src/keys/trigonometry.ts +0 -25
  98. package/src/keys/utils.ts +0 -76
  99. package/src/keys/vars.ts +0 -29
  100. package/src/math-input.tsx +0 -129
  101. package/src/mq/common-mq-styles.ts +0 -115
  102. package/src/mq/custom-elements.tsx +0 -21
  103. package/src/mq/index.ts +0 -15
  104. package/src/mq/input.tsx +0 -172
  105. package/src/mq/static.tsx +0 -279
  106. package/src/updateSpans.ts +0 -26
@@ -1,731 +0,0 @@
1
- // @ts-nocheck
2
- import React from 'react';
3
- import PropTypes from 'prop-types';
4
- import Button from '@mui/material/Button';
5
- import IconButton from '@mui/material/IconButton';
6
- import { styled } from '@mui/material/styles';
7
- import debug from 'debug';
8
- import { flatten } from 'lodash-es';
9
- import { color } from '@pie-lib/render-ui';
10
- import { commonMqKeyboardStyles } from '../mq/common-mq-styles';
11
- import { baseSet } from '../keys';
12
- import { getAriaLabel } from './model';
13
- import { sortKeys } from './keys-layout';
14
-
15
- const log = debug('pie-lib:math-inline:keypad:a11y');
16
-
17
- const KeypadRoot: any = styled('div')(() => ({
18
- ...commonMqKeyboardStyles,
19
- width: '100%',
20
- display: 'grid',
21
- gridTemplateRows: 'repeat(5, minmax(40px, 60px))',
22
- gridAutoFlow: 'column',
23
- border: `1px solid ${color.borderLight()}`,
24
- '&.character': {
25
- textTransform: 'initial !important',
26
- gridTemplateRows: 'repeat(5, minmax(40px, 50px)) !important',
27
- },
28
- '&.language': {
29
- gridTemplateRows: 'repeat(4, minmax(40px, 50px)) !important',
30
- '& *': {
31
- fontFamily: 'Roboto, Helvetica, Arial, sans-serif !important',
32
- },
33
- },
34
- }));
35
-
36
- const KeypadLane: any = styled('div')(({ operator }) => ({
37
- flex: operator ? '0 0 auto' : '1 1 0',
38
- display: 'grid',
39
- gridAutoFlow: 'column',
40
- gridTemplateRows: 'repeat(5, minmax(44px, 56px))',
41
- gridAutoColumns: 'minmax(72px, 1fr)',
42
- backgroundColor: operator ? color.blueGrey300() : color.blueGrey100(),
43
- }));
44
-
45
- const TextKey: any = styled(Button, { shouldForwardProp: (prop) => prop !== 'operator' })(({ operator }) => ({
46
- minWidth: 'auto',
47
- borderRadius: 0,
48
- color: color.text(),
49
- backgroundColor: operator ? color.blueGrey300() : color.blueGrey100(),
50
- textTransform: 'none',
51
- fontSize: '1.6rem',
52
- lineHeight: 1,
53
- '&:hover': {
54
- backgroundColor: operator ? color.blueGrey600() : color.blueGrey300(),
55
- },
56
- '&:focus-visible': {
57
- outline: `3px solid ${color.focusCheckedBorder()}`,
58
- outlineOffset: -3,
59
- zIndex: 2,
60
- },
61
- }));
62
-
63
- const IconKey: any = styled(IconButton, { shouldForwardProp: (prop) => prop !== 'operator' })(({ operator }) => ({
64
- minWidth: 'auto',
65
- borderRadius: 0,
66
- color: color.text(),
67
- backgroundColor: operator ? color.blueGrey300() : color.blueGrey100(),
68
- '&:hover': {
69
- backgroundColor: operator ? color.blueGrey600() : color.blueGrey300(),
70
- },
71
- '&:focus-visible': {
72
- outline: `3px solid ${color.focusCheckedBorder()}`,
73
- outlineOffset: -3,
74
- zIndex: 2,
75
- },
76
- '& .icon': {
77
- height: '30px',
78
- },
79
- }));
80
-
81
- const MathPreviewRoot: any = styled('span')(() => ({
82
- pointerEvents: 'none',
83
- color: color.text(),
84
- display: 'inline-flex',
85
- alignItems: 'center',
86
- justifyContent: 'center',
87
- minHeight: 40,
88
- minWidth: 34,
89
- '& .MathJax': {
90
- fontSize: '1.45rem',
91
- lineHeight: 1.1,
92
- },
93
- '& mjx-container': {
94
- overflow: 'visible !important',
95
- lineHeight: 1.1,
96
- },
97
- '& .tpl-root': {
98
- display: 'inline-flex',
99
- alignItems: 'center',
100
- justifyContent: 'center',
101
- gap: 4,
102
- fontSize: '1.5rem',
103
- lineHeight: 1,
104
- minHeight: 34,
105
- fontFamily: '"Times New Roman", Times, serif',
106
- textTransform: 'none',
107
- },
108
- '& .mq-keycap': {
109
- fontFamily: '"STIX Two Text", MJXZERO, MJXTEX, "Times New Roman", Times, serif',
110
- fontStyle: 'normal',
111
- },
112
- '& .mq-keycap var': {
113
- fontFamily: '"STIX Two Text", MJXZERO, MJXTEX-I, "Times New Roman", Times, serif',
114
- fontStyle: 'italic',
115
- },
116
- '& .mq-keycap .mq-operator-name': {
117
- fontFamily: '"STIX Two Text", MJXZERO, MJXTEX, "Times New Roman", Times, serif',
118
- fontStyle: 'normal',
119
- },
120
- '& .tpl-fraction': {
121
- display: 'inline-grid',
122
- gridTemplateRows: 'auto 1px auto',
123
- justifyItems: 'center',
124
- alignItems: 'center',
125
- minWidth: 22,
126
- },
127
- '& .tpl-frac-line': {
128
- width: '1.05em',
129
- borderTop: `2px solid ${color.text()}`,
130
- margin: '2px 0',
131
- },
132
- '& .tpl-mixed-fraction': {
133
- display: 'inline-flex',
134
- alignItems: 'center',
135
- gap: 3,
136
- },
137
- '& .tpl-frac-cell': {
138
- display: 'inline-flex',
139
- alignItems: 'center',
140
- justifyContent: 'center',
141
- minHeight: '0.55em',
142
- },
143
- '& .tpl-placeholder': {
144
- display: 'inline-block',
145
- width: '0.42em',
146
- height: '0.76em',
147
- backgroundColor: color.secondaryLight(),
148
- },
149
- '& .tpl-longdiv': {
150
- display: 'inline-flex',
151
- alignItems: 'center',
152
- gap: 3,
153
- },
154
- '& .tpl-longdiv-radicand': {
155
- display: 'inline-flex',
156
- alignItems: 'center',
157
- borderTop: `2px solid ${color.text()}`,
158
- paddingTop: '0.15em',
159
- minWidth: '0.8em',
160
- justifyContent: 'center',
161
- },
162
- '& .tpl-sup': {
163
- display: 'inline-flex',
164
- alignItems: 'flex-start',
165
- },
166
- '& .tpl-sup .tpl-placeholder': {
167
- width: '0.3em',
168
- height: '0.5em',
169
- marginLeft: '0.05em',
170
- transform: 'translateY(-0.35em)',
171
- },
172
- '& .tpl-sub': {
173
- display: 'inline-flex',
174
- alignItems: 'flex-end',
175
- },
176
- '& .tpl-sub .tpl-placeholder': {
177
- width: '0.3em',
178
- height: '0.5em',
179
- marginLeft: '0.05em',
180
- transform: 'translateY(0.3em)',
181
- },
182
- '& .tpl-root-radical': {
183
- display: 'inline-flex',
184
- alignItems: 'flex-start',
185
- },
186
- '& .tpl-root-sign': {
187
- fontSize: '1.15em',
188
- lineHeight: 1,
189
- },
190
- '& .tpl-radicand': {
191
- borderTop: `2px solid ${color.text()}`,
192
- padding: '0.05em 0 0 0.08em',
193
- marginLeft: '0.03em',
194
- },
195
- '& .tpl-over': {
196
- display: 'inline-grid',
197
- justifyItems: 'center',
198
- gap: 2,
199
- },
200
- '& .tpl-over-line': {
201
- display: 'block',
202
- width: '1em',
203
- borderTop: `2px solid ${color.text()}`,
204
- },
205
- '& .tpl-over-arrow': {
206
- display: 'block',
207
- fontSize: '0.65em',
208
- lineHeight: 1,
209
- },
210
- '& .tpl-over-arc': {
211
- display: 'block',
212
- fontSize: '0.8em',
213
- lineHeight: 1,
214
- transform: 'translateY(0.1em)',
215
- },
216
- }));
217
-
218
- const MathKey: any = styled(Button, { shouldForwardProp: (prop) => prop !== 'operator' })(({ operator }) => ({
219
- minWidth: 'auto',
220
- borderRadius: 0,
221
- padding: '6px',
222
- overflow: 'visible',
223
- textTransform: 'none',
224
- color: color.text(),
225
- backgroundColor: operator ? color.blueGrey300() : color.blueGrey100(),
226
- '&:hover': {
227
- backgroundColor: operator ? color.blueGrey600() : color.blueGrey300(),
228
- },
229
- '&:focus-visible': {
230
- outline: `3px solid ${color.focusCheckedBorder()}`,
231
- outlineOffset: -3,
232
- zIndex: 2,
233
- },
234
- }));
235
-
236
- const LATEX_SYMBOL_MAP = {
237
- '\\theta': 'θ',
238
- '\\pi': 'π',
239
- '\\infty': '∞',
240
- '\\propto': '∝',
241
- '\\sin': 'sin',
242
- '\\cos': 'cos',
243
- '\\tan': 'tan',
244
- '\\sec': 'sec',
245
- '\\csc': 'csc',
246
- '\\cot': 'cot',
247
- '\\log': 'log',
248
- '\\ln': 'ln',
249
- '\\pm': '±',
250
- '\\approx': '≈',
251
- '\\napprox': '≉',
252
- '\\neq': '≠',
253
- '\\sim': '∼',
254
- '\\nsim': '≁',
255
- '\\mu': 'μ',
256
- '\\Sigma': 'Σ',
257
- '\\sigma': 'σ',
258
- '\\parallel': '∥',
259
- '\\nparallel': '∦',
260
- '\\perp': '⟂',
261
- '\\angle': '∠',
262
- '\\measuredangle': '∡',
263
- '\\triangle': '△',
264
- '\\square': '□',
265
- '\\parallelogram': '▱',
266
- '\\odot': '⊙',
267
- '\\degree': '°',
268
- '\\cong': '≅',
269
- '\\ncong': '≇',
270
- '\\leftarrow': '←',
271
- '\\rightarrow': '→',
272
- '\\leftrightarrow': '↔',
273
- '\\le': '≤',
274
- '\\ge': '≥',
275
- };
276
-
277
- const OPERATOR_NAMES = new Set(['\\sin', '\\cos', '\\tan', '\\sec', '\\csc', '\\cot', '\\log', '\\ln']);
278
- const ITALIC_VARS = new Set(['x', 'y', 'i', 'e', 'AB']);
279
-
280
- const renderMathToken = (token = '') => {
281
- if (ITALIC_VARS.has(token)) {
282
- return <var>{token}</var>;
283
- }
284
- return <span>{token}</span>;
285
- };
286
-
287
- const renderSymbolNode = (latex = '') => {
288
- if (OPERATOR_NAMES.has(latex)) {
289
- return <var className="mq-operator-name">{latex.replace(/^\\/, '')}</var>;
290
- }
291
- const mapped = LATEX_SYMBOL_MAP[latex];
292
- if (mapped) {
293
- if (ITALIC_VARS.has(mapped)) {
294
- return <var>{mapped}</var>;
295
- }
296
- return <span>{mapped}</span>;
297
- }
298
- const normalized = latex
299
- .replace(/^\\/, '')
300
- .replace(/\\(left|right)/g, '')
301
- .replace(/[{}\\]/g, '')
302
- .replace(/\s+/g, ' ')
303
- .trim();
304
- return renderMathToken(normalized);
305
- };
306
-
307
- const Placeholder = ({ short = false }) => (
308
- <span className="tpl-placeholder" style={short ? { width: '0.3em', height: '0.55em' } : undefined} />
309
- );
310
-
311
- const OverVisual = ({ body, marker }) => (
312
- <span className="tpl-root tpl-over mq-keycap" aria-hidden>
313
- <span className={marker}>{marker === 'tpl-over-line' ? '' : marker === 'tpl-over-arrow' ? '↔' : '◠'}</span>
314
- <span>{body}</span>
315
- </span>
316
- );
317
-
318
- const KeyVisual = ({ definition }) => {
319
- const latex = definition.key?.latex || '';
320
- switch (definition.visualType) {
321
- case 'fractionTemplate':
322
- if (latex === '\\frac{x}{ }') {
323
- return (
324
- <span className="tpl-root mq-keycap" aria-hidden>
325
- <span className="tpl-fraction">
326
- <var>x</var>
327
- <span className="tpl-frac-line" />
328
- <Placeholder />
329
- </span>
330
- </span>
331
- );
332
- }
333
- return (
334
- <span className="tpl-root mq-keycap" aria-hidden>
335
- <span className="tpl-fraction">
336
- <Placeholder />
337
- <span className="tpl-frac-line" />
338
- <Placeholder />
339
- </span>
340
- </span>
341
- );
342
- case 'mixedFractionTemplate':
343
- return (
344
- <span className="tpl-root tpl-mixed-fraction mq-keycap" aria-hidden>
345
- <var>x</var>
346
- <span className="tpl-fraction" style={{ minWidth: '0.9em' }}>
347
- <span className="tpl-frac-cell">
348
- <Placeholder short />
349
- </span>
350
- <span className="tpl-frac-line" />
351
- <span className="tpl-frac-cell">
352
- <Placeholder short />
353
- </span>
354
- </span>
355
- </span>
356
- );
357
- case 'squaredTemplate':
358
- return (
359
- <span className="tpl-root tpl-sup mq-keycap" aria-hidden>
360
- <var>x</var>
361
- <span style={{ fontSize: '0.7em', transform: 'translateY(-0.25em)' }}>2</span>
362
- </span>
363
- );
364
- case 'longdivTemplate':
365
- return (
366
- <span className="tpl-root mq-keycap" aria-hidden>
367
- <span className="tpl-longdiv">
368
- <span>)</span>
369
- <span className="tpl-longdiv-radicand">
370
- <Placeholder />
371
- </span>
372
- </span>
373
- </span>
374
- );
375
- case 'supTemplate':
376
- return (
377
- <span className="tpl-root tpl-sup mq-keycap" aria-hidden>
378
- <var>x</var>
379
- <Placeholder short />
380
- </span>
381
- );
382
- case 'subTemplate':
383
- return (
384
- <span className="tpl-root tpl-sub mq-keycap" aria-hidden>
385
- <var>x</var>
386
- <Placeholder short />
387
- </span>
388
- );
389
- case 'sqrtTemplate':
390
- return (
391
- <span className="tpl-root tpl-root-radical mq-keycap" aria-hidden>
392
- <span className="tpl-root-sign">√</span>
393
- <span className="tpl-radicand">
394
- <Placeholder />
395
- </span>
396
- </span>
397
- );
398
- case 'nthRootTemplate':
399
- return (
400
- <span className="tpl-root tpl-root-radical mq-keycap" aria-hidden>
401
- <span style={{ fontSize: '0.55em', transform: 'translate(0.15em,-0.2em)' }}>
402
- <Placeholder short />
403
- </span>
404
- <span className="tpl-root-sign">√</span>
405
- <span className="tpl-radicand">
406
- <Placeholder />
407
- </span>
408
- </span>
409
- );
410
- case 'parenTemplate':
411
- return (
412
- <span className="tpl-root mq-keycap" aria-hidden>
413
- (<Placeholder />)
414
- </span>
415
- );
416
- case 'bracketTemplate':
417
- return (
418
- <span className="tpl-root mq-keycap" aria-hidden>
419
- [<Placeholder />]
420
- </span>
421
- );
422
- case 'absTemplate':
423
- return (
424
- <span className="tpl-root mq-keycap" aria-hidden>
425
- |<Placeholder />|
426
- </span>
427
- );
428
- case 'logSubTemplate':
429
- return (
430
- <span className="tpl-root tpl-sub mq-keycap" aria-hidden>
431
- <var className="mq-operator-name">log</var>
432
- <Placeholder short />
433
- </span>
434
- );
435
- case 'overlineTemplate':
436
- return (
437
- <OverVisual
438
- body={latex.includes('y') ? <var>y</var> : latex.includes('x') ? <var>x</var> : <Placeholder />}
439
- marker="tpl-over-line"
440
- />
441
- );
442
- case 'overArrowTemplate':
443
- return <OverVisual body={<Placeholder />} marker="tpl-over-arrow" />;
444
- case 'overBiArrowTemplate':
445
- return <OverVisual body={latex.includes('AB') ? <var>AB</var> : <Placeholder />} marker="tpl-over-arrow" />;
446
- case 'overArcTemplate':
447
- return <OverVisual body={<Placeholder />} marker="tpl-over-arc" />;
448
- case 'symbolText':
449
- return (
450
- <span className="tpl-root mq-keycap" aria-hidden>
451
- {renderSymbolNode(latex)}
452
- </span>
453
- );
454
- default:
455
- return (
456
- <span className="tpl-root" aria-hidden>
457
- {definition.key?.label || definition.key?.write || definition.key?.name}
458
- </span>
459
- );
460
- }
461
- };
462
-
463
- const buildOrderedKeys = (baseRows, extraRows, includeBaseSet) => {
464
- const orderedBase = includeBaseSet ? toColumnMajor(baseRows || []) : [];
465
- const orderedExtra = toColumnMajor(extraRows || []);
466
- return [...orderedBase, ...orderedExtra];
467
- };
468
-
469
- const flattenGridByColumn = (grid) => {
470
- if (!grid || !grid.length) {
471
- return [];
472
- }
473
- const rows = grid.length;
474
- const cols = Math.max(...grid.map((r) => r.length), 0);
475
- const out = [];
476
- for (let col = 0; col < cols; col++) {
477
- for (let row = 0; row < rows; row++) {
478
- out.push(grid[row][col] ?? null);
479
- }
480
- }
481
- return out;
482
- };
483
-
484
- const isEmptyKey = (key) => {
485
- return key && key.name === '' && key.latex === '' && key.write === '';
486
- };
487
-
488
- const normalizeInlineLatex = (value = '') => value.replace(/\$\$/g, '').replace(/^\$|\$$/g, '').trim();
489
-
490
- const createCustomLayout = (layoutObj) => {
491
- if (layoutObj) {
492
- return {
493
- gridTemplateColumns: `repeat(${layoutObj.columns}, minmax(min-content, 150px))`,
494
- gridTemplateRows: `repeat(${layoutObj.rows}, minmax(40px, 60px))`,
495
- gridAutoFlow: 'initial',
496
- };
497
- }
498
- return {};
499
- };
500
-
501
- export default class AccessibleKeypad extends React.Component {
502
- static propTypes = {
503
- className: PropTypes.string,
504
- controlledKeypadMode: PropTypes.bool,
505
- baseSet: PropTypes.array,
506
- additionalKeys: PropTypes.array,
507
- layoutForKeyPad: PropTypes.object,
508
- onPress: PropTypes.func.isRequired,
509
- onFocus: PropTypes.func,
510
- noDecimal: PropTypes.bool,
511
- setKeypadInteraction: PropTypes.func,
512
- mode: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
513
- onRequestClose: PropTypes.func,
514
- };
515
-
516
- static defaultProps = {
517
- baseSet: baseSet,
518
- noDecimal: false,
519
- };
520
-
521
- constructor(props) {
522
- super(props);
523
- this.keypadRef = React.createRef();
524
- this.buttonRefs = [];
525
- this.state = { activeIndex: 0 };
526
- }
527
-
528
- componentDidMount() {
529
- const keyPadElement = this.keypadRef?.current;
530
- const mainContainer = keyPadElement?.closest('.main-container');
531
- const currentToolbar = keyPadElement?.closest('.pie-toolbar');
532
- if (this.props.controlledKeypadMode && mainContainer && currentToolbar) {
533
- const mainContainerPosition = mainContainer.getBoundingClientRect();
534
- const currentToolbarPosition = currentToolbar.getBoundingClientRect();
535
- const difference =
536
- mainContainerPosition.top + mainContainerPosition.height - (currentToolbarPosition.top + currentToolbarPosition.height);
537
- if (difference < 0 && mainContainer) {
538
- mainContainer.style.height = `${mainContainerPosition.height + mainContainerPosition.top - difference}px`;
539
- }
540
- }
541
- if (keyPadElement) {
542
- keyPadElement.addEventListener('touchstart', this.handleKeypadInteraction, true);
543
- keyPadElement.addEventListener('mousedown', this.handleKeypadInteraction, true);
544
- }
545
- }
546
-
547
- componentWillUnmount() {
548
- const keyPadElement = this.keypadRef?.current;
549
- if (this.props.controlledKeypadMode && keyPadElement) {
550
- const mainContainer = keyPadElement.closest('.main-container');
551
- if (mainContainer) {
552
- mainContainer.style.height = 'unset';
553
- }
554
- }
555
- if (keyPadElement) {
556
- keyPadElement.removeEventListener('touchstart', this.handleKeypadInteraction, true);
557
- keyPadElement.removeEventListener('mousedown', this.handleKeypadInteraction, true);
558
- }
559
- }
560
-
561
- handleKeypadInteraction = () => {
562
- if (this.props.setKeypadInteraction) {
563
- this.props.setKeypadInteraction(true);
564
- }
565
- };
566
-
567
- keyIsNotAllowed = (key) => {
568
- const { noDecimal } = this.props;
569
- return ((key.write === '.' && key.label === '.') || (key.write === ',' && key.label === ',')) && noDecimal;
570
- };
571
-
572
- flowKeys = (base, extras) => {
573
- const transposed = [...sortKeys([...(base || []).map((r) => [...r])]), ...sortKeys([...(extras || []).map((r) => [...r])])];
574
- return flatten(transposed);
575
- };
576
-
577
- toRenderModel = () => {
578
- const { baseSet, additionalKeys, mode } = this.props;
579
- const includeBaseSet = !['non-negative-integers', 'integers', 'decimals', 'fractions', 'item-authoring', 'language'].includes(
580
- mode,
581
- );
582
- const allKeys = includeBaseSet ? this.flowKeys(baseSet, additionalKeys || []) : this.flowKeys([], additionalKeys || []);
583
- const filtered = allKeys
584
- .filter((k) => k && !isEmptyKey(k))
585
- .map((k) =>
586
- k.latex
587
- ? {
588
- ...k,
589
- latex: normalizeInlineLatex(k.latex),
590
- write: typeof k.write === 'string' ? normalizeInlineLatex(k.write) : k.write,
591
- label: typeof k.label === 'string' ? normalizeInlineLatex(k.label) : k.label,
592
- }
593
- : k,
594
- );
595
- return filtered.map((key, index) => ({
596
- id: `${key.label || key.latex || key.name || key.command || 'key'}-${index}`,
597
- ariaLabel: getAriaLabel(key),
598
- key,
599
- }));
600
- };
601
-
602
- pressKey = (definition) => {
603
- if (!definition || this.keyIsNotAllowed(definition.key)) {
604
- return;
605
- }
606
- log('[pressKey]', definition.id);
607
- this.props.onPress(definition.key);
608
- };
609
-
610
- moveFocus = (nextIndex) => {
611
- const bounded = Math.max(0, Math.min(nextIndex, this.buttonRefs.length - 1));
612
- this.setState({ activeIndex: bounded }, () => {
613
- const target = this.buttonRefs[bounded];
614
- if (target && !target.disabled) {
615
- target.focus();
616
- }
617
- });
618
- };
619
-
620
- onButtonKeyDown = (event, index) => {
621
- if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {
622
- event.preventDefault();
623
- this.moveFocus(index + 1);
624
- return;
625
- }
626
- if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {
627
- event.preventDefault();
628
- this.moveFocus(index - 1);
629
- return;
630
- }
631
- if (event.key === 'Home') {
632
- event.preventDefault();
633
- this.moveFocus(0);
634
- return;
635
- }
636
- if (event.key === 'End') {
637
- event.preventDefault();
638
- this.moveFocus(this.buttonRefs.length - 1);
639
- return;
640
- }
641
- if (event.key === 'Escape' && this.props.onRequestClose) {
642
- event.preventDefault();
643
- this.props.onRequestClose();
644
- }
645
- };
646
-
647
- renderKey = (definition, index) => {
648
- const key = definition.key;
649
- const operator = key.category === 'operators';
650
- const disabled = this.keyIsNotAllowed(key);
651
- const common = {
652
- key: `${definition.id}-${index}`,
653
- onClick: () => this.pressKey(definition),
654
- onKeyDown: (e) => this.onButtonKeyDown(e, index),
655
- onFocus: () => this.setState({ activeIndex: index }),
656
- tabIndex: this.state.activeIndex === index ? 0 : -1,
657
- disabled,
658
- 'aria-label': definition.ariaLabel || getAriaLabel(key),
659
- ...(key.actions || {}),
660
- ...(key.extraProps || {}),
661
- };
662
-
663
- if (key.icon) {
664
- const Icon = key.icon ? key.icon : 'div';
665
- return (
666
- <IconKey
667
- {...common}
668
- operator={operator}
669
- >
670
- <Icon className="icon" aria-hidden="true" />
671
- </IconKey>
672
- );
673
- }
674
-
675
- if (key.latex) {
676
- return (
677
- <MathKey
678
- {...common}
679
- operator={operator}
680
- >
681
- <MathPreviewRoot>
682
- <KeyVisual definition={definition} />
683
- </MathPreviewRoot>
684
- </MathKey>
685
- );
686
- }
687
-
688
- return (
689
- <TextKey
690
- {...common}
691
- operator={operator}
692
- >
693
- {key.label || key.write || key.name}
694
- </TextKey>
695
- );
696
- };
697
-
698
- render() {
699
- const { className, onFocus, layoutForKeyPad } = this.props;
700
- this.buttonRefs = [];
701
- const items = this.toRenderModel();
702
- const shift = items.length % 5 ? 1 : 0;
703
- const style = {
704
- gridTemplateColumns: `repeat(${Math.floor(items.length / 5) + shift}, minmax(min-content, 150px))`,
705
- ...createCustomLayout(layoutForKeyPad),
706
- };
707
- let indexCounter = 0;
708
- const renderWithRef = (definition) => {
709
- const currentIndex = indexCounter++;
710
- const node = this.renderKey(definition, currentIndex);
711
- return React.cloneElement(node, {
712
- ref: (r) => {
713
- this.buttonRefs[currentIndex] = r;
714
- },
715
- });
716
- };
717
-
718
- return (
719
- <KeypadRoot
720
- ref={this.keypadRef}
721
- className={[className, this.props.mode].filter(Boolean).join(' ')}
722
- style={style}
723
- onFocus={onFocus}
724
- role="grid"
725
- aria-label="Math keypad"
726
- >
727
- {items.map((definition) => renderWithRef(definition))}
728
- </KeypadRoot>
729
- );
730
- }
731
- }