@khanacademy/perseus 11.6.1 → 12.0.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/dist/es/index.js CHANGED
@@ -1,33 +1,33 @@
1
1
  import * as KAS from '@khanacademy/kas';
2
- import { keyTranslator, createMathField, mathQuillInstance, KeypadInput, KeypadType, keypadElementPropType, MobileKeypad } from '@khanacademy/math-input';
2
+ import { keyTranslator, CursorContext, createMathField, mathQuillInstance, getCursorContext, DesktopKeypad, KeypadInput, KeypadType, KeyArray, keypadElementPropType, MobileKeypad } from '@khanacademy/math-input';
3
3
  import * as PerseusLinter from '@khanacademy/perseus-linter';
4
4
  import { linterContextDefault, Rule, linterContextProps } from '@khanacademy/perseus-linter';
5
+ import { View, RenderStateRoot, Text } from '@khanacademy/wonder-blocks-core';
5
6
  import * as i18n from '@khanacademy/wonder-blocks-i18n';
7
+ import Tooltip$1 from '@khanacademy/wonder-blocks-tooltip';
6
8
  import classNames from 'classnames';
7
9
  import * as React from 'react';
8
10
  import { useState, useEffect, useRef } from 'react';
9
11
  import _, { debounce } from 'underscore';
12
+ import Clickable from '@khanacademy/wonder-blocks-clickable';
13
+ import Color, { fade } from '@khanacademy/wonder-blocks-color';
14
+ import { Popover, PopoverContentCore, PopoverContent } from '@khanacademy/wonder-blocks-popover';
15
+ import Spacing from '@khanacademy/wonder-blocks-spacing';
16
+ import { StyleSheet, css } from 'aphrodite';
10
17
  import $ from 'jquery';
18
+ import PropTypes from 'prop-types';
11
19
  import * as ReactDOM from 'react-dom';
12
20
  import ReactDOM__default from 'react-dom';
13
- import PropTypes from 'prop-types';
14
21
  import { number, point, vector as vector$1, line, ray } from '@khanacademy/kmath';
15
- import Spacing from '@khanacademy/wonder-blocks-spacing';
16
- import { StyleSheet, css } from 'aphrodite';
17
22
  import { CircularSpinner } from '@khanacademy/wonder-blocks-progress-spinner';
18
- import { RenderStateRoot, View, Text } from '@khanacademy/wonder-blocks-core';
19
23
  import SimpleMarkdown from '@khanacademy/simple-markdown';
20
24
  import { pureMarkdownRules } from '@khanacademy/pure-markdown';
21
25
  import { TextField } from '@khanacademy/wonder-blocks-form';
22
26
  import Button from '@khanacademy/wonder-blocks-button';
23
- import Clickable from '@khanacademy/wonder-blocks-clickable';
24
- import Color from '@khanacademy/wonder-blocks-color';
25
27
  import { Strut } from '@khanacademy/wonder-blocks-layout';
26
- import { Popover, PopoverContent, PopoverContentCore } from '@khanacademy/wonder-blocks-popover';
27
28
  import { OptionItem, SingleSelect } from '@khanacademy/wonder-blocks-dropdown';
28
29
  import debounce$1 from 'lodash.debounce';
29
30
  import createReactClass from 'create-react-class';
30
- import Tooltip$1 from '@khanacademy/wonder-blocks-tooltip';
31
31
  import Link from '@khanacademy/wonder-blocks-link';
32
32
  import { addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-core';
33
33
 
@@ -49,317 +49,6 @@ function _extends() {
49
49
  return _extends.apply(this, arguments);
50
50
  }
51
51
 
52
- /**
53
- * A stripped version of Icon.jsx from webapp. Takes an SVG icon and renders it
54
- * inline like Font Awesome did.
55
- *
56
- * If you are looking for an icon that we've used before you should look in
57
- * webapp's `icon-paths.js` which is a reference file for all the SVG paths
58
- * that we've used. You'll need to copy the object from that file into
59
- * whichever file you're using the icon and explicitly pass it in to the
60
- * <InlineIcon/> React component.
61
- *
62
- * We assume that the viewBox is cropped and aligned to (0, 0), but icons can
63
- * be defined differently. At some point we might want to add these attributes
64
- * to `icon-paths.js`, but for now this is a fairly safe assumption.
65
- *
66
- * Sample usage:
67
- *
68
- * const editIcon = {
69
- * path: "M41.209 53.753l5.39 0l0 5.39l3.136 0l6.468-6.517-8.477-8.526-6.517 6.517l0 3.136zm33.173-34.937q-.882-.882-1.862.049l-19.6 19.6q-.931.98-.049 1.862t1.862-.049l19.6-19.6q.931-.98.049-1.862zm-38.563 45.668l0-16.121l37.632-37.632 16.17 16.121-37.632 37.632l-16.17 0zm43.022-12.397l0 10.633q-.049 6.713-4.753 11.417t-11.368 4.704l-46.599 0q-6.713 0-11.417-4.753t-4.704-11.368l0-46.599q0-6.664 4.753-11.417t11.368-4.704l46.599 0q3.528 0 6.566 1.372.833.392.98 1.323t-.49 1.617l-2.744 2.744q-.784.784-1.96.441t-2.352-.343l-46.599 0q-3.675 0-6.321 2.646t-2.646 6.321l0 46.599q0 3.675 2.646 6.321t6.321 2.646l46.599 0q3.675 0 6.321-2.646t2.646-6.321l0-7.056q0-.735.49-1.225l3.577-3.577q.833-.833 1.96-.392t1.127 1.617zm7.203-51.646q2.254 0 3.773 1.568l8.526 8.526q1.568 1.568 1.568 3.822t-1.568 3.773l-5.145 5.145-16.121-16.121 5.145-5.145q1.568-1.568 3.822-1.568z",
70
- * width: 100,
71
- * height: 78.912,
72
- * };
73
- * <InlineIcon {...editIcon} />
74
- *
75
- */
76
- const InlineIcon = ({
77
- path,
78
- width,
79
- height,
80
- style: _style = {},
81
- title
82
- }) => /*#__PURE__*/React.createElement("svg", {
83
- role: "img",
84
- "aria-hidden": !title,
85
- style: _extends({
86
- verticalAlign: "middle"
87
- }, _style),
88
- width: `${width / height}em`,
89
- height: "1em",
90
- viewBox: `0 0 ${width} ${height}`
91
- }, !!title && /*#__PURE__*/React.createElement("title", null, title), /*#__PURE__*/React.createElement("path", {
92
- d: path,
93
- fill: "currentColor"
94
- }));
95
-
96
- let _dependencies = null;
97
- const setDependencies = dependencies => {
98
- _dependencies = dependencies;
99
- };
100
- const getDependencies = () => {
101
- if (_dependencies) {
102
- return _dependencies;
103
- }
104
- throw new Error(["Perseus has not been provided required dependencies.", "setDependencies(dependencies) must be called first.", "Make sure Perseus is being imported from javascript/perseus/perseus.js."].join("\n"));
105
- };
106
- const DependenciesContext = /*#__PURE__*/React.createContext({
107
- analytics: {
108
- onAnalyticsEvent: async () => {}
109
- }
110
- });
111
-
112
- /**
113
- * useDependencies provides easy access to the current Perseus dependencies.
114
- */
115
- const useDependencies = () => {
116
- const deps = React.useContext(DependenciesContext);
117
- return deps;
118
- };
119
-
120
- var dependencies = /*#__PURE__*/Object.freeze({
121
- __proto__: null,
122
- setDependencies: setDependencies,
123
- getDependencies: getDependencies,
124
- DependenciesContext: DependenciesContext,
125
- useDependencies: useDependencies
126
- });
127
-
128
- /* eslint-disable @khanacademy/ts-no-error-suppressions */
129
- const prettyBig = {
130
- fontSize: "150%"
131
- };
132
- const slightlyBig = {
133
- fontSize: "120%"
134
- };
135
- const symbStyle = {
136
- fontSize: "130%"
137
- };
138
- // These are functions because we want to generate a new component for each use
139
- // on the page rather than reusing an instance (which will cause an error).
140
- // Also, it's useful for things which might look different depending on the
141
- // props.
142
-
143
- const basic = [() => [/*#__PURE__*/React.createElement("span", {
144
- key: "plus",
145
- style: slightlyBig
146
- }, "+"), "PLUS", i18n._("Plus")], () => [/*#__PURE__*/React.createElement("span", {
147
- key: "minus",
148
- style: prettyBig
149
- }, "-"), "MINUS", i18n._("Minus")],
150
- // TODO(joel) - display as \cdot when appropriate
151
- props => {
152
- const {
153
- TeX
154
- } = getDependencies();
155
- if (props.convertDotToTimes) {
156
- return [/*#__PURE__*/React.createElement(TeX, {
157
- key: "times",
158
- style: prettyBig
159
- }, i18n.doNotTranslate("\\times")), "TIMES", i18n._("Multiply")];
160
- }
161
- return [/*#__PURE__*/React.createElement(TeX, {
162
- key: "times",
163
- style: prettyBig
164
- }, i18n.doNotTranslate("\\cdot")), "CDOT", i18n._("Multiply")];
165
- }, () => {
166
- const {
167
- TeX
168
- } = getDependencies();
169
- return [/*#__PURE__*/React.createElement(TeX, {
170
- key: "frac",
171
- style: prettyBig
172
- }, i18n.doNotTranslate("\\frac{x}{y}")), "FRAC", i18n._("Fraction, excluding the current expression")];
173
- }];
174
- const buttonSets = {
175
- basic,
176
- "basic+div": basic.concat([() => {
177
- const {
178
- TeX
179
- } = getDependencies();
180
- return [/*#__PURE__*/React.createElement(TeX, {
181
- key: "div"
182
- }, i18n.doNotTranslate("\\div")), "DIVIDE", i18n._("Divide")];
183
- }]),
184
- trig: [() => {
185
- const {
186
- TeX
187
- } = getDependencies();
188
- return [/*#__PURE__*/React.createElement(TeX, {
189
- key: "sin"
190
- }, i18n.doNotTranslate("\\sin")), "SIN", i18n._("Sine")];
191
- }, () => {
192
- const {
193
- TeX
194
- } = getDependencies();
195
- return [/*#__PURE__*/React.createElement(TeX, {
196
- key: "cos"
197
- }, i18n.doNotTranslate("\\cos")), "COS", i18n._("Cosine")];
198
- }, () => {
199
- const {
200
- TeX
201
- } = getDependencies();
202
- return [/*#__PURE__*/React.createElement(TeX, {
203
- key: "tan"
204
- }, i18n.doNotTranslate("\\tan")), "TAN", i18n._("Tangent")];
205
- }, () => {
206
- const {
207
- TeX
208
- } = getDependencies();
209
- return [/*#__PURE__*/React.createElement(TeX, {
210
- key: "theta",
211
- style: symbStyle
212
- }, i18n.doNotTranslate("\\theta")), "THETA", i18n._("Theta")];
213
- }, () => {
214
- const {
215
- TeX
216
- } = getDependencies();
217
- return [/*#__PURE__*/React.createElement("span", {
218
- key: "phi"
219
- }, /*#__PURE__*/React.createElement("span", {
220
- className: "perseus-sr-only"
221
- }, i18n.doNotTranslate("phi")), /*#__PURE__*/React.createElement(TeX, {
222
- key: "pi",
223
- style: symbStyle
224
- }, i18n.doNotTranslate("\\phi"))), "PHI", i18n._("Phi")];
225
- }],
226
- prealgebra: [() => {
227
- const {
228
- TeX
229
- } = getDependencies();
230
- return [/*#__PURE__*/React.createElement(TeX, {
231
- key: "sqrt"
232
- }, i18n.doNotTranslate("\\sqrt{x}")), "SQRT", i18n._("Square root")];
233
- },
234
- // TODO(joel) - how does desmos do this?
235
- () => {
236
- const {
237
- TeX
238
- } = getDependencies();
239
- return [/*#__PURE__*/React.createElement(TeX, {
240
- key: "nthroot"
241
- }, i18n.doNotTranslate("\\sqrt[3]{x}")), "NTHROOT3", i18n._("Cube root")];
242
- }, () => {
243
- const {
244
- TeX
245
- } = getDependencies();
246
- return [/*#__PURE__*/React.createElement(TeX, {
247
- key: "pow",
248
- style: slightlyBig
249
- }, i18n.doNotTranslate("a^b")), "POW", i18n._("Exponent")];
250
- }, () => {
251
- const {
252
- TeX
253
- } = getDependencies();
254
- return [/*#__PURE__*/React.createElement(TeX, {
255
- key: "pi",
256
- style: slightlyBig
257
- }, i18n.doNotTranslate("\\pi")), "PI", i18n._("Pi")];
258
- }],
259
- logarithms: [() => {
260
- const {
261
- TeX
262
- } = getDependencies();
263
- return [/*#__PURE__*/React.createElement(TeX, {
264
- key: "log"
265
- }, i18n.doNotTranslate("\\log")), "LOG", i18n._("Logarithm with base 10")];
266
- }, () => {
267
- const {
268
- TeX
269
- } = getDependencies();
270
- return [/*#__PURE__*/React.createElement(TeX, {
271
- key: "ln"
272
- }, i18n.doNotTranslate("\\ln")), "LN", i18n._("Natural logarithm")];
273
- }, () => {
274
- const {
275
- TeX
276
- } = getDependencies();
277
- return [/*#__PURE__*/React.createElement(TeX, {
278
- key: "log_b"
279
- }, i18n.doNotTranslate("\\log_b")), "LOG_B", i18n._("Logarithm with custom base")];
280
- }],
281
- "basic relations": [() => {
282
- const {
283
- TeX
284
- } = getDependencies();
285
- return [/*#__PURE__*/React.createElement(TeX, {
286
- key: "eq"
287
- }, "="), "EQUAL", i18n._("Equals sign")];
288
- }, () => {
289
- const {
290
- TeX
291
- } = getDependencies();
292
- return [/*#__PURE__*/React.createElement(TeX, {
293
- key: "lt"
294
- }, i18n.doNotTranslate("\\lt")), "LT", i18n._("Less than sign")];
295
- }, () => {
296
- const {
297
- TeX
298
- } = getDependencies();
299
- return [/*#__PURE__*/React.createElement(TeX, {
300
- key: "gt"
301
- }, i18n.doNotTranslate("\\gt")), "GT", i18n._("Greater than sign")];
302
- }],
303
- "advanced relations": [() => {
304
- const {
305
- TeX
306
- } = getDependencies();
307
- return [/*#__PURE__*/React.createElement(TeX, {
308
- key: "neq"
309
- }, i18n.doNotTranslate("\\neq")), "NEQ", i18n._("Not-equals sign")];
310
- }, () => {
311
- const {
312
- TeX
313
- } = getDependencies();
314
- return [/*#__PURE__*/React.createElement(TeX, {
315
- key: "leq"
316
- }, i18n.doNotTranslate("\\leq")), "LEQ", i18n._("Less than or equal to sign")];
317
- }, () => {
318
- const {
319
- TeX
320
- } = getDependencies();
321
- return [/*#__PURE__*/React.createElement(TeX, {
322
- key: "geq"
323
- }, i18n.doNotTranslate("\\geq")), "GEQ", i18n._("Greater than or equal to sign")];
324
- }]
325
- };
326
- const buttonSetsPropType = PropTypes.arrayOf(PropTypes.oneOf(_(buttonSets).keys()));
327
- class TexButtons extends React.Component {
328
- render() {
329
- // Always show buttonSets in the same order. Note: Technically it's ok
330
- // for _.keys() to return the keys in an arbitrary order, but in
331
- // practice, they will be ordered as listed above.
332
- const sortedButtonSets = _.sortBy(this.props.sets, setName =>
333
- // @ts-expect-error - TS2345 - Argument of type 'string | number' is not assignable to parameter of type 'string'.
334
- _.keys(buttonSets).indexOf(setName));
335
- const buttons = _(sortedButtonSets).map(setName => buttonSets[setName]);
336
- const buttonRows = _(buttons).map(row => row.map(symbGen => {
337
- // create a (component, thing we should send to mathquill) pair
338
- const symbol = symbGen(this.props);
339
- return /*#__PURE__*/React.createElement("button", {
340
- onClick: () => this.props.onInsert(symbol[1]),
341
- className: "tex-button"
342
- // @ts-expect-error - TS2533 - Object is possibly 'null' or 'undefined'. | TS2339 - Property 'key' does not exist on type 'boolean | ReactChild | ReactFragment | ReactPortal'.
343
- ,
344
- key: symbol[0].key,
345
- type: "button",
346
- "aria-label": symbol[2]
347
- }, symbol[0]);
348
- }));
349
- const buttonPopup = _(buttonRows).map((row, i) => {
350
- return /*#__PURE__*/React.createElement("div", {
351
- className: "clearfix tex-button-row",
352
- key: this.props.sets[i]
353
- }, row);
354
- });
355
- return /*#__PURE__*/React.createElement("div", {
356
- className: `${this.props.className || ""} preview-measure`
357
- }, buttonPopup);
358
- }
359
- }
360
- TexButtons.buttonSets = buttonSets;
361
- TexButtons.buttonSetsType = buttonSetsPropType;
362
-
363
52
  const customKeyTranslator = _extends({}, keyTranslator, {
364
53
  // If there's something in the input that can become part of a
365
54
  // fraction, typing "/" puts it in the numerator. If not, typing
@@ -381,57 +70,9 @@ class MathInput extends React.Component {
381
70
  this.__mathFieldWrapperRef = null;
382
71
  this.__mathField = null;
383
72
  this.state = {
384
- focused: false
385
- };
386
- this.handleFocus = () => {
387
- this.setState({
388
- focused: true
389
- });
390
- // TODO(joel) fix properly - we should probably allow onFocus handlers
391
- // to this property, but we need to work correctly with them.
392
- // if (this.props.onFocus) {
393
- // this.props.onFocus();
394
- // }
395
- };
396
- this.handleMouseDown = event => {
397
- // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2345 - Argument of type 'EventTarget | null' is not assignable to parameter of type 'Node | null'.
398
- const focused = ReactDOM__default.findDOMNode(this).contains(event.target);
399
- this.mouseDown = focused;
400
- if (!focused) {
401
- this.setState({
402
- focused: false
403
- });
404
- }
405
- };
406
- this.handleMouseUp = () => {
407
- // this mouse click started in the buttons div so we should focus the
408
- // input
409
- if (this.mouseDown) {
410
- this.focus();
411
- }
412
- this.mouseDown = false;
413
- };
414
- this.handleBlur = e => {
415
- // TODO(michaelpolyak): Consider trapping focus within the button group.
416
- // Focusing back on the input when TAB out of the last button in the
417
- // group. This will probably require ESCAPE key handling to enable to
418
- // close (blur) the button group in order to focus on next page element.
419
- if (!this.mouseDown &&
420
- // @ts-expect-error - TS2531 - Object is possibly 'null'.
421
- !ReactDOM__default.findDOMNode(this).contains(e.relatedTarget)) {
422
- this.setState({
423
- focused: false
424
- });
425
- }
426
- };
427
- this._shouldShowButtons = () => {
428
- if (this.props.buttonsVisible === "always") {
429
- return true;
430
- }
431
- if (this.props.buttonsVisible === "never") {
432
- return false;
433
- }
434
- return this.state.focused;
73
+ focused: false,
74
+ keypadOpen: this.props.buttonsVisible === "always" ? true : false,
75
+ cursorContext: CursorContext.NONE
435
76
  };
436
77
  this.insert = value => {
437
78
  const input = this.mathField();
@@ -493,6 +134,9 @@ class MathInput extends React.Component {
493
134
  if (this.props.value !== value) {
494
135
  this.props.onChange(value);
495
136
  }
137
+ this.setState({
138
+ cursorContext: getCursorContext(mathField)
139
+ });
496
140
  },
497
141
  enter: () => {
498
142
  // NOTE(kevinb): This isn't how answers to exercises are
@@ -525,479 +169,250 @@ class MathInput extends React.Component {
525
169
  focused: true
526
170
  });
527
171
  };
528
- this.blur = () => {
529
- var _this$mathField2;
530
- (_this$mathField2 = this.mathField()) == null ? void 0 : _this$mathField2.blur();
531
- this.setState({
532
- focused: false
533
- });
534
- };
535
- }
536
- componentDidMount() {
537
- var _this$mathField3;
538
- window.addEventListener("mousedown", this.handleMouseDown);
539
- window.addEventListener("mouseup", this.handleMouseUp);
540
-
541
- // Ideally, we would be able to pass an initial value directly into
542
- // the constructor
543
- (_this$mathField3 = this.mathField()) == null ? void 0 : _this$mathField3.latex(this.props.value);
544
- }
545
- componentWillUnmount() {
546
- window.removeEventListener("mousedown", this.handleMouseDown);
547
- window.removeEventListener("mouseup", this.handleMouseUp);
548
- }
549
-
550
- // handlers:
551
- // keep track of two related bits of state:
552
- // * this.state.focused - whether the buttons are currently shown
553
- // * this.mouseDown - whether a mouse click is active that started in the
554
- // buttons div
555
-
556
- render() {
557
- let className = classNames({
558
- "perseus-math-input": true,
559
- // mathquill usually adds these itself but react removes them when
560
- // updating the component.
561
- "mq-editable-field": true,
562
- "mq-math-mode": true
172
+ this.blur = () => this.setState({
173
+ focused: false
563
174
  });
564
- if (this.props.className) {
565
- className = className + " " + this.props.className;
566
- }
567
- let buttons = null;
568
- if (this._shouldShowButtons()) {
569
- // @ts-expect-error - TS2322 - Type 'Element' is not assignable to type 'null'.
570
- buttons = /*#__PURE__*/React.createElement(TexButtons, {
571
- sets: this.props.buttonSets,
572
- className: "math-input-buttons absolute",
573
- convertDotToTimes: this.props.convertDotToTimes,
574
- onInsert: this.insert
575
- });
576
- }
577
- return /*#__PURE__*/React.createElement("div", {
578
- style: {
579
- display: "inline-block"
580
- }
581
- }, /*#__PURE__*/React.createElement("div", {
582
- style: {
583
- display: "inline-block"
175
+ this.handleKeypadPress = (key, e) => {
176
+ const translator = keyTranslator[key];
177
+ const mathField = this.mathField();
178
+ if (mathField) {
179
+ if (translator) {
180
+ translator(mathField, key);
181
+ }
182
+ this.setState({
183
+ cursorContext: getCursorContext(mathField)
184
+ });
584
185
  }
585
- }, /*#__PURE__*/React.createElement("span", {
586
- className: className,
587
- ref: ref => this.__mathFieldWrapperRef = ref,
588
- "aria-label": this.props.labelText,
589
- onFocus: this.handleFocus,
590
- onBlur: this.handleBlur
591
- })), /*#__PURE__*/React.createElement("div", {
592
- style: {
593
- position: "relative"
594
- },
595
- onBlur: this.handleBlur
596
- }, buttons));
597
- }
598
- }
599
- MathInput.defaultProps = {
600
- value: "",
601
- convertDotToTimes: false,
602
- buttonsVisible: "focused"
603
- };
604
-
605
- /* eslint-disable react/no-unsafe */
606
- const zIndex = 10;
607
- let HorizontalDirection = /*#__PURE__*/function (HorizontalDirection) {
608
- HorizontalDirection["Left"] = "left";
609
- HorizontalDirection["Right"] = "right";
610
- return HorizontalDirection;
611
- }({});
612
- let VerticalDirection = /*#__PURE__*/function (VerticalDirection) {
613
- VerticalDirection["Top"] = "top";
614
- VerticalDirection["Bottom"] = "bottom";
615
- return VerticalDirection;
616
- }({});
617
- const Triangle = props => {
618
- let borderLeft;
619
- let borderRight;
620
- let borderTop;
621
- let borderBottom;
622
- const hBorder = `${props.width}px solid transparent`;
623
- if (props.horizontalDirection === "right") {
624
- borderLeft = hBorder;
625
- } else {
626
- borderRight = hBorder;
627
- }
628
- const vBorder = `${props.height}px solid ${props.color}`;
629
- if (props.verticalDirection === "top") {
630
- borderTop = vBorder;
631
- } else {
632
- borderBottom = vBorder;
633
- }
634
- return /*#__PURE__*/React.createElement("div", {
635
- style: {
636
- display: "block",
637
- height: 0,
638
- width: 0,
639
- position: "absolute",
640
- left: props.left,
641
- top: props["top"],
642
- borderLeft: borderLeft,
643
- borderRight: borderRight,
644
- borderTop: borderTop,
645
- borderBottom: borderBottom
646
- }
647
- });
648
- };
649
- const TooltipArrow = props => {
650
- // TODO(aria): Think about adding a box-shadow to the triangle here
651
- // See http://css-tricks.com/triangle-with-shadow/
652
-
653
- //const isRight = (this.props.horizontalDirection === "right");
654
- const isTop = props.verticalDirection === "top";
655
- const frontTopOffset = isTop ? 0 : 1;
656
- const borderTopOffset = isTop ? 0 : -1;
657
- return /*#__PURE__*/React.createElement("div", {
658
- style: {
659
- display: "block",
660
- position: props.position,
661
- visibility: props.visibility,
662
- left: props.left,
663
- top: props["top"],
664
- width: props.width + 2,
665
- height: props.height + 1,
666
- marginTop: -1,
667
- marginBottom: -2,
668
- zIndex: zIndex
669
- }
670
- }, /*#__PURE__*/React.createElement(Triangle, {
671
- horizontalDirection: props.horizontalDirection,
672
- verticalDirection: props.verticalDirection,
673
- color: props.border,
674
- left: 0,
675
- top: borderTopOffset,
676
- width: props.width + 2 // one extra for the diagonal
677
- ,
678
- height: props.height + 2
679
- }), /*#__PURE__*/React.createElement(Triangle, {
680
- horizontalDirection: props.horizontalDirection,
681
- verticalDirection: props.verticalDirection,
682
- color: props.color,
683
- left: 1,
684
- top: frontTopOffset,
685
- width: props.width,
686
- height: props.height
687
- }));
688
- };
689
- TooltipArrow.defaultProps = {
690
- position: "relative",
691
- visibility: "visible",
692
- left: 0,
693
- top: 0
694
- };
695
- const VERTICAL_CORNERS = {
696
- top: {
697
- top: "-100%"
698
- },
699
- bottom: {
700
- top: 0
701
- }
702
- };
703
- const HORIZONTAL_CORNERS = {
704
- left: {
705
- targetLeft: 0
706
- },
707
- right: {
708
- targetLeft: "100%"
709
- }
710
- };
711
- const HORIZONTAL_ALIGNMNENTS = {
712
- left: {
713
- tooltipLeft: 0,
714
- arrowLeft: arrowSize => 0
715
- },
716
- right: {
717
- tooltipLeft: "-100%",
718
- arrowLeft: arrowSize => -arrowSize - 2
719
- }
720
- };
721
- class Tooltip extends React.Component {
722
- constructor(...args) {
723
- super(...args);
724
- this.state = {
725
- height: null // used for offsetting "top" positioned tooltips
726
- };
727
- }
728
186
 
729
- componentDidMount() {
730
- this._updateHeight();
731
- }
732
- UNSAFE_componentWillReceiveProps() {
733
- // If the contents have changed, reset our measure of the height
734
- this.setState({
735
- height: null
736
- });
737
- }
738
- componentDidUpdate() {
739
- this._updateHeight();
740
- }
741
- _renderToolTipDiv(isTooltipAbove) {
742
- const settings = Object.assign({}, HORIZONTAL_CORNERS[this.props.horizontalPosition], HORIZONTAL_ALIGNMNENTS[this.props.horizontalAlign], VERTICAL_CORNERS[this.props.verticalPosition]);
743
- let arrowAbove;
744
- let arrowBelow;
745
- if (isTooltipAbove) {
746
- // We put an absolutely positioned arrow in the correct place
747
- arrowAbove = /*#__PURE__*/React.createElement(TooltipArrow, {
748
- verticalDirection: VerticalDirection.Top,
749
- horizontalDirection: this.props.horizontalAlign,
750
- position: "absolute",
751
- color: "white",
752
- border: this.props.borderColor,
753
- left: settings.arrowLeft(this.props.arrowSize),
754
- top: -this.props.arrowSize + 2,
755
- width: this.props.arrowSize,
756
- height: this.props.arrowSize,
757
- zIndex: zIndex
758
- });
759
-
760
- // And we use a visibility: hidden arrow below to shift up the
761
- // content by the correct amount
762
- arrowBelow = /*#__PURE__*/React.createElement(TooltipArrow, {
763
- verticalDirection: VerticalDirection.Top,
764
- horizontalDirection: this.props.horizontalAlign,
765
- visibility: "hidden",
766
- color: "white",
767
- border: this.props.borderColor,
768
- left: settings.arrowLeft(this.props.arrowSize),
769
- top: -1,
770
- width: this.props.arrowSize,
771
- height: this.props.arrowSize,
772
- zIndex: zIndex
773
- });
774
- } else {
775
- arrowAbove = /*#__PURE__*/React.createElement(TooltipArrow, {
776
- verticalDirection: VerticalDirection.Bottom,
777
- horizontalDirection: this.props.horizontalAlign,
778
- color: "white",
779
- border: this.props.borderColor,
780
- left: settings.arrowLeft(this.props.arrowSize),
781
- top: -1,
782
- width: this.props.arrowSize,
783
- height: this.props.arrowSize,
784
- zIndex: zIndex
785
- });
786
- arrowBelow = null;
787
- }
788
-
789
- /* A positioned div below the input to be the parent for our
790
- tooltip */
791
- return /*#__PURE__*/React.createElement("div", {
792
- style: {
793
- position: "relative",
794
- height: 0,
795
- display: this.props.show ? "block" : "none"
796
- }
797
- }, /*#__PURE__*/React.createElement("div", {
798
- // eslint-disable-next-line react/no-string-refs
799
- ref: "tooltipContainer",
800
- className: "tooltipContainer",
801
- style: {
802
- position: "absolute",
803
- // height must start out undefined, not null, so that
804
- // we can measure the actual height with jquery.
805
- // This is used to position the tooltip with top: -100%
806
- // when in verticalPosition: "top" mode
807
- height: this.state.height || undefined,
808
- left: settings.targetLeft
809
- }
810
- }, arrowAbove, /*#__PURE__*/React.createElement("div", {
811
- className: this.props.className
812
- // eslint-disable-next-line react/no-string-refs
813
- ,
814
- ref: "tooltipContent",
815
- style: {
816
- position: "relative",
817
- top: settings["top"],
818
- left: settings.tooltipLeft,
819
- border: "1px solid " + this.props.borderColor,
820
- boxShadow: "0 1px 3px " + this.props.borderColor,
821
- zIndex: zIndex - 1
187
+ // We want to prevent taking focus from input when clicking on keypad
188
+ // Clickable handles "onClick" differently than react;
189
+ // a keyboard event is "keydown" type.
190
+ // In react without WonderBlocks, "enter" or "space" keydown events
191
+ // are also "click" events, differentiated by "detail".
192
+ if (e.type === "click") {
193
+ this.focus();
822
194
  }
823
- }, this.props.children.slice(1)), arrowBelow));
195
+ };
824
196
  }
825
- _updateHeight() {
826
- const tooltipContainer = ReactDOM__default.findDOMNode(
827
- // eslint-disable-next-line react/no-string-refs
828
- this.refs.tooltipContainer);
829
- const height = tooltipContainer.offsetHeight;
830
- if (height !== this.state.height) {
831
- this.setState({
832
- height
833
- });
197
+ componentDidMount() {
198
+ var _this$mathField2;
199
+ // Ideally, we would be able to pass an initial value directly into
200
+ // the constructor
201
+ (_this$mathField2 = this.mathField()) == null ? void 0 : _this$mathField2.latex(this.props.value);
202
+ }
203
+ openKeypad() {
204
+ if (this.props.buttonsVisible === "never") {
205
+ return;
834
206
  }
207
+ this.setState({
208
+ keypadOpen: true
209
+ });
210
+ }
211
+ closeKeypad() {
212
+ this.setState({
213
+ keypadOpen: false
214
+ });
835
215
  }
836
216
  render() {
837
- const isTooltipAbove = this.props.verticalPosition === "top";
838
-
839
- /* We wrap the entire output in a span so that it displays inline */
840
- return /*#__PURE__*/React.createElement("span", null, isTooltipAbove && this._renderToolTipDiv(isTooltipAbove), /*#__PURE__*/React.createElement("div", {
841
- style: this.props.targetContainerStyle
842
- }, this.props.children[0]), !isTooltipAbove && this._renderToolTipDiv());
217
+ let className = classNames({
218
+ "perseus-math-input": true,
219
+ // mathquill usually adds these itself but react removes them when
220
+ // updating the component.
221
+ "mq-editable-field": true,
222
+ "mq-math-mode": true
223
+ });
224
+ if (this.props.className) {
225
+ className = className + " " + this.props.className;
226
+ }
227
+ return /*#__PURE__*/React.createElement(View, {
228
+ style: [styles$A.outerWrapper, this.state.focused && styles$A.wrapperFocused, this.props.hasError && styles$A.wrapperError]
229
+ }, /*#__PURE__*/React.createElement("div", {
230
+ style: {
231
+ display: "flex",
232
+ padding: 1
233
+ },
234
+ onClick: () => {
235
+ const mathField = this.mathField();
236
+ if (!mathField) {
237
+ return;
238
+ }
239
+ this.setState({
240
+ cursorContext: getCursorContext(mathField)
241
+ });
242
+ }
243
+ }, /*#__PURE__*/React.createElement("span", {
244
+ className: className,
245
+ ref: ref => this.__mathFieldWrapperRef = ref,
246
+ "aria-label": this.props.labelText,
247
+ onFocus: () => this.focus(),
248
+ onBlur: () => this.blur()
249
+ }), /*#__PURE__*/React.createElement(Popover, {
250
+ opened: this.state.keypadOpen,
251
+ onClose: () => this.closeKeypad(),
252
+ dismissEnabled: true,
253
+ content: () => {
254
+ var _this$props$keypadBut, _this$props;
255
+ return /*#__PURE__*/React.createElement(PopoverContentCore, {
256
+ closeButtonVisible: true,
257
+ style: styles$A.popoverContent
258
+ }, /*#__PURE__*/React.createElement(DesktopKeypad, _extends({
259
+ onAnalyticsEvent: this.props.analytics.onAnalyticsEvent,
260
+ extraKeys: this.props.extraKeys,
261
+ onClickKey: this.handleKeypadPress,
262
+ cursorContext: this.state.cursorContext,
263
+ convertDotToTimes: this.props.convertDotToTimes
264
+ }, (_this$props$keypadBut = this.props.keypadButtonSets) != null ? _this$props$keypadBut : mapButtonSets((_this$props = this.props) == null ? void 0 : _this$props.buttonSets))));
265
+ }
266
+ }, this.props.buttonsVisible === "never" ? /*#__PURE__*/React.createElement(MathInputIcon, {
267
+ hovered: false,
268
+ focused: false,
269
+ active: false
270
+ }) : /*#__PURE__*/React.createElement(Clickable, {
271
+ "aria-label": this.state.keypadOpen ? i18n._("close math keypad") : i18n._("open math keypad"),
272
+ "aria-checked": this.state.keypadOpen,
273
+ role: "switch",
274
+ onClick: () => this.state.keypadOpen ? this.closeKeypad() : this.openKeypad()
275
+ }, props => /*#__PURE__*/React.createElement(MathInputIcon, _extends({
276
+ active: this.state.keypadOpen
277
+ }, props))))));
843
278
  }
844
279
  }
845
-
846
- // Sorry. // Apology-Oriented-Programming
847
- Tooltip.defaultProps = {
848
- className: "",
849
- arrowSize: 10,
850
- borderColor: "#ccc",
851
- verticalPosition: VerticalDirection.Bottom,
852
- horizontalPosition: HorizontalDirection.Left,
853
- horizontalAlign: HorizontalDirection.Left,
854
- targetContainerStyle: {}
855
- };
856
-
857
- /**
858
- * Icon paths to be used with `inline-icon.jsx`.
859
- *
860
- * These paths are taken directly from webapp's `icon-paths.js`. Unlike the
861
- * webapp equivalent, these can be directly required within Perseus files since
862
- * this is all bundled together anyway.
863
- */
864
-
865
- const iconCheck = {
866
- path: `M8.70710678,12.2928932 C8.31658249,11.9023689 7.68341751,11.9023689 7.29289322,12.2928932 C6.90236893,12.6834175 6.90236893,13.3165825 7.29289322,13.7071068 L9.82842712,16.2426407 C10.2207367,16.6349502 10.8574274,16.6328935 11.2471942,16.2380576 L16.7116603,10.7025237 C17.0996535,10.3094846 17.0955629,9.67633279 16.7025237,9.28833966 C16.3094846,8.90034653 15.6763328,8.90443714 15.2883397,9.29747629 L10.5309507,14.1167372 L8.70710678,12.2928932 Z`,
867
- width: 24,
868
- height: 24
869
- };
870
- const iconCheckMedium = {
871
- path: "M6.072 10.4l6.175-7.058a1 1 0 1 1 1.506 1.317L6.769 12.64a1 1 0 0 1-1.55-.054L2.203 8.604a1 1 0 1 1 1.594-1.208L6.072 10.4z",
872
- width: 16,
873
- height: 16
874
- };
875
- const iconChevronDown = {
876
- path: "M99.669 13.048q0 3.36-2.352 5.712l-41.664 41.664q-2.408 2.408-5.88 2.408t-5.712-2.408l-41.664-41.664q-2.408-2.24-2.408-5.712t2.408-5.88l4.76-4.816q2.52-2.352 5.88-2.352t5.656 2.352l31.136 31.136 31.08-31.136q2.352-2.352 5.712-2.352t5.88 2.352l4.816 4.816q2.352 2.52 2.352 5.88z",
877
- width: 100,
878
- height: 63.034
879
- };
880
- const iconChevronRight = {
881
- path: "M62.808 49.728q0 3.36-2.352 5.88l-41.72 41.664q-2.352 2.408-5.768 2.408t-5.768-2.408l-4.872-4.76q-2.352-2.52-2.352-5.88t2.352-5.712l31.08-31.136-31.08-31.024q-2.352-2.52-2.352-5.88t2.352-5.712l4.872-4.76q2.296-2.408 5.768-2.408t5.768 2.408l41.72 41.664q2.352 2.296 2.352 5.656z",
882
- width: 63.034,
883
- height: 100
884
- };
885
- const iconCircle = {
886
- path: "M100.035 50.046q.057 13.623-6.669 25.137t-18.24 18.183-25.08 6.669-25.137-6.726q-11.514-6.726-18.183-18.183-6.726-11.571-6.726-25.137t6.726-25.08 18.24-18.24 25.08-6.669q13.566 0 25.08 6.726 11.514 6.669 18.24 18.183t6.669 25.137z",
887
- width: 100,
888
- height: 100
889
- };
890
- const iconCircleArrowDown = {
891
- path: "M50.046 83.676q1.767 0 2.907-1.14l29.526-29.526q1.197-1.197 1.197-2.907t-1.197-2.964l-5.928-5.928q-1.197-1.14-2.964-1.14t-2.907 1.14l-12.312 12.312l0-32.661q0-1.71-1.254-2.964t-2.907-1.254l-8.322 0q-1.71 0-2.964 1.254t-1.254 2.964l0 32.661l-12.312-12.312q-1.197-1.254-2.907-1.254t-2.907 1.254l-5.928 5.928q-1.197 1.197-1.197 2.964t1.197 2.907l29.469 29.526q1.197 1.14 2.964 1.14zm49.989-33.63q.057 13.623-6.669 25.137t-18.24 18.183-25.08 6.669-25.137-6.726q-11.514-6.726-18.183-18.183-6.726-11.571-6.726-25.137t6.726-25.08 18.24-18.24 25.08-6.669q13.566 0 25.08 6.726 11.514 6.669 18.24 18.183t6.669 25.137z",
892
- width: 100,
893
- height: 100
894
- };
895
- const iconCircleArrowUp = {
896
- path: "M54.207 83.391q1.653 0 2.907-1.254t1.254-2.907l0-32.718l12.312 12.312q1.254 1.254 2.964 1.254t2.907-1.254l5.928-5.928q1.197-1.197 1.14-2.964 0-1.767-1.14-2.907l-29.526-29.526q-1.197-1.14-2.907-1.14t-2.964 1.14l-29.469 29.526q-1.197 1.254-1.197 2.964t1.197 2.907l5.928 5.928q1.197 1.197 2.907 1.197t2.907-1.197l12.312-12.312l0 32.718q0 1.653 1.254 2.907t2.964 1.254l8.322 0zm45.828-33.345q.057 13.623-6.669 25.137t-18.24 18.183-25.08 6.669-25.137-6.726q-11.514-6.726-18.183-18.183-6.726-11.571-6.726-25.137t6.726-25.08 18.24-18.24 25.08-6.669q13.566 0 25.08 6.726 11.514 6.669 18.24 18.183t6.669 25.137z",
897
- width: 100,
898
- height: 100
899
- };
900
- const iconCircleThin = {
901
- path: "M50.046 8.322q-8.493 0-16.188 3.306-15.561 6.669-22.173 22.23-3.363 7.695-3.363 16.188t3.306 16.188 8.949 13.281q5.586 5.586 13.281 8.892t16.188 3.306 16.188-3.306 13.281-8.892 8.892-13.281 3.306-16.188-3.306-16.188-8.892-13.281-13.281-8.949q-7.695-3.306-16.188-3.306zm0 91.713q-13.623 0-25.137-6.726t-18.183-18.183q-6.726-11.571-6.726-25.137t6.726-25.08 18.24-18.24 25.08-6.669q13.566 0 25.08 6.726 11.514 6.669 18.24 18.183t6.726 25.137-6.726 25.137-18.24 18.126q-11.514 6.726-25.08 6.726z",
902
- width: 100,
903
- height: 99.944
904
- };
905
- const iconDesktop = {
906
- path: "M94.208 52.119l0-43.746q0-.69-.506-1.15t-1.196-.506l-84.088 0q-.69 0-1.196.506t-.506 1.15l0 43.746q0 .69.506 1.196t1.196.506l84.088 0q.69 0 1.196-.506t.506-1.196zm6.716-43.746l0 57.224q0 3.45-2.484 5.934t-5.934 2.484l-28.566 0q0 3.128 2.53 7.774.828 1.61.828 2.622t-1.012 2.07q-1.012 1.012-2.346.966l-26.91 0q-1.38 0-2.392-1.012t-1.012-2.024q0-1.058 1.656-4.14t1.748-6.256l-28.612 0q-3.45 0-5.934-2.484t-2.484-5.934l0-57.224q0-3.45 2.484-5.934t5.934-2.438l84.088 0q3.45 0 5.98 2.438 2.438 2.484 2.438 5.934z",
907
- width: 100,
908
- height: 86.648
909
- };
910
- const iconDropdownArrow = {
911
- path: "M9 9.8c0 .5.7 1.7 1.5 2.8 1.5 1.9 1.5 1.9 3 0C15.7 9.7 15.4 9 12 9c-1.6 0-3 .4-3 .8z",
912
- width: 24,
913
- height: 24
914
- };
915
- const iconExclamationSign = {
916
- path: "M58.368 81.225l0-12.369q0-.912-.57-1.539t-1.425-.627l-12.54 0q-.855-.057-1.482.627t-.684 1.539l0 12.369q-.057.855.627 1.482t1.539.684l12.54 0q.855 0 1.425-.627t.57-1.539zm1.026-62.871q0-1.596-2.223-1.71l-14.307 0q-2.109 0-2.223 1.71l1.14 40.47q0 .627.627 1.14t1.539.456l12.084 0q.912-.057 1.539-.513t.684-1.083zm-9.348-18.354q13.566 0 25.08 6.726 11.514 6.669 18.24 18.183t6.726 25.137-6.726 25.137-18.24 18.183-25.08 6.669-25.137-6.726q-11.514-6.726-18.183-18.183-6.726-11.571-6.726-25.137t6.726-25.08 18.24-18.24 25.08-6.669z",
917
- width: 100,
918
- height: 99.944
919
- };
920
- // Grabbed from https://github.com/encharm/Font-Awesome-SVG-PNG
921
- const iconGear = {
922
- path: "M1152 896q0-106-75-181t-181-75-181 75-75 181 75 181 181 75 181-75 75-181zm512-109v222q0 12-8 23t-20 13l-185 28q-19 54-39 91 35 50 107 138 10 12 10 25t-9 23q-27 37-99 108t-94 71q-12 0-26-9l-138-108q-44 23-91 38-16 136-29 186-7 28-36 28h-222q-14 0-24.5-8.5t-11.5-21.5l-28-184q-49-16-90-37l-141 107q-10 9-25 9-14 0-25-11-126-114-165-168-7-10-7-23 0-12 8-23 15-21 51-66.5t54-70.5q-27-50-41-99l-183-27q-13-2-21-12.5t-8-23.5v-222q0-12 8-23t19-13l186-28q14-46 39-92-40-57-107-138-10-12-10-24 0-10 9-23 26-36 98.5-107.5t94.5-71.5q13 0 26 10l138 107q44-23 91-38 16-136 29-186 7-28 36-28h222q14 0 24.5 8.5t11.5 21.5l28 184q49 16 90 37l142-107q9-9 24-9 13 0 25 10 129 119 165 170 7 8 7 22 0 12-8 23-15 21-51 66.5t-54 70.5q26 50 41 98l183 28q13 2 21 12.5t8 23.5z",
923
- width: 1792,
924
- height: 1792
925
- };
926
- const iconMobilePhone = {
927
- path: "M36.04 89.557q0-2.584-1.836-4.42t-4.42-1.836-4.352 1.836q-1.836 1.836-1.836 4.42t1.836 4.352 4.42 1.836q2.652-.068 4.42-1.836t1.768-4.352zm16.184-12.444l0-54.74q0-1.088-.748-1.768t-1.768-.68l-39.78 0q-1.088 0-1.768.748t-.68 1.7l0 54.74q0 1.02.748 1.768t1.7.68l39.78 0q1.02-.068 1.768-.748t.748-1.7zm-14.892-65.892q0-1.224-1.292-1.292l-12.444 0q-1.224.068-1.224 1.292t1.224 1.224l12.444 0q1.292 0 1.292-1.224zm22.372-1.292l0 79.628q0 3.944-2.992 6.936t-7.004 2.992l-39.78 0q-4.012 0-7.004-2.924-2.924-2.924-2.924-7.004l0-79.628q0-4.012 2.924-6.936t7.004-2.992l39.78 0q4.012-.068 7.004 2.924t2.992 7.004z",
928
- width: 60.013,
929
- height: 100
930
- };
931
- const iconOk = {
932
- path: "M37.964 76.048q-2.576 0-4.368-1.792l-31.864-31.864q-1.792-1.792-1.792-4.368t1.792-4.368l8.736-8.68q1.792-1.792 4.368-1.792t4.312 1.792l18.816 18.872 42-42.056q1.792-1.792 4.368-1.792t4.312 1.792l8.736 8.736q1.792 1.792 1.792 4.368t-1.792 4.312l-55.048 55.048q-1.792 1.792-4.368 1.792z",
933
- width: 100,
934
- height: 76.637
935
- };
936
- const iconPlus = {
937
- path: "M99.758 43.09l0 13.578q0 2.852-1.984 4.836t-4.836 1.984l-29.45 0l0 29.45q0 2.852-1.984 4.836t-4.836 1.984l-13.578 0q-2.852 0-4.836-1.984t-1.984-4.836l0-29.45l-29.45 0q-2.852 0-4.836-1.984t-1.984-4.836l0-13.578q0-2.852 1.984-4.836t4.836-1.984l29.45 0l0-29.45q0-2.852 1.984-4.836t4.836-1.984l13.578 0q2.852 0 4.836 1.984t1.984 4.836l0 29.45l29.45 0q2.852 0 4.836 1.984t1.984 4.836z",
938
- width: 100,
939
- height: 100
940
- };
941
- const iconRemove = {
942
- path: "M100.464 80.808q0 3.404-2.368 5.772l-11.47 11.544q-2.368 2.368-5.772 2.368t-5.698-2.368l-24.864-24.864-24.864 24.864q-2.368 2.368-5.772 2.368t-5.772-2.368l-11.47-11.544q-2.368-2.368-2.368-5.772t2.368-5.698l24.864-24.864-24.864-24.864q-2.368-2.368-2.368-5.772t2.368-5.772l11.47-11.47q2.368-2.368 5.772-2.368t5.772 2.368l24.864 24.864 24.864-24.864q2.294-2.368 5.698-2.368t5.772 2.368l11.47 11.47q2.368 2.368 2.368 5.772t-2.368 5.772l-24.864 24.864 24.864 24.864q2.368 2.294 2.368 5.698z",
943
- width: 100,
944
- height: 100
280
+ MathInput.defaultProps = {
281
+ value: "",
282
+ convertDotToTimes: false
945
283
  };
946
- const iconStar = {
947
- path: "M15.1052249,9.55978547 L22.0028147,9.55978545 C23.6568673,9.55978545 23.9349557,10.3753626 22.6181351,11.3858845 L16.9943688,15.7015366 L19.2518801,22.8294455 C19.7526645,24.4106317 19.0984455,24.8825885 17.769353,23.8673293 L12.0490577,19.4977438 L6.5116497,23.8422153 C5.20921411,24.8640642 4.53299569,24.4067544 5.00266927,22.8160582 L7.10332364,15.7015366 L1.42794544,11.3634306 C0.110226041,10.3562014 0.383967283,9.54239221 2.0409646,9.54574013 L8.9924676,9.55978547 L11.1485117,2.72669438 C11.6458693,1.15043244 12.4548928,1.15900049 12.9494787,2.72669438 L15.1052249,9.55978547 Z",
948
- width: 24,
949
- height: 24
284
+ const MathInputIcon = ({
285
+ hovered,
286
+ focused,
287
+ active
288
+ }) => {
289
+ let color;
290
+ switch (true) {
291
+ case focused || active:
292
+ color = Color.white;
293
+ break;
294
+ case hovered:
295
+ color = Color.blue;
296
+ break;
297
+ default:
298
+ color = Color.offBlack;
299
+ break;
300
+ }
301
+ const dynamicClass = active || focused ? styles$A.iconActive : styles$A.iconInactive;
302
+ return /*#__PURE__*/React.createElement(View, {
303
+ style: [styles$A.iconContainer, dynamicClass]
304
+ }, /*#__PURE__*/React.createElement("svg", {
305
+ xmlns: "http://www.w3.org/2000/svg",
306
+ width: "16",
307
+ height: "16",
308
+ fill: color,
309
+ viewBox: "0 0 256 256"
310
+ }, /*#__PURE__*/React.createElement("path", {
311
+ d: "M112,72a8,8,0,0,1-8,8H40a8,8,0,0,1,0-16h64A8,8,0,0,1,112,72Zm-8,104H80V152a8,8,0,0,0-16,0v24H40a8,8,0,0,0,0,16H64v24a8,8,0,0,0,16,0V192h24a8,8,0,0,0,0-16Zm48,0h64a8,8,0,0,0,0-16H152a8,8,0,0,0,0,16Zm64,16H152a8,8,0,0,0,0,16h64a8,8,0,0,0,0-16Zm-61.66-90.34a8,8,0,0,0,11.32,0L184,83.31l18.34,18.35a8,8,0,0,0,11.32-11.32L195.31,72l18.35-18.34a8,8,0,0,0-11.32-11.32L184,60.69,165.66,42.34a8,8,0,0,0-11.32,11.32L172.69,72,154.34,90.34A8,8,0,0,0,154.34,101.66Z"
312
+ })));
950
313
  };
951
- const iconTryAgain = {
952
- path: "M3.74890556,17.9799506 C2.19251241,16.1970909 1.10103636,13.4971457 1.13090903,11.1491783 C1.17160478,7.95052637 4.01704076,0.865059407 11.7028044,0.865059407 C19.388568,0.865059407 22.3026521,7.35203035 22.3026521,11.5879453 C22.3026521,15.8238603 19.386629,20.5574509 13.6832464,21.7131548 L13.6757539,17.3722171 C17.0812986,16.2190517 18.331158,14.1944123 18.3311578,11.5879451 C18.3311574,8.16554692 15.6664205,5.03476549 11.7028048,5.20494205 C7.73918903,5.37511861 5.59244567,8.66930079 5.59244567,11.1491783 C5.59244567,12.9090077 6.11128139,14.1753512 6.93640437,15.3053215 L8.14052356,14.2949456 C8.98559348,13.5858477 9.6994861,13.9070448 9.73489556,15.0076413 L9.91284941,20.5388014 C9.94832683,21.6415103 9.09967118,22.3514475 8.02194403,22.1254594 L2.60571602,20.9897332 C1.5259204,20.7633114 1.34338662,19.9984207 2.18070755,19.295825 L3.74890556,17.9799506 Z",
953
- width: 23,
954
- height: 23
314
+ const mapButtonSets = buttonSets => {
315
+ const keypadButtonSets = {};
316
+ if (!buttonSets) {
317
+ return keypadButtonSets;
318
+ }
319
+ buttonSets.forEach(buttonSet => {
320
+ switch (buttonSet) {
321
+ case "advanced relations":
322
+ keypadButtonSets.advancedRelations = true;
323
+ break;
324
+ case "basic relations":
325
+ keypadButtonSets.basicRelations = true;
326
+ break;
327
+ case "basic+div":
328
+ keypadButtonSets.divisionKey = true;
329
+ break;
330
+ case "logarithms":
331
+ keypadButtonSets.logarithms = true;
332
+ break;
333
+ case "prealgebra":
334
+ keypadButtonSets.preAlgebra = true;
335
+ break;
336
+ case "trig":
337
+ keypadButtonSets.trigonometry = true;
338
+ break;
339
+ }
340
+ });
341
+ return keypadButtonSets;
955
342
  };
956
- const iconTablet = {
957
- path: "M45.322 90.706q0-1.86-1.302-3.224-1.364-1.364-3.224-1.364t-3.224 1.364-1.302 3.224q0 1.86 1.364 3.224 1.302 1.364 3.162 1.302 1.86.062 3.224-1.302t1.302-3.224zm27.218-11.346l0-68.014q0-.93-.682-1.612t-1.55-.682l-58.962 0q-.93 0-1.612.682t-.682 1.612l0 68.014q0 .93.682 1.612t1.612.62l58.962 0q.992-.062 1.612-.682t.62-1.55zm9.114-68.014l0 77.066q0 4.65-3.348 7.998t-7.998 3.348l-58.962 0q-4.65 0-7.998-3.348t-3.348-7.998l0-77.066q0-4.65 3.348-7.998t7.998-3.348l58.962 0q4.65 0 7.998 3.348t3.348 7.998z",
958
- width: 81.852,
959
- height: 100
343
+ const inputFocused = {
344
+ borderWidth: 2,
345
+ borderColor: Color.blue,
346
+ margin: -1
960
347
  };
961
- const iconTrash = {
962
- path: "M31.293 37.506q2.052 0 2.052 2.109l0 37.506q0 1.995-2.052 2.109l-4.218 0q-.912-.057-1.482-.627t-.57-1.482l0-37.506q0-2.109 2.052-2.109l4.218 0zm18.753 2.109l0 37.506q0 .912-.57 1.482t-1.539.627l-4.161 0q-1.995 0-2.109-2.109l0-37.506q.057-.912.627-1.482t1.482-.627l4.161 0q.969.057 1.539.627t.57 1.482zm14.592-2.109q2.052 0 2.052 2.109l0 37.506q0 1.995-2.052 2.109l-4.161 0q-.969-.057-1.539-.627t-.57-1.482l0-37.506q0-2.109 2.109-2.109l4.161 0zm10.431 49.248l0-61.731l-58.368 0l0 61.731q.057 2.679.969 3.819t1.083 1.14l54.207 0q.171 0 1.14-1.083t.969-3.876zm-43.776-70.11l29.184 0l-3.135-7.581q-.456-.57-1.14-.741l-20.634 0q-.627.114-1.083.741zm-31.293 2.109q0-1.995 2.109-2.109l20.121 0l4.56-10.83q.969-2.394 3.477-4.104 2.565-1.71 5.187-1.71l20.805 0q2.622 0 5.187 1.71t3.477 4.104l4.56 10.83l20.178 0q.912.057 1.482.627t.57 1.482l0 4.161q0 1.995-2.052 2.109l-6.27 0l0 61.731q0 5.415-3.078 9.348t-7.353 3.933l-54.207 0q-4.275 0-7.353-3.819t-3.078-9.177l0-62.016l-6.213 0q-.969 0-1.539-.57t-.57-1.539l0-4.161z",
963
- width: 91.681,
964
- height: 100
348
+ const styles$A = StyleSheet.create({
349
+ iconContainer: {
350
+ display: "flex",
351
+ justifyContent: "center",
352
+ height: "100%",
353
+ padding: Spacing.xxxSmall_4,
354
+ borderRadius: 1
355
+ },
356
+ iconInactive: {
357
+ backgroundColor: Color.offBlack8
358
+ },
359
+ iconActive: {
360
+ backgroundColor: Color.offBlack64
361
+ },
362
+ outerWrapper: {
363
+ display: "inline-block",
364
+ borderStyle: "solid",
365
+ borderWidth: 1,
366
+ borderColor: Color.offBlack50,
367
+ borderRadius: 3,
368
+ background: Color.white,
369
+ ":hover": inputFocused
370
+ },
371
+ wrapperFocused: inputFocused,
372
+ wrapperError: {
373
+ borderColor: Color.red,
374
+ background: fade(Color.red, 0.08),
375
+ ":hover": {
376
+ borderColor: Color.red
377
+ }
378
+ },
379
+ popoverContent: {
380
+ padding: 0,
381
+ paddingBottom: Spacing.xxSmall_6,
382
+ maxWidth: "initial"
383
+ }
384
+ });
385
+
386
+ let _dependencies = null;
387
+ const setDependencies = dependencies => {
388
+ _dependencies = dependencies;
965
389
  };
966
- const iconUndo = {
967
- path: "M10,6.6C10,7.2,9.8,8,9.3,9.1c0,0,0,0.1-0.1,0.1S9.2,9.3,9.2,9.4c0,0,0,0.1-0.1,0.1C9,9.6,9,9.6,8.9,9.6 c-0.1,0-0.1,0-0.1-0.1c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1s0-0.1,0-0.1c0-0.3,0-0.5,0-0.7c0-0.4,0-0.7-0.1-1C8.6,7.1,8.5,6.9,8.4,6.7S8.2,6.3,8,6.1C7.8,5.9,7.6,5.8,7.4,5.7S6.9,5.5,6.7,5.5S6.1,5.4,5.8,5.4c-0.3,0-0.6,0-1,0H3.6v1.4c0,0.1,0,0.2-0.1,0.3C3.4,7.1,3.3,7.1,3.2,7.1C3.1,7.1,3,7.1,3,7L0.1,4.1C0,4.1,0,4,0,3.9s0-0.2,0.1-0.3L3,0.8C3,0.7,3.1,0.7,3.2,0.7c0.1,0,0.2,0,0.3,0.1C3.5,0.9,3.6,0.9,3.6,1v1.4h1.2c2.6,0,4.3,0.7,4.9,2.2C9.9,5.2,10,5.8,10,6.6z",
968
- width: 10,
969
- height: 10
390
+ const getDependencies = () => {
391
+ if (_dependencies) {
392
+ return _dependencies;
393
+ }
394
+ throw new Error(["Perseus has not been provided required dependencies.", "setDependencies(dependencies) must be called first.", "Make sure Perseus is being imported from javascript/perseus/perseus.js."].join("\n"));
970
395
  };
971
- const iconMinus = {
972
- path: `M8,13 L16,13 C16.5522847,13 17,12.5522847 17,12 C17,11.4477153 16.5522847,11 16,11 L8,11 C7.44771525,11 7,11.4477153 7,12 C7,12.5522847 7.44771525,13 8,13 Z`,
973
- width: 24,
974
- height: 24
396
+ const DependenciesContext = /*#__PURE__*/React.createContext({
397
+ analytics: {
398
+ onAnalyticsEvent: async () => {}
399
+ }
400
+ });
401
+
402
+ /**
403
+ * useDependencies provides easy access to the current Perseus dependencies.
404
+ */
405
+ const useDependencies = () => {
406
+ const deps = React.useContext(DependenciesContext);
407
+ return deps;
975
408
  };
976
409
 
977
- var iconPaths = /*#__PURE__*/Object.freeze({
410
+ var dependencies = /*#__PURE__*/Object.freeze({
978
411
  __proto__: null,
979
- iconCheck: iconCheck,
980
- iconCheckMedium: iconCheckMedium,
981
- iconChevronDown: iconChevronDown,
982
- iconChevronRight: iconChevronRight,
983
- iconCircle: iconCircle,
984
- iconCircleArrowDown: iconCircleArrowDown,
985
- iconCircleArrowUp: iconCircleArrowUp,
986
- iconCircleThin: iconCircleThin,
987
- iconDesktop: iconDesktop,
988
- iconDropdownArrow: iconDropdownArrow,
989
- iconExclamationSign: iconExclamationSign,
990
- iconGear: iconGear,
991
- iconMobilePhone: iconMobilePhone,
992
- iconOk: iconOk,
993
- iconPlus: iconPlus,
994
- iconRemove: iconRemove,
995
- iconStar: iconStar,
996
- iconTryAgain: iconTryAgain,
997
- iconTablet: iconTablet,
998
- iconTrash: iconTrash,
999
- iconUndo: iconUndo,
1000
- iconMinus: iconMinus
412
+ setDependencies: setDependencies,
413
+ getDependencies: getDependencies,
414
+ DependenciesContext: DependenciesContext,
415
+ useDependencies: useDependencies
1001
416
  });
1002
417
 
1003
418
  // TODO(LP-11481): Change the primary API we use for logging in Perseus to use
@@ -1521,6 +936,26 @@ const ClassNames = {
1521
936
  MOBILE: "perseus-mobile"
1522
937
  };
1523
938
 
939
+ /*
940
+ * A11y (Accessibility) style rules
941
+ * These are used to mark up areas on the site that should only be readable
942
+ * when using a screen reader.
943
+ */
944
+ var a11y = StyleSheet.create({
945
+ // Make content only visible to screen readers.
946
+ // Both collegeboard.org and Bootstrap 3 use this exact implementation.
947
+ srOnly: {
948
+ border: 0,
949
+ clip: "rect(0,0,0,0)",
950
+ height: 1,
951
+ margin: -1,
952
+ overflow: "hidden",
953
+ padding: 0,
954
+ position: "absolute",
955
+ width: 1
956
+ }
957
+ });
958
+
1524
959
  const KhanMath = {
1525
960
  // Simplify formulas before display
1526
961
  cleanMath: function (expr) {
@@ -2435,6 +1870,7 @@ const KhanAnswerTypes = {
2435
1870
  }
2436
1871
  };
2437
1872
 
1873
+ const ERROR_TITLE = i18n._("Oops!");
2438
1874
  const ERROR_MESSAGE = i18n._("Sorry, I don't understand that!");
2439
1875
  const insertBraces = value => {
2440
1876
  // HACK(alex): Make sure that all LaTeX super/subscripts are wrapped
@@ -2469,53 +1905,48 @@ class Expression extends React.Component {
2469
1905
  constructor(..._args) {
2470
1906
  super(..._args);
2471
1907
  this._isMounted = false;
2472
- this.errorTimeout = null;
2473
1908
  this.displayName = "Expression";
2474
1909
  this.state = {
1910
+ invalid: false,
2475
1911
  showErrorTooltip: false,
2476
- showErrorText: false
1912
+ showErrorStyle: false
2477
1913
  };
2478
1914
  this.componentDidMount = () => {
1915
+ document.addEventListener("mousedown", this._handleMouseDown);
1916
+
2479
1917
  // TODO(scottgrant): This is a hack to remove the deprecated call to
2480
1918
  // this.isMounted() but is still considered an anti-pattern.
2481
1919
  this._isMounted = true;
2482
1920
  };
2483
1921
  this.componentDidUpdate = prevProps => {
2484
1922
  if (!_.isEqual(this.props.value, prevProps.value) || !_.isEqual(this.props.functions, prevProps.functions)) {
2485
- // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
2486
- // eslint-disable-next-line no-restricted-syntax
2487
- // @ts-expect-error - TS2769 - No overload matches this call.
2488
- clearTimeout(this.errorTimeout);
2489
- if (this.parse(this.props.value, this.props).parsed) {
2490
- // eslint-disable-next-line react/no-did-update-set-state
2491
- this.setState({
2492
- showErrorTooltip: false
2493
- });
2494
- } else {
2495
- // Store timeout ID so that we can clear it above
2496
- // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
2497
- // eslint-disable-next-line no-restricted-syntax
2498
- // @ts-expect-error - TS2322 - Type 'Timeout' is not assignable to type 'number'.
2499
- this.errorTimeout = setTimeout(() => {
2500
- const apiResult = this.props.apiOptions.onInputError(null,
2501
- // reserved for some widget identifier
2502
- this.props.value, ERROR_MESSAGE);
2503
- if (apiResult !== false) {
2504
- this.setState({
2505
- showErrorTooltip: true
2506
- });
2507
- }
2508
- }, 2000);
1923
+ this.setState({
1924
+ invalid: false,
1925
+ showErrorTooltip: false,
1926
+ showErrorStyle: false
1927
+ });
1928
+ if (!this.parse(this.props.value, this.props).parsed) {
1929
+ const apiResult = this.props.apiOptions.onInputError(null,
1930
+ // reserved for some widget identifier
1931
+ this.props.value, ERROR_MESSAGE);
1932
+ if (apiResult !== false) {
1933
+ this.setState({
1934
+ invalid: true
1935
+ });
1936
+ }
2509
1937
  }
2510
1938
  }
2511
1939
  };
2512
1940
  this.componentWillUnmount = () => {
2513
- // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
2514
- // eslint-disable-next-line no-restricted-syntax
2515
- // @ts-expect-error - TS2769 - No overload matches this call.
2516
- clearTimeout(this.errorTimeout);
2517
1941
  this._isMounted = false;
2518
1942
  };
1943
+ this._handleMouseDown = () => {
1944
+ if (this._isMounted && this.state.showErrorTooltip) {
1945
+ this.setState({
1946
+ showErrorTooltip: false
1947
+ });
1948
+ }
1949
+ };
2519
1950
  this.simpleValidate = (rubric, onInputError) => {
2520
1951
  onInputError = onInputError || function () {};
2521
1952
  const result = Expression.validate(this.getUserInput(), rubric, onInputError);
@@ -2542,7 +1973,8 @@ class Expression extends React.Component {
2542
1973
  this.props.trackInteraction();
2543
1974
  };
2544
1975
  this._handleFocus = () => {
2545
- this.props.analytics.onAnalyticsEvent({
1976
+ var _this$props$analytics;
1977
+ (_this$props$analytics = this.props.analytics) == null ? void 0 : _this$props$analytics.onAnalyticsEvent({
2546
1978
  type: "perseus:expression-focused",
2547
1979
  payload: null
2548
1980
  });
@@ -2751,7 +2183,8 @@ class Expression extends React.Component {
2751
2183
  this.refs.input.insert(keyPressed);
2752
2184
  }
2753
2185
  sendExpressionEvaluatedEvent(result) {
2754
- this.props.analytics.onAnalyticsEvent({
2186
+ var _this$props$analytics2;
2187
+ (_this$props$analytics2 = this.props.analytics) == null ? void 0 : _this$props$analytics2.onAnalyticsEvent({
2755
2188
  type: "perseus:expression-evaluated",
2756
2189
  payload: {
2757
2190
  result,
@@ -2760,6 +2193,7 @@ class Expression extends React.Component {
2760
2193
  });
2761
2194
  }
2762
2195
  render() {
2196
+ var _this$props$keypadCon, _this$props$analytics3;
2763
2197
  if (this.props.apiOptions.customKeypad) {
2764
2198
  return /*#__PURE__*/React.createElement(KeypadInput
2765
2199
  // eslint-disable-next-line react/no-string-refs
@@ -2782,47 +2216,27 @@ class Expression extends React.Component {
2782
2216
  onBlur: this._handleBlur
2783
2217
  });
2784
2218
  }
2785
- // TODO(alex): Style this tooltip to be more consistent with other
2786
- // tooltips on the site; align to left middle (once possible)
2787
- const errorTooltip = /*#__PURE__*/React.createElement("span", {
2788
- className: "error-tooltip",
2789
- role: "tooltip"
2790
- }, /*#__PURE__*/React.createElement(Tooltip, {
2791
- className: "error-text-container",
2792
- horizontalPosition: HorizontalDirection.Right,
2793
- horizontalAlign: HorizontalDirection.Left,
2794
- verticalPosition: VerticalDirection.Top,
2795
- arrowSize: 10,
2796
- borderColor: "#fcc335",
2797
- show: this.state.showErrorText
2798
- }, /*#__PURE__*/React.createElement("span", {
2799
- className: "error-icon",
2800
- "data-test-id": "test-error-icon",
2801
- onMouseEnter: () => {
2802
- this.setState({
2803
- showErrorText: true
2804
- });
2805
- },
2806
- onMouseLeave: () => {
2807
- this.setState({
2808
- showErrorText: false
2809
- });
2810
- },
2811
- onClick: () => {
2812
- // TODO(alex): Better error feedback for mobile
2813
- this.setState({
2814
- showErrorText: !this.state.showErrorText
2815
- });
2816
- }
2817
- }, /*#__PURE__*/React.createElement(InlineIcon, iconExclamationSign)), /*#__PURE__*/React.createElement("div", {
2818
- className: "error-text"
2819
- }, ERROR_MESSAGE)));
2820
2219
  const className = classNames({
2821
2220
  "perseus-widget-expression": true,
2822
2221
  "show-error-tooltip": this.state.showErrorTooltip
2823
2222
  });
2824
2223
  return /*#__PURE__*/React.createElement("div", {
2825
- className: className
2224
+ className: className,
2225
+ onBlur: () => this.state.invalid && this.setState({
2226
+ showErrorTooltip: true,
2227
+ showErrorStyle: true
2228
+ }),
2229
+ onFocus: () => this.setState({
2230
+ showErrorTooltip: false
2231
+ })
2232
+ }, /*#__PURE__*/React.createElement(View, {
2233
+ style: a11y.srOnly,
2234
+ role: "alert"
2235
+ }, this.state.showErrorTooltip && ERROR_TITLE + " " + ERROR_MESSAGE), /*#__PURE__*/React.createElement(Tooltip$1, {
2236
+ forceAnchorFocusivity: false,
2237
+ opened: this.state.showErrorTooltip,
2238
+ title: ERROR_TITLE,
2239
+ content: ERROR_MESSAGE
2826
2240
  }, /*#__PURE__*/React.createElement(MathInput
2827
2241
  // eslint-disable-next-line react/no-string-refs
2828
2242
  , {
@@ -2831,11 +2245,15 @@ class Expression extends React.Component {
2831
2245
  value: this.props.value,
2832
2246
  onChange: this.changeAndTrack,
2833
2247
  convertDotToTimes: this.props.times,
2834
- buttonsVisible: this.props.buttonsVisible || "focused",
2835
2248
  buttonSets: this.props.buttonSets,
2836
2249
  onFocus: this._handleFocus,
2837
- onBlur: this._handleBlur
2838
- }), this.state.showErrorTooltip && errorTooltip);
2250
+ onBlur: this._handleBlur,
2251
+ hasError: this.state.showErrorStyle,
2252
+ extraKeys: (_this$props$keypadCon = this.props.keypadConfiguration) == null ? void 0 : _this$props$keypadCon.extraKeys,
2253
+ analytics: (_this$props$analytics3 = this.props.analytics) != null ? _this$props$analytics3 : {
2254
+ onAnalyticsEvent: async function () {}
2255
+ }
2256
+ })));
2839
2257
  }
2840
2258
  }
2841
2259
 
@@ -2878,11 +2296,18 @@ const keypadConfigurationForProps = widgetOptions => {
2878
2296
  // and Theta, so we special-case.
2879
2297
  const isGreek = symbol => symbol === "pi" || symbol === "theta";
2880
2298
  const toKey = symbol => isGreek(symbol) ? symbol.toUpperCase() : symbol;
2299
+ const isKey = key => KeyArray.includes(key);
2881
2300
  for (const variable of expr.getVars()) {
2882
- uniqueExtraVariables[toKey(variable)] = true;
2301
+ const maybeKey = toKey(variable);
2302
+ if (isKey(maybeKey)) {
2303
+ uniqueExtraVariables[maybeKey] = true;
2304
+ }
2883
2305
  }
2884
2306
  for (const constant of expr.getConsts()) {
2885
- uniqueExtraConstants[toKey(constant)] = true;
2307
+ const maybeKey = toKey(constant);
2308
+ if (isKey(maybeKey)) {
2309
+ uniqueExtraConstants[maybeKey] = true;
2310
+ }
2886
2311
  }
2887
2312
  }
2888
2313
  }
@@ -14949,6 +14374,196 @@ _.extend(MovableLine$4.prototype, {
14949
14374
  }
14950
14375
  });
14951
14376
 
14377
+ /**
14378
+ * A stripped version of Icon.jsx from webapp. Takes an SVG icon and renders it
14379
+ * inline like Font Awesome did.
14380
+ *
14381
+ * If you are looking for an icon that we've used before you should look in
14382
+ * webapp's `icon-paths.js` which is a reference file for all the SVG paths
14383
+ * that we've used. You'll need to copy the object from that file into
14384
+ * whichever file you're using the icon and explicitly pass it in to the
14385
+ * <InlineIcon/> React component.
14386
+ *
14387
+ * We assume that the viewBox is cropped and aligned to (0, 0), but icons can
14388
+ * be defined differently. At some point we might want to add these attributes
14389
+ * to `icon-paths.js`, but for now this is a fairly safe assumption.
14390
+ *
14391
+ * Sample usage:
14392
+ *
14393
+ * const editIcon = {
14394
+ * path: "M41.209 53.753l5.39 0l0 5.39l3.136 0l6.468-6.517-8.477-8.526-6.517 6.517l0 3.136zm33.173-34.937q-.882-.882-1.862.049l-19.6 19.6q-.931.98-.049 1.862t1.862-.049l19.6-19.6q.931-.98.049-1.862zm-38.563 45.668l0-16.121l37.632-37.632 16.17 16.121-37.632 37.632l-16.17 0zm43.022-12.397l0 10.633q-.049 6.713-4.753 11.417t-11.368 4.704l-46.599 0q-6.713 0-11.417-4.753t-4.704-11.368l0-46.599q0-6.664 4.753-11.417t11.368-4.704l46.599 0q3.528 0 6.566 1.372.833.392.98 1.323t-.49 1.617l-2.744 2.744q-.784.784-1.96.441t-2.352-.343l-46.599 0q-3.675 0-6.321 2.646t-2.646 6.321l0 46.599q0 3.675 2.646 6.321t6.321 2.646l46.599 0q3.675 0 6.321-2.646t2.646-6.321l0-7.056q0-.735.49-1.225l3.577-3.577q.833-.833 1.96-.392t1.127 1.617zm7.203-51.646q2.254 0 3.773 1.568l8.526 8.526q1.568 1.568 1.568 3.822t-1.568 3.773l-5.145 5.145-16.121-16.121 5.145-5.145q1.568-1.568 3.822-1.568z",
14395
+ * width: 100,
14396
+ * height: 78.912,
14397
+ * };
14398
+ * <InlineIcon {...editIcon} />
14399
+ *
14400
+ */
14401
+ const InlineIcon = ({
14402
+ path,
14403
+ width,
14404
+ height,
14405
+ style: _style = {},
14406
+ title
14407
+ }) => /*#__PURE__*/React.createElement("svg", {
14408
+ role: "img",
14409
+ "aria-hidden": !title,
14410
+ style: _extends({
14411
+ verticalAlign: "middle"
14412
+ }, _style),
14413
+ width: `${width / height}em`,
14414
+ height: "1em",
14415
+ viewBox: `0 0 ${width} ${height}`
14416
+ }, !!title && /*#__PURE__*/React.createElement("title", null, title), /*#__PURE__*/React.createElement("path", {
14417
+ d: path,
14418
+ fill: "currentColor"
14419
+ }));
14420
+
14421
+ /**
14422
+ * Icon paths to be used with `inline-icon.jsx`.
14423
+ *
14424
+ * These paths are taken directly from webapp's `icon-paths.js`. Unlike the
14425
+ * webapp equivalent, these can be directly required within Perseus files since
14426
+ * this is all bundled together anyway.
14427
+ */
14428
+
14429
+ const iconCheck = {
14430
+ path: `M8.70710678,12.2928932 C8.31658249,11.9023689 7.68341751,11.9023689 7.29289322,12.2928932 C6.90236893,12.6834175 6.90236893,13.3165825 7.29289322,13.7071068 L9.82842712,16.2426407 C10.2207367,16.6349502 10.8574274,16.6328935 11.2471942,16.2380576 L16.7116603,10.7025237 C17.0996535,10.3094846 17.0955629,9.67633279 16.7025237,9.28833966 C16.3094846,8.90034653 15.6763328,8.90443714 15.2883397,9.29747629 L10.5309507,14.1167372 L8.70710678,12.2928932 Z`,
14431
+ width: 24,
14432
+ height: 24
14433
+ };
14434
+ const iconCheckMedium = {
14435
+ path: "M6.072 10.4l6.175-7.058a1 1 0 1 1 1.506 1.317L6.769 12.64a1 1 0 0 1-1.55-.054L2.203 8.604a1 1 0 1 1 1.594-1.208L6.072 10.4z",
14436
+ width: 16,
14437
+ height: 16
14438
+ };
14439
+ const iconChevronDown = {
14440
+ path: "M99.669 13.048q0 3.36-2.352 5.712l-41.664 41.664q-2.408 2.408-5.88 2.408t-5.712-2.408l-41.664-41.664q-2.408-2.24-2.408-5.712t2.408-5.88l4.76-4.816q2.52-2.352 5.88-2.352t5.656 2.352l31.136 31.136 31.08-31.136q2.352-2.352 5.712-2.352t5.88 2.352l4.816 4.816q2.352 2.52 2.352 5.88z",
14441
+ width: 100,
14442
+ height: 63.034
14443
+ };
14444
+ const iconChevronRight = {
14445
+ path: "M62.808 49.728q0 3.36-2.352 5.88l-41.72 41.664q-2.352 2.408-5.768 2.408t-5.768-2.408l-4.872-4.76q-2.352-2.52-2.352-5.88t2.352-5.712l31.08-31.136-31.08-31.024q-2.352-2.52-2.352-5.88t2.352-5.712l4.872-4.76q2.296-2.408 5.768-2.408t5.768 2.408l41.72 41.664q2.352 2.296 2.352 5.656z",
14446
+ width: 63.034,
14447
+ height: 100
14448
+ };
14449
+ const iconCircle = {
14450
+ path: "M100.035 50.046q.057 13.623-6.669 25.137t-18.24 18.183-25.08 6.669-25.137-6.726q-11.514-6.726-18.183-18.183-6.726-11.571-6.726-25.137t6.726-25.08 18.24-18.24 25.08-6.669q13.566 0 25.08 6.726 11.514 6.669 18.24 18.183t6.669 25.137z",
14451
+ width: 100,
14452
+ height: 100
14453
+ };
14454
+ const iconCircleArrowDown = {
14455
+ path: "M50.046 83.676q1.767 0 2.907-1.14l29.526-29.526q1.197-1.197 1.197-2.907t-1.197-2.964l-5.928-5.928q-1.197-1.14-2.964-1.14t-2.907 1.14l-12.312 12.312l0-32.661q0-1.71-1.254-2.964t-2.907-1.254l-8.322 0q-1.71 0-2.964 1.254t-1.254 2.964l0 32.661l-12.312-12.312q-1.197-1.254-2.907-1.254t-2.907 1.254l-5.928 5.928q-1.197 1.197-1.197 2.964t1.197 2.907l29.469 29.526q1.197 1.14 2.964 1.14zm49.989-33.63q.057 13.623-6.669 25.137t-18.24 18.183-25.08 6.669-25.137-6.726q-11.514-6.726-18.183-18.183-6.726-11.571-6.726-25.137t6.726-25.08 18.24-18.24 25.08-6.669q13.566 0 25.08 6.726 11.514 6.669 18.24 18.183t6.669 25.137z",
14456
+ width: 100,
14457
+ height: 100
14458
+ };
14459
+ const iconCircleArrowUp = {
14460
+ path: "M54.207 83.391q1.653 0 2.907-1.254t1.254-2.907l0-32.718l12.312 12.312q1.254 1.254 2.964 1.254t2.907-1.254l5.928-5.928q1.197-1.197 1.14-2.964 0-1.767-1.14-2.907l-29.526-29.526q-1.197-1.14-2.907-1.14t-2.964 1.14l-29.469 29.526q-1.197 1.254-1.197 2.964t1.197 2.907l5.928 5.928q1.197 1.197 2.907 1.197t2.907-1.197l12.312-12.312l0 32.718q0 1.653 1.254 2.907t2.964 1.254l8.322 0zm45.828-33.345q.057 13.623-6.669 25.137t-18.24 18.183-25.08 6.669-25.137-6.726q-11.514-6.726-18.183-18.183-6.726-11.571-6.726-25.137t6.726-25.08 18.24-18.24 25.08-6.669q13.566 0 25.08 6.726 11.514 6.669 18.24 18.183t6.669 25.137z",
14461
+ width: 100,
14462
+ height: 100
14463
+ };
14464
+ const iconCircleThin = {
14465
+ path: "M50.046 8.322q-8.493 0-16.188 3.306-15.561 6.669-22.173 22.23-3.363 7.695-3.363 16.188t3.306 16.188 8.949 13.281q5.586 5.586 13.281 8.892t16.188 3.306 16.188-3.306 13.281-8.892 8.892-13.281 3.306-16.188-3.306-16.188-8.892-13.281-13.281-8.949q-7.695-3.306-16.188-3.306zm0 91.713q-13.623 0-25.137-6.726t-18.183-18.183q-6.726-11.571-6.726-25.137t6.726-25.08 18.24-18.24 25.08-6.669q13.566 0 25.08 6.726 11.514 6.669 18.24 18.183t6.726 25.137-6.726 25.137-18.24 18.126q-11.514 6.726-25.08 6.726z",
14466
+ width: 100,
14467
+ height: 99.944
14468
+ };
14469
+ const iconDesktop = {
14470
+ path: "M94.208 52.119l0-43.746q0-.69-.506-1.15t-1.196-.506l-84.088 0q-.69 0-1.196.506t-.506 1.15l0 43.746q0 .69.506 1.196t1.196.506l84.088 0q.69 0 1.196-.506t.506-1.196zm6.716-43.746l0 57.224q0 3.45-2.484 5.934t-5.934 2.484l-28.566 0q0 3.128 2.53 7.774.828 1.61.828 2.622t-1.012 2.07q-1.012 1.012-2.346.966l-26.91 0q-1.38 0-2.392-1.012t-1.012-2.024q0-1.058 1.656-4.14t1.748-6.256l-28.612 0q-3.45 0-5.934-2.484t-2.484-5.934l0-57.224q0-3.45 2.484-5.934t5.934-2.438l84.088 0q3.45 0 5.98 2.438 2.438 2.484 2.438 5.934z",
14471
+ width: 100,
14472
+ height: 86.648
14473
+ };
14474
+ const iconDropdownArrow = {
14475
+ path: "M9 9.8c0 .5.7 1.7 1.5 2.8 1.5 1.9 1.5 1.9 3 0C15.7 9.7 15.4 9 12 9c-1.6 0-3 .4-3 .8z",
14476
+ width: 24,
14477
+ height: 24
14478
+ };
14479
+ const iconExclamationSign = {
14480
+ path: "M58.368 81.225l0-12.369q0-.912-.57-1.539t-1.425-.627l-12.54 0q-.855-.057-1.482.627t-.684 1.539l0 12.369q-.057.855.627 1.482t1.539.684l12.54 0q.855 0 1.425-.627t.57-1.539zm1.026-62.871q0-1.596-2.223-1.71l-14.307 0q-2.109 0-2.223 1.71l1.14 40.47q0 .627.627 1.14t1.539.456l12.084 0q.912-.057 1.539-.513t.684-1.083zm-9.348-18.354q13.566 0 25.08 6.726 11.514 6.669 18.24 18.183t6.726 25.137-6.726 25.137-18.24 18.183-25.08 6.669-25.137-6.726q-11.514-6.726-18.183-18.183-6.726-11.571-6.726-25.137t6.726-25.08 18.24-18.24 25.08-6.669z",
14481
+ width: 100,
14482
+ height: 99.944
14483
+ };
14484
+ // Grabbed from https://github.com/encharm/Font-Awesome-SVG-PNG
14485
+ const iconGear = {
14486
+ path: "M1152 896q0-106-75-181t-181-75-181 75-75 181 75 181 181 75 181-75 75-181zm512-109v222q0 12-8 23t-20 13l-185 28q-19 54-39 91 35 50 107 138 10 12 10 25t-9 23q-27 37-99 108t-94 71q-12 0-26-9l-138-108q-44 23-91 38-16 136-29 186-7 28-36 28h-222q-14 0-24.5-8.5t-11.5-21.5l-28-184q-49-16-90-37l-141 107q-10 9-25 9-14 0-25-11-126-114-165-168-7-10-7-23 0-12 8-23 15-21 51-66.5t54-70.5q-27-50-41-99l-183-27q-13-2-21-12.5t-8-23.5v-222q0-12 8-23t19-13l186-28q14-46 39-92-40-57-107-138-10-12-10-24 0-10 9-23 26-36 98.5-107.5t94.5-71.5q13 0 26 10l138 107q44-23 91-38 16-136 29-186 7-28 36-28h222q14 0 24.5 8.5t11.5 21.5l28 184q49 16 90 37l142-107q9-9 24-9 13 0 25 10 129 119 165 170 7 8 7 22 0 12-8 23-15 21-51 66.5t-54 70.5q26 50 41 98l183 28q13 2 21 12.5t8 23.5z",
14487
+ width: 1792,
14488
+ height: 1792
14489
+ };
14490
+ const iconMobilePhone = {
14491
+ path: "M36.04 89.557q0-2.584-1.836-4.42t-4.42-1.836-4.352 1.836q-1.836 1.836-1.836 4.42t1.836 4.352 4.42 1.836q2.652-.068 4.42-1.836t1.768-4.352zm16.184-12.444l0-54.74q0-1.088-.748-1.768t-1.768-.68l-39.78 0q-1.088 0-1.768.748t-.68 1.7l0 54.74q0 1.02.748 1.768t1.7.68l39.78 0q1.02-.068 1.768-.748t.748-1.7zm-14.892-65.892q0-1.224-1.292-1.292l-12.444 0q-1.224.068-1.224 1.292t1.224 1.224l12.444 0q1.292 0 1.292-1.224zm22.372-1.292l0 79.628q0 3.944-2.992 6.936t-7.004 2.992l-39.78 0q-4.012 0-7.004-2.924-2.924-2.924-2.924-7.004l0-79.628q0-4.012 2.924-6.936t7.004-2.992l39.78 0q4.012-.068 7.004 2.924t2.992 7.004z",
14492
+ width: 60.013,
14493
+ height: 100
14494
+ };
14495
+ const iconOk = {
14496
+ path: "M37.964 76.048q-2.576 0-4.368-1.792l-31.864-31.864q-1.792-1.792-1.792-4.368t1.792-4.368l8.736-8.68q1.792-1.792 4.368-1.792t4.312 1.792l18.816 18.872 42-42.056q1.792-1.792 4.368-1.792t4.312 1.792l8.736 8.736q1.792 1.792 1.792 4.368t-1.792 4.312l-55.048 55.048q-1.792 1.792-4.368 1.792z",
14497
+ width: 100,
14498
+ height: 76.637
14499
+ };
14500
+ const iconPlus = {
14501
+ path: "M99.758 43.09l0 13.578q0 2.852-1.984 4.836t-4.836 1.984l-29.45 0l0 29.45q0 2.852-1.984 4.836t-4.836 1.984l-13.578 0q-2.852 0-4.836-1.984t-1.984-4.836l0-29.45l-29.45 0q-2.852 0-4.836-1.984t-1.984-4.836l0-13.578q0-2.852 1.984-4.836t4.836-1.984l29.45 0l0-29.45q0-2.852 1.984-4.836t4.836-1.984l13.578 0q2.852 0 4.836 1.984t1.984 4.836l0 29.45l29.45 0q2.852 0 4.836 1.984t1.984 4.836z",
14502
+ width: 100,
14503
+ height: 100
14504
+ };
14505
+ const iconRemove = {
14506
+ path: "M100.464 80.808q0 3.404-2.368 5.772l-11.47 11.544q-2.368 2.368-5.772 2.368t-5.698-2.368l-24.864-24.864-24.864 24.864q-2.368 2.368-5.772 2.368t-5.772-2.368l-11.47-11.544q-2.368-2.368-2.368-5.772t2.368-5.698l24.864-24.864-24.864-24.864q-2.368-2.368-2.368-5.772t2.368-5.772l11.47-11.47q2.368-2.368 5.772-2.368t5.772 2.368l24.864 24.864 24.864-24.864q2.294-2.368 5.698-2.368t5.772 2.368l11.47 11.47q2.368 2.368 2.368 5.772t-2.368 5.772l-24.864 24.864 24.864 24.864q2.368 2.294 2.368 5.698z",
14507
+ width: 100,
14508
+ height: 100
14509
+ };
14510
+ const iconStar = {
14511
+ path: "M15.1052249,9.55978547 L22.0028147,9.55978545 C23.6568673,9.55978545 23.9349557,10.3753626 22.6181351,11.3858845 L16.9943688,15.7015366 L19.2518801,22.8294455 C19.7526645,24.4106317 19.0984455,24.8825885 17.769353,23.8673293 L12.0490577,19.4977438 L6.5116497,23.8422153 C5.20921411,24.8640642 4.53299569,24.4067544 5.00266927,22.8160582 L7.10332364,15.7015366 L1.42794544,11.3634306 C0.110226041,10.3562014 0.383967283,9.54239221 2.0409646,9.54574013 L8.9924676,9.55978547 L11.1485117,2.72669438 C11.6458693,1.15043244 12.4548928,1.15900049 12.9494787,2.72669438 L15.1052249,9.55978547 Z",
14512
+ width: 24,
14513
+ height: 24
14514
+ };
14515
+ const iconTryAgain = {
14516
+ path: "M3.74890556,17.9799506 C2.19251241,16.1970909 1.10103636,13.4971457 1.13090903,11.1491783 C1.17160478,7.95052637 4.01704076,0.865059407 11.7028044,0.865059407 C19.388568,0.865059407 22.3026521,7.35203035 22.3026521,11.5879453 C22.3026521,15.8238603 19.386629,20.5574509 13.6832464,21.7131548 L13.6757539,17.3722171 C17.0812986,16.2190517 18.331158,14.1944123 18.3311578,11.5879451 C18.3311574,8.16554692 15.6664205,5.03476549 11.7028048,5.20494205 C7.73918903,5.37511861 5.59244567,8.66930079 5.59244567,11.1491783 C5.59244567,12.9090077 6.11128139,14.1753512 6.93640437,15.3053215 L8.14052356,14.2949456 C8.98559348,13.5858477 9.6994861,13.9070448 9.73489556,15.0076413 L9.91284941,20.5388014 C9.94832683,21.6415103 9.09967118,22.3514475 8.02194403,22.1254594 L2.60571602,20.9897332 C1.5259204,20.7633114 1.34338662,19.9984207 2.18070755,19.295825 L3.74890556,17.9799506 Z",
14517
+ width: 23,
14518
+ height: 23
14519
+ };
14520
+ const iconTablet = {
14521
+ path: "M45.322 90.706q0-1.86-1.302-3.224-1.364-1.364-3.224-1.364t-3.224 1.364-1.302 3.224q0 1.86 1.364 3.224 1.302 1.364 3.162 1.302 1.86.062 3.224-1.302t1.302-3.224zm27.218-11.346l0-68.014q0-.93-.682-1.612t-1.55-.682l-58.962 0q-.93 0-1.612.682t-.682 1.612l0 68.014q0 .93.682 1.612t1.612.62l58.962 0q.992-.062 1.612-.682t.62-1.55zm9.114-68.014l0 77.066q0 4.65-3.348 7.998t-7.998 3.348l-58.962 0q-4.65 0-7.998-3.348t-3.348-7.998l0-77.066q0-4.65 3.348-7.998t7.998-3.348l58.962 0q4.65 0 7.998 3.348t3.348 7.998z",
14522
+ width: 81.852,
14523
+ height: 100
14524
+ };
14525
+ const iconTrash = {
14526
+ path: "M31.293 37.506q2.052 0 2.052 2.109l0 37.506q0 1.995-2.052 2.109l-4.218 0q-.912-.057-1.482-.627t-.57-1.482l0-37.506q0-2.109 2.052-2.109l4.218 0zm18.753 2.109l0 37.506q0 .912-.57 1.482t-1.539.627l-4.161 0q-1.995 0-2.109-2.109l0-37.506q.057-.912.627-1.482t1.482-.627l4.161 0q.969.057 1.539.627t.57 1.482zm14.592-2.109q2.052 0 2.052 2.109l0 37.506q0 1.995-2.052 2.109l-4.161 0q-.969-.057-1.539-.627t-.57-1.482l0-37.506q0-2.109 2.109-2.109l4.161 0zm10.431 49.248l0-61.731l-58.368 0l0 61.731q.057 2.679.969 3.819t1.083 1.14l54.207 0q.171 0 1.14-1.083t.969-3.876zm-43.776-70.11l29.184 0l-3.135-7.581q-.456-.57-1.14-.741l-20.634 0q-.627.114-1.083.741zm-31.293 2.109q0-1.995 2.109-2.109l20.121 0l4.56-10.83q.969-2.394 3.477-4.104 2.565-1.71 5.187-1.71l20.805 0q2.622 0 5.187 1.71t3.477 4.104l4.56 10.83l20.178 0q.912.057 1.482.627t.57 1.482l0 4.161q0 1.995-2.052 2.109l-6.27 0l0 61.731q0 5.415-3.078 9.348t-7.353 3.933l-54.207 0q-4.275 0-7.353-3.819t-3.078-9.177l0-62.016l-6.213 0q-.969 0-1.539-.57t-.57-1.539l0-4.161z",
14527
+ width: 91.681,
14528
+ height: 100
14529
+ };
14530
+ const iconUndo = {
14531
+ path: "M10,6.6C10,7.2,9.8,8,9.3,9.1c0,0,0,0.1-0.1,0.1S9.2,9.3,9.2,9.4c0,0,0,0.1-0.1,0.1C9,9.6,9,9.6,8.9,9.6 c-0.1,0-0.1,0-0.1-0.1c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1s0-0.1,0-0.1c0-0.3,0-0.5,0-0.7c0-0.4,0-0.7-0.1-1C8.6,7.1,8.5,6.9,8.4,6.7S8.2,6.3,8,6.1C7.8,5.9,7.6,5.8,7.4,5.7S6.9,5.5,6.7,5.5S6.1,5.4,5.8,5.4c-0.3,0-0.6,0-1,0H3.6v1.4c0,0.1,0,0.2-0.1,0.3C3.4,7.1,3.3,7.1,3.2,7.1C3.1,7.1,3,7.1,3,7L0.1,4.1C0,4.1,0,4,0,3.9s0-0.2,0.1-0.3L3,0.8C3,0.7,3.1,0.7,3.2,0.7c0.1,0,0.2,0,0.3,0.1C3.5,0.9,3.6,0.9,3.6,1v1.4h1.2c2.6,0,4.3,0.7,4.9,2.2C9.9,5.2,10,5.8,10,6.6z",
14532
+ width: 10,
14533
+ height: 10
14534
+ };
14535
+ const iconMinus = {
14536
+ path: `M8,13 L16,13 C16.5522847,13 17,12.5522847 17,12 C17,11.4477153 16.5522847,11 16,11 L8,11 C7.44771525,11 7,11.4477153 7,12 C7,12.5522847 7.44771525,13 8,13 Z`,
14537
+ width: 24,
14538
+ height: 24
14539
+ };
14540
+
14541
+ var iconPaths = /*#__PURE__*/Object.freeze({
14542
+ __proto__: null,
14543
+ iconCheck: iconCheck,
14544
+ iconCheckMedium: iconCheckMedium,
14545
+ iconChevronDown: iconChevronDown,
14546
+ iconChevronRight: iconChevronRight,
14547
+ iconCircle: iconCircle,
14548
+ iconCircleArrowDown: iconCircleArrowDown,
14549
+ iconCircleArrowUp: iconCircleArrowUp,
14550
+ iconCircleThin: iconCircleThin,
14551
+ iconDesktop: iconDesktop,
14552
+ iconDropdownArrow: iconDropdownArrow,
14553
+ iconExclamationSign: iconExclamationSign,
14554
+ iconGear: iconGear,
14555
+ iconMobilePhone: iconMobilePhone,
14556
+ iconOk: iconOk,
14557
+ iconPlus: iconPlus,
14558
+ iconRemove: iconRemove,
14559
+ iconStar: iconStar,
14560
+ iconTryAgain: iconTryAgain,
14561
+ iconTablet: iconTablet,
14562
+ iconTrash: iconTrash,
14563
+ iconUndo: iconUndo,
14564
+ iconMinus: iconMinus
14565
+ });
14566
+
14952
14567
  /* eslint-disable @babel/no-invalid-this */
14953
14568
  const add$1 = {
14954
14569
  constrain: function () {
@@ -20819,6 +20434,258 @@ TextInput.defaultProps = {
20819
20434
  disabled: false
20820
20435
  };
20821
20436
 
20437
+ /* eslint-disable react/no-unsafe */
20438
+ const zIndex = 10;
20439
+ let HorizontalDirection = /*#__PURE__*/function (HorizontalDirection) {
20440
+ HorizontalDirection["Left"] = "left";
20441
+ HorizontalDirection["Right"] = "right";
20442
+ return HorizontalDirection;
20443
+ }({});
20444
+ let VerticalDirection = /*#__PURE__*/function (VerticalDirection) {
20445
+ VerticalDirection["Top"] = "top";
20446
+ VerticalDirection["Bottom"] = "bottom";
20447
+ return VerticalDirection;
20448
+ }({});
20449
+ const Triangle = props => {
20450
+ let borderLeft;
20451
+ let borderRight;
20452
+ let borderTop;
20453
+ let borderBottom;
20454
+ const hBorder = `${props.width}px solid transparent`;
20455
+ if (props.horizontalDirection === "right") {
20456
+ borderLeft = hBorder;
20457
+ } else {
20458
+ borderRight = hBorder;
20459
+ }
20460
+ const vBorder = `${props.height}px solid ${props.color}`;
20461
+ if (props.verticalDirection === "top") {
20462
+ borderTop = vBorder;
20463
+ } else {
20464
+ borderBottom = vBorder;
20465
+ }
20466
+ return /*#__PURE__*/React.createElement("div", {
20467
+ style: {
20468
+ display: "block",
20469
+ height: 0,
20470
+ width: 0,
20471
+ position: "absolute",
20472
+ left: props.left,
20473
+ top: props["top"],
20474
+ borderLeft: borderLeft,
20475
+ borderRight: borderRight,
20476
+ borderTop: borderTop,
20477
+ borderBottom: borderBottom
20478
+ }
20479
+ });
20480
+ };
20481
+ const TooltipArrow = props => {
20482
+ // TODO(aria): Think about adding a box-shadow to the triangle here
20483
+ // See http://css-tricks.com/triangle-with-shadow/
20484
+
20485
+ //const isRight = (this.props.horizontalDirection === "right");
20486
+ const isTop = props.verticalDirection === "top";
20487
+ const frontTopOffset = isTop ? 0 : 1;
20488
+ const borderTopOffset = isTop ? 0 : -1;
20489
+ return /*#__PURE__*/React.createElement("div", {
20490
+ style: {
20491
+ display: "block",
20492
+ position: props.position,
20493
+ visibility: props.visibility,
20494
+ left: props.left,
20495
+ top: props["top"],
20496
+ width: props.width + 2,
20497
+ height: props.height + 1,
20498
+ marginTop: -1,
20499
+ marginBottom: -2,
20500
+ zIndex: zIndex
20501
+ }
20502
+ }, /*#__PURE__*/React.createElement(Triangle, {
20503
+ horizontalDirection: props.horizontalDirection,
20504
+ verticalDirection: props.verticalDirection,
20505
+ color: props.border,
20506
+ left: 0,
20507
+ top: borderTopOffset,
20508
+ width: props.width + 2 // one extra for the diagonal
20509
+ ,
20510
+ height: props.height + 2
20511
+ }), /*#__PURE__*/React.createElement(Triangle, {
20512
+ horizontalDirection: props.horizontalDirection,
20513
+ verticalDirection: props.verticalDirection,
20514
+ color: props.color,
20515
+ left: 1,
20516
+ top: frontTopOffset,
20517
+ width: props.width,
20518
+ height: props.height
20519
+ }));
20520
+ };
20521
+ TooltipArrow.defaultProps = {
20522
+ position: "relative",
20523
+ visibility: "visible",
20524
+ left: 0,
20525
+ top: 0
20526
+ };
20527
+ const VERTICAL_CORNERS = {
20528
+ top: {
20529
+ top: "-100%"
20530
+ },
20531
+ bottom: {
20532
+ top: 0
20533
+ }
20534
+ };
20535
+ const HORIZONTAL_CORNERS = {
20536
+ left: {
20537
+ targetLeft: 0
20538
+ },
20539
+ right: {
20540
+ targetLeft: "100%"
20541
+ }
20542
+ };
20543
+ const HORIZONTAL_ALIGNMNENTS = {
20544
+ left: {
20545
+ tooltipLeft: 0,
20546
+ arrowLeft: arrowSize => 0
20547
+ },
20548
+ right: {
20549
+ tooltipLeft: "-100%",
20550
+ arrowLeft: arrowSize => -arrowSize - 2
20551
+ }
20552
+ };
20553
+ class Tooltip extends React.Component {
20554
+ constructor(...args) {
20555
+ super(...args);
20556
+ this.state = {
20557
+ height: null // used for offsetting "top" positioned tooltips
20558
+ };
20559
+ }
20560
+
20561
+ componentDidMount() {
20562
+ this._updateHeight();
20563
+ }
20564
+ UNSAFE_componentWillReceiveProps() {
20565
+ // If the contents have changed, reset our measure of the height
20566
+ this.setState({
20567
+ height: null
20568
+ });
20569
+ }
20570
+ componentDidUpdate() {
20571
+ this._updateHeight();
20572
+ }
20573
+ _renderToolTipDiv(isTooltipAbove) {
20574
+ const settings = Object.assign({}, HORIZONTAL_CORNERS[this.props.horizontalPosition], HORIZONTAL_ALIGNMNENTS[this.props.horizontalAlign], VERTICAL_CORNERS[this.props.verticalPosition]);
20575
+ let arrowAbove;
20576
+ let arrowBelow;
20577
+ if (isTooltipAbove) {
20578
+ // We put an absolutely positioned arrow in the correct place
20579
+ arrowAbove = /*#__PURE__*/React.createElement(TooltipArrow, {
20580
+ verticalDirection: VerticalDirection.Top,
20581
+ horizontalDirection: this.props.horizontalAlign,
20582
+ position: "absolute",
20583
+ color: "white",
20584
+ border: this.props.borderColor,
20585
+ left: settings.arrowLeft(this.props.arrowSize),
20586
+ top: -this.props.arrowSize + 2,
20587
+ width: this.props.arrowSize,
20588
+ height: this.props.arrowSize,
20589
+ zIndex: zIndex
20590
+ });
20591
+
20592
+ // And we use a visibility: hidden arrow below to shift up the
20593
+ // content by the correct amount
20594
+ arrowBelow = /*#__PURE__*/React.createElement(TooltipArrow, {
20595
+ verticalDirection: VerticalDirection.Top,
20596
+ horizontalDirection: this.props.horizontalAlign,
20597
+ visibility: "hidden",
20598
+ color: "white",
20599
+ border: this.props.borderColor,
20600
+ left: settings.arrowLeft(this.props.arrowSize),
20601
+ top: -1,
20602
+ width: this.props.arrowSize,
20603
+ height: this.props.arrowSize,
20604
+ zIndex: zIndex
20605
+ });
20606
+ } else {
20607
+ arrowAbove = /*#__PURE__*/React.createElement(TooltipArrow, {
20608
+ verticalDirection: VerticalDirection.Bottom,
20609
+ horizontalDirection: this.props.horizontalAlign,
20610
+ color: "white",
20611
+ border: this.props.borderColor,
20612
+ left: settings.arrowLeft(this.props.arrowSize),
20613
+ top: -1,
20614
+ width: this.props.arrowSize,
20615
+ height: this.props.arrowSize,
20616
+ zIndex: zIndex
20617
+ });
20618
+ arrowBelow = null;
20619
+ }
20620
+
20621
+ /* A positioned div below the input to be the parent for our
20622
+ tooltip */
20623
+ return /*#__PURE__*/React.createElement("div", {
20624
+ style: {
20625
+ position: "relative",
20626
+ height: 0,
20627
+ display: this.props.show ? "block" : "none"
20628
+ }
20629
+ }, /*#__PURE__*/React.createElement("div", {
20630
+ // eslint-disable-next-line react/no-string-refs
20631
+ ref: "tooltipContainer",
20632
+ className: "tooltipContainer",
20633
+ style: {
20634
+ position: "absolute",
20635
+ // height must start out undefined, not null, so that
20636
+ // we can measure the actual height with jquery.
20637
+ // This is used to position the tooltip with top: -100%
20638
+ // when in verticalPosition: "top" mode
20639
+ height: this.state.height || undefined,
20640
+ left: settings.targetLeft
20641
+ }
20642
+ }, arrowAbove, /*#__PURE__*/React.createElement("div", {
20643
+ className: this.props.className
20644
+ // eslint-disable-next-line react/no-string-refs
20645
+ ,
20646
+ ref: "tooltipContent",
20647
+ style: {
20648
+ position: "relative",
20649
+ top: settings["top"],
20650
+ left: settings.tooltipLeft,
20651
+ border: "1px solid " + this.props.borderColor,
20652
+ boxShadow: "0 1px 3px " + this.props.borderColor,
20653
+ zIndex: zIndex - 1
20654
+ }
20655
+ }, this.props.children.slice(1)), arrowBelow));
20656
+ }
20657
+ _updateHeight() {
20658
+ const tooltipContainer = ReactDOM__default.findDOMNode(
20659
+ // eslint-disable-next-line react/no-string-refs
20660
+ this.refs.tooltipContainer);
20661
+ const height = tooltipContainer.offsetHeight;
20662
+ if (height !== this.state.height) {
20663
+ this.setState({
20664
+ height
20665
+ });
20666
+ }
20667
+ }
20668
+ render() {
20669
+ const isTooltipAbove = this.props.verticalPosition === "top";
20670
+
20671
+ /* We wrap the entire output in a span so that it displays inline */
20672
+ return /*#__PURE__*/React.createElement("span", null, isTooltipAbove && this._renderToolTipDiv(isTooltipAbove), /*#__PURE__*/React.createElement("div", {
20673
+ style: this.props.targetContainerStyle
20674
+ }, this.props.children[0]), !isTooltipAbove && this._renderToolTipDiv());
20675
+ }
20676
+ }
20677
+
20678
+ // Sorry. // Apology-Oriented-Programming
20679
+ Tooltip.defaultProps = {
20680
+ className: "",
20681
+ arrowSize: 10,
20682
+ borderColor: "#ccc",
20683
+ verticalPosition: VerticalDirection.Bottom,
20684
+ horizontalPosition: HorizontalDirection.Left,
20685
+ horizontalAlign: HorizontalDirection.Left,
20686
+ targetContainerStyle: {}
20687
+ };
20688
+
20822
20689
  /* eslint-disable react/sort-comp */
20823
20690
  const {
20824
20691
  captureScratchpadTouchStart: captureScratchpadTouchStart$3
@@ -24608,26 +24475,6 @@ var Explanation$1 = {
24608
24475
  isLintable: true
24609
24476
  };
24610
24477
 
24611
- /*
24612
- * A11y (Accessibility) style rules
24613
- * These are used to mark up areas on the site that should only be readable
24614
- * when using a screen reader.
24615
- */
24616
- var a11y = StyleSheet.create({
24617
- // Make content only visible to screen readers.
24618
- // Both collegeboard.org and Bootstrap 3 use this exact implementation.
24619
- srOnly: {
24620
- border: 0,
24621
- clip: "rect(0,0,0,0)",
24622
- height: 1,
24623
- margin: -1,
24624
- overflow: "hidden",
24625
- padding: 0,
24626
- position: "absolute",
24627
- width: 1
24628
- }
24629
- });
24630
-
24631
24478
  class GradedGroupAnswerBar extends React.Component {
24632
24479
  render() {
24633
24480
  const {
@@ -43895,7 +43742,7 @@ const init = function init(options) {
43895
43742
 
43896
43743
  // This file is processed by a Rollup plugin (replace) to inject the production
43897
43744
  const libName = "@khanacademy/perseus";
43898
- const libVersion = "11.6.1";
43745
+ const libVersion = "12.0.1";
43899
43746
  addLibraryVersionToPerseusDebug(libName, libVersion);
43900
43747
 
43901
43748
  const apiVersion = {
@@ -45611,6 +45458,239 @@ RangeInput.defaultProps = {
45611
45458
  placeholder: [null, null]
45612
45459
  };
45613
45460
 
45461
+ /* eslint-disable @khanacademy/ts-no-error-suppressions */
45462
+ const prettyBig = {
45463
+ fontSize: "150%"
45464
+ };
45465
+ const slightlyBig = {
45466
+ fontSize: "120%"
45467
+ };
45468
+ const symbStyle = {
45469
+ fontSize: "130%"
45470
+ };
45471
+ // These are functions because we want to generate a new component for each use
45472
+ // on the page rather than reusing an instance (which will cause an error).
45473
+ // Also, it's useful for things which might look different depending on the
45474
+ // props.
45475
+
45476
+ const basic = [() => [/*#__PURE__*/React.createElement("span", {
45477
+ key: "plus",
45478
+ style: slightlyBig
45479
+ }, "+"), "PLUS", i18n._("Plus")], () => [/*#__PURE__*/React.createElement("span", {
45480
+ key: "minus",
45481
+ style: prettyBig
45482
+ }, "-"), "MINUS", i18n._("Minus")],
45483
+ // TODO(joel) - display as \cdot when appropriate
45484
+ props => {
45485
+ const {
45486
+ TeX
45487
+ } = getDependencies();
45488
+ if (props.convertDotToTimes) {
45489
+ return [/*#__PURE__*/React.createElement(TeX, {
45490
+ key: "times",
45491
+ style: prettyBig
45492
+ }, i18n.doNotTranslate("\\times")), "TIMES", i18n._("Multiply")];
45493
+ }
45494
+ return [/*#__PURE__*/React.createElement(TeX, {
45495
+ key: "times",
45496
+ style: prettyBig
45497
+ }, i18n.doNotTranslate("\\cdot")), "CDOT", i18n._("Multiply")];
45498
+ }, () => {
45499
+ const {
45500
+ TeX
45501
+ } = getDependencies();
45502
+ return [/*#__PURE__*/React.createElement(TeX, {
45503
+ key: "frac",
45504
+ style: prettyBig
45505
+ }, i18n.doNotTranslate("\\frac{x}{y}")), "FRAC", i18n._("Fraction, excluding the current expression")];
45506
+ }];
45507
+ const buttonSets = {
45508
+ basic,
45509
+ "basic+div": basic.concat([() => {
45510
+ const {
45511
+ TeX
45512
+ } = getDependencies();
45513
+ return [/*#__PURE__*/React.createElement(TeX, {
45514
+ key: "div"
45515
+ }, i18n.doNotTranslate("\\div")), "DIVIDE", i18n._("Divide")];
45516
+ }]),
45517
+ trig: [() => {
45518
+ const {
45519
+ TeX
45520
+ } = getDependencies();
45521
+ return [/*#__PURE__*/React.createElement(TeX, {
45522
+ key: "sin"
45523
+ }, i18n.doNotTranslate("\\sin")), "SIN", i18n._("Sine")];
45524
+ }, () => {
45525
+ const {
45526
+ TeX
45527
+ } = getDependencies();
45528
+ return [/*#__PURE__*/React.createElement(TeX, {
45529
+ key: "cos"
45530
+ }, i18n.doNotTranslate("\\cos")), "COS", i18n._("Cosine")];
45531
+ }, () => {
45532
+ const {
45533
+ TeX
45534
+ } = getDependencies();
45535
+ return [/*#__PURE__*/React.createElement(TeX, {
45536
+ key: "tan"
45537
+ }, i18n.doNotTranslate("\\tan")), "TAN", i18n._("Tangent")];
45538
+ }, () => {
45539
+ const {
45540
+ TeX
45541
+ } = getDependencies();
45542
+ return [/*#__PURE__*/React.createElement(TeX, {
45543
+ key: "theta",
45544
+ style: symbStyle
45545
+ }, i18n.doNotTranslate("\\theta")), "THETA", i18n._("Theta")];
45546
+ }, () => {
45547
+ const {
45548
+ TeX
45549
+ } = getDependencies();
45550
+ return [/*#__PURE__*/React.createElement("span", {
45551
+ key: "phi"
45552
+ }, /*#__PURE__*/React.createElement("span", {
45553
+ className: "perseus-sr-only"
45554
+ }, i18n.doNotTranslate("phi")), /*#__PURE__*/React.createElement(TeX, {
45555
+ key: "pi",
45556
+ style: symbStyle
45557
+ }, i18n.doNotTranslate("\\phi"))), "PHI", i18n._("Phi")];
45558
+ }],
45559
+ prealgebra: [() => {
45560
+ const {
45561
+ TeX
45562
+ } = getDependencies();
45563
+ return [/*#__PURE__*/React.createElement(TeX, {
45564
+ key: "sqrt"
45565
+ }, i18n.doNotTranslate("\\sqrt{x}")), "SQRT", i18n._("Square root")];
45566
+ },
45567
+ // TODO(joel) - how does desmos do this?
45568
+ () => {
45569
+ const {
45570
+ TeX
45571
+ } = getDependencies();
45572
+ return [/*#__PURE__*/React.createElement(TeX, {
45573
+ key: "nthroot"
45574
+ }, i18n.doNotTranslate("\\sqrt[3]{x}")), "NTHROOT3", i18n._("Cube root")];
45575
+ }, () => {
45576
+ const {
45577
+ TeX
45578
+ } = getDependencies();
45579
+ return [/*#__PURE__*/React.createElement(TeX, {
45580
+ key: "pow",
45581
+ style: slightlyBig
45582
+ }, i18n.doNotTranslate("a^b")), "POW", i18n._("Exponent")];
45583
+ }, () => {
45584
+ const {
45585
+ TeX
45586
+ } = getDependencies();
45587
+ return [/*#__PURE__*/React.createElement(TeX, {
45588
+ key: "pi",
45589
+ style: slightlyBig
45590
+ }, i18n.doNotTranslate("\\pi")), "PI", i18n._("Pi")];
45591
+ }],
45592
+ logarithms: [() => {
45593
+ const {
45594
+ TeX
45595
+ } = getDependencies();
45596
+ return [/*#__PURE__*/React.createElement(TeX, {
45597
+ key: "log"
45598
+ }, i18n.doNotTranslate("\\log")), "LOG", i18n._("Logarithm with base 10")];
45599
+ }, () => {
45600
+ const {
45601
+ TeX
45602
+ } = getDependencies();
45603
+ return [/*#__PURE__*/React.createElement(TeX, {
45604
+ key: "ln"
45605
+ }, i18n.doNotTranslate("\\ln")), "LN", i18n._("Natural logarithm")];
45606
+ }, () => {
45607
+ const {
45608
+ TeX
45609
+ } = getDependencies();
45610
+ return [/*#__PURE__*/React.createElement(TeX, {
45611
+ key: "log_b"
45612
+ }, i18n.doNotTranslate("\\log_b")), "LOG_B", i18n._("Logarithm with custom base")];
45613
+ }],
45614
+ "basic relations": [() => {
45615
+ const {
45616
+ TeX
45617
+ } = getDependencies();
45618
+ return [/*#__PURE__*/React.createElement(TeX, {
45619
+ key: "eq"
45620
+ }, "="), "EQUAL", i18n._("Equals sign")];
45621
+ }, () => {
45622
+ const {
45623
+ TeX
45624
+ } = getDependencies();
45625
+ return [/*#__PURE__*/React.createElement(TeX, {
45626
+ key: "lt"
45627
+ }, i18n.doNotTranslate("\\lt")), "LT", i18n._("Less than sign")];
45628
+ }, () => {
45629
+ const {
45630
+ TeX
45631
+ } = getDependencies();
45632
+ return [/*#__PURE__*/React.createElement(TeX, {
45633
+ key: "gt"
45634
+ }, i18n.doNotTranslate("\\gt")), "GT", i18n._("Greater than sign")];
45635
+ }],
45636
+ "advanced relations": [() => {
45637
+ const {
45638
+ TeX
45639
+ } = getDependencies();
45640
+ return [/*#__PURE__*/React.createElement(TeX, {
45641
+ key: "neq"
45642
+ }, i18n.doNotTranslate("\\neq")), "NEQ", i18n._("Not-equals sign")];
45643
+ }, () => {
45644
+ const {
45645
+ TeX
45646
+ } = getDependencies();
45647
+ return [/*#__PURE__*/React.createElement(TeX, {
45648
+ key: "leq"
45649
+ }, i18n.doNotTranslate("\\leq")), "LEQ", i18n._("Less than or equal to sign")];
45650
+ }, () => {
45651
+ const {
45652
+ TeX
45653
+ } = getDependencies();
45654
+ return [/*#__PURE__*/React.createElement(TeX, {
45655
+ key: "geq"
45656
+ }, i18n.doNotTranslate("\\geq")), "GEQ", i18n._("Greater than or equal to sign")];
45657
+ }]
45658
+ };
45659
+ const buttonSetsPropType = PropTypes.arrayOf(PropTypes.oneOf(_(buttonSets).keys()));
45660
+ class TexButtons extends React.Component {
45661
+ render() {
45662
+ // Always show buttonSets in the same order. Note: Technically it's ok
45663
+ // for _.keys() to return the keys in an arbitrary order, but in
45664
+ // practice, they will be ordered as listed above.
45665
+ const sortedButtonSets = _.sortBy(this.props.sets, setName => _.keys(buttonSets).indexOf(setName));
45666
+ const buttons = _(sortedButtonSets).map(setName => buttonSets[setName]);
45667
+ const buttonRows = _(buttons).map(row => row.map(symbGen => {
45668
+ // create a (component, thing we should send to mathquill) pair
45669
+ const symbol = symbGen(this.props);
45670
+ return /*#__PURE__*/React.createElement("button", {
45671
+ onClick: () => this.props.onInsert(symbol[1]),
45672
+ className: "tex-button"
45673
+ // @ts-expect-error - TS2533 - Object is possibly 'null' or 'undefined'. | TS2339 - Property 'key' does not exist on type 'boolean | ReactChild | ReactFragment | ReactPortal'.
45674
+ ,
45675
+ key: symbol[0].key,
45676
+ type: "button",
45677
+ "aria-label": symbol[2]
45678
+ }, symbol[0]);
45679
+ }));
45680
+ const buttonPopup = _(buttonRows).map((row, i) => {
45681
+ return /*#__PURE__*/React.createElement("div", {
45682
+ className: "clearfix tex-button-row",
45683
+ key: this.props.sets[i]
45684
+ }, row);
45685
+ });
45686
+ return /*#__PURE__*/React.createElement("div", {
45687
+ className: `${this.props.className || ""} preview-measure`
45688
+ }, buttonPopup);
45689
+ }
45690
+ }
45691
+ TexButtons.buttonSets = buttonSets;
45692
+ TexButtons.buttonSetsType = buttonSetsPropType;
45693
+
45614
45694
  var components = /*#__PURE__*/Object.freeze({
45615
45695
  __proto__: null,
45616
45696
  ButtonGroup: ButtonGroup,