@khanacademy/perseus 27.2.0 → 28.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/components/image-loader.d.ts +2 -2
  2. package/dist/components/{info-tip.d.ts → info-tip/index.d.ts} +2 -2
  3. package/dist/components/info-tip/info-tip-base.d.ts +6 -0
  4. package/dist/components.d.ts +0 -1
  5. package/dist/es/index.js +452 -362
  6. package/dist/es/index.js.map +1 -1
  7. package/dist/es/strings.js +6 -2
  8. package/dist/es/strings.js.map +1 -1
  9. package/dist/hints-renderer.d.ts +2 -1
  10. package/dist/index.d.ts +4 -4
  11. package/dist/index.js +438 -348
  12. package/dist/index.js.map +1 -1
  13. package/dist/interactive2/movable-point.d.ts +3 -3
  14. package/dist/perseus-api.d.ts +3 -0
  15. package/dist/perseus-types.d.ts +10 -6
  16. package/dist/renderer.d.ts +2 -1
  17. package/dist/server-item-renderer.d.ts +3 -0
  18. package/dist/strings.d.ts +2 -0
  19. package/dist/strings.js +6 -2
  20. package/dist/strings.js.map +1 -1
  21. package/dist/types.d.ts +7 -7
  22. package/dist/util/{katex-preprocess.d.ts → tex-preprocess.d.ts} +1 -1
  23. package/dist/widgets/expression.d.ts +20 -2
  24. package/dist/widgets/interactive-graphs/graphs/angle.d.ts +7 -0
  25. package/dist/widgets/interactive-graphs/graphs/components/angle-indicators.d.ts +0 -2
  26. package/dist/widgets/interactive-graphs/graphs/components/movable-point.d.ts +3 -2
  27. package/dist/widgets/interactive-graphs/graphs/polygon.d.ts +1 -0
  28. package/dist/widgets/interactive-graphs/graphs/use-draggable.d.ts +7 -1
  29. package/dist/widgets/interactive-graphs/locked-figures/locked-function.d.ts +1 -1
  30. package/dist/widgets/interactive-graphs/mafs-graph.d.ts +1 -0
  31. package/dist/widgets/interactive-graphs/math/geometry.d.ts +2 -1
  32. package/dist/widgets/interactive-graphs/reducer/initialize-graph-state.d.ts +8 -1
  33. package/dist/widgets/interactive-graphs/reducer/use-graph-config.d.ts +1 -1
  34. package/dist/widgets/interactive-graphs/stateful-mafs-graph.d.ts +1 -0
  35. package/dist/widgets/label-image.d.ts +6 -0
  36. package/package.json +8 -7
  37. package/dist/components/info-tip/info-tip.d.ts +0 -6
  38. package/dist/components/prop-check-box.d.ts +0 -4
package/dist/es/index.js CHANGED
@@ -5,7 +5,7 @@ import * as PerseusLinter from '@khanacademy/perseus-linter';
5
5
  import { linterContextDefault, Rule, linterContextProps } from '@khanacademy/perseus-linter';
6
6
  import { View, RenderStateRoot, UniqueIDProvider, useLatestRef, useUniqueIdWithMock, Text as Text$2 } from '@khanacademy/wonder-blocks-core';
7
7
  import Tooltip$1 from '@khanacademy/wonder-blocks-tooltip';
8
- import { LabelSmall, Body, LabelMedium } from '@khanacademy/wonder-blocks-typography';
8
+ import { HeadingMedium, LabelSmall, Body, LabelMedium } from '@khanacademy/wonder-blocks-typography';
9
9
  import { StyleSheet, css } from 'aphrodite';
10
10
  import classNames from 'classnames';
11
11
  import * as React from 'react';
@@ -17,13 +17,14 @@ import Clickable from '@khanacademy/wonder-blocks-clickable';
17
17
  import { Popover, PopoverContentCore, PopoverContent } from '@khanacademy/wonder-blocks-popover';
18
18
  import { color, spacing } from '@khanacademy/wonder-blocks-tokens';
19
19
  import $ from 'jquery';
20
+ import { v4 } from 'uuid';
20
21
  import PropTypes from 'prop-types';
21
22
  import { number, point, vector as vector$1, line } from '@khanacademy/kmath';
22
23
  import { entries, UnreachableCaseError, keys } from '@khanacademy/wonder-stuff-core';
23
24
  import { CircularSpinner } from '@khanacademy/wonder-blocks-progress-spinner';
24
25
  import SimpleMarkdown from '@khanacademy/simple-markdown';
25
26
  import { pureMarkdownRules } from '@khanacademy/pure-markdown';
26
- import { TextField, Checkbox } from '@khanacademy/wonder-blocks-form';
27
+ import { TextField } from '@khanacademy/wonder-blocks-form';
27
28
  import Button from '@khanacademy/wonder-blocks-button';
28
29
  import { Strut } from '@khanacademy/wonder-blocks-layout';
29
30
  import Banner from '@khanacademy/wonder-blocks-banner';
@@ -34,11 +35,11 @@ import debounce$2 from 'lodash.debounce';
34
35
  import { vec, Coordinates, Ellipse, Plot as Plot$3, Line as Line$4, Point as Point$2, Polygon, useTransformContext, Text as Text$1, Mafs } from 'mafs';
35
36
  import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
36
37
  import questionIcon from '@phosphor-icons/core/regular/question.svg';
37
- import createReactClass from 'create-react-class';
38
38
  import { useDrag } from '@use-gesture/react';
39
39
  import Switch from '@khanacademy/wonder-blocks-switch';
40
40
  import Pill from '@khanacademy/wonder-blocks-pill';
41
41
  import { Popper } from 'react-popper';
42
+ import createReactClass from 'create-react-class';
42
43
  import Link from '@khanacademy/wonder-blocks-link';
43
44
 
44
45
  function _extends() {
@@ -244,7 +245,9 @@ const mockStrings = {
244
245
  loading: "Loading...",
245
246
  videoTranscript: "See video transcript",
246
247
  somethingWrong: "Something went wrong.",
247
- videoWrapper: "Khan Academy video wrapper"
248
+ videoWrapper: "Khan Academy video wrapper",
249
+ mathInputTitle: "mathematics keyboard",
250
+ mathInputDescription: "Use keyboard/mouse to interact with math-based input fields"
248
251
  };
249
252
 
250
253
  /**
@@ -275,6 +278,26 @@ function PerseusI18nContextProvider({
275
278
  }
276
279
  const usePerseusI18n = () => useContext(PerseusI18nContext);
277
280
 
281
+ /*
282
+ * A11y (Accessibility) style rules
283
+ * These are used to mark up areas on the site that should only be readable
284
+ * when using a screen reader.
285
+ */
286
+ var a11y = StyleSheet.create({
287
+ // Make content only visible to screen readers.
288
+ // Both collegeboard.org and Bootstrap 3 use this exact implementation.
289
+ srOnly: {
290
+ border: 0,
291
+ clip: "rect(0,0,0,0)",
292
+ height: 1,
293
+ margin: -1,
294
+ overflow: "hidden",
295
+ padding: 0,
296
+ position: "absolute",
297
+ width: 1
298
+ }
299
+ });
300
+
278
301
  const debounce = (func, delay) => {
279
302
  let timer = null;
280
303
  return (...args) => {
@@ -472,11 +495,12 @@ class InnerMathInput extends React.Component {
472
495
  "mq-editable-field": true,
473
496
  "mq-math-mode": true
474
497
  });
498
+ const popoverContentUniqueId = v4().slice(0, 8);
475
499
  if (this.props.className) {
476
500
  className = className + " " + this.props.className;
477
501
  }
478
502
  return /*#__PURE__*/React.createElement(View, {
479
- style: [styles$E.outerWrapper, this.state.focused && styles$E.wrapperFocused, this.props.hasError && styles$E.wrapperError]
503
+ style: [styles$D.outerWrapper, this.state.focused && styles$D.wrapperFocused, this.props.hasError && styles$D.wrapperError]
480
504
  }, /*#__PURE__*/React.createElement("div", {
481
505
  style: {
482
506
  display: "flex",
@@ -504,18 +528,23 @@ class InnerMathInput extends React.Component {
504
528
  opened: this.state.keypadOpen,
505
529
  onClose: () => this.closeKeypad(),
506
530
  dismissEnabled: true,
531
+ "aria-label": this.context.strings.mathInputTitle,
532
+ "aria-describedby": `popover-content-${popoverContentUniqueId}`,
507
533
  content: () => {
508
534
  var _this$props$keypadBut, _this$props;
509
- return /*#__PURE__*/React.createElement(PopoverContentCore, {
535
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(HeadingMedium, {
536
+ id: `popover-content-${popoverContentUniqueId}`,
537
+ style: a11y.srOnly
538
+ }, this.context.strings.mathInputDescription), /*#__PURE__*/React.createElement(PopoverContentCore, {
510
539
  closeButtonVisible: true,
511
- style: styles$E.popoverContent
540
+ style: styles$D.popoverContent
512
541
  }, /*#__PURE__*/React.createElement(DesktopKeypad, _extends({
513
542
  onAnalyticsEvent: this.props.analytics.onAnalyticsEvent,
514
543
  extraKeys: this.props.extraKeys,
515
544
  onClickKey: this.handleKeypadPress,
516
545
  cursorContext: this.state.cursorContext,
517
546
  convertDotToTimes: this.props.convertDotToTimes
518
- }, (_this$props$keypadBut = this.props.keypadButtonSets) != null ? _this$props$keypadBut : mapButtonSets((_this$props = this.props) == null ? void 0 : _this$props.buttonSets))));
547
+ }, (_this$props$keypadBut = this.props.keypadButtonSets) != null ? _this$props$keypadBut : mapButtonSets((_this$props = this.props) == null ? void 0 : _this$props.buttonSets)))));
519
548
  }
520
549
  }, this.props.buttonsVisible === "never" ? /*#__PURE__*/React.createElement(MathInputIcon, {
521
550
  hovered: false,
@@ -584,9 +613,9 @@ const MathInputIcon = ({
584
613
  fillColor = color.offBlack;
585
614
  break;
586
615
  }
587
- const dynamicClass = active || focused ? styles$E.iconActive : styles$E.iconInactive;
616
+ const dynamicClass = active || focused ? styles$D.iconActive : styles$D.iconInactive;
588
617
  return /*#__PURE__*/React.createElement(View, {
589
- style: [styles$E.iconContainer, dynamicClass]
618
+ style: [styles$D.iconContainer, dynamicClass]
590
619
  }, /*#__PURE__*/React.createElement("svg", {
591
620
  xmlns: "http://www.w3.org/2000/svg",
592
621
  width: "16",
@@ -631,7 +660,7 @@ const inputFocused = {
631
660
  borderColor: color.blue,
632
661
  margin: -1
633
662
  };
634
- const styles$E = StyleSheet.create({
663
+ const styles$D = StyleSheet.create({
635
664
  iconContainer: {
636
665
  display: "flex",
637
666
  justifyContent: "center",
@@ -640,9 +669,11 @@ const styles$E = StyleSheet.create({
640
669
  borderRadius: 1
641
670
  },
642
671
  iconInactive: {
672
+ border: "2px solid transparent",
643
673
  backgroundColor: color.offBlack8
644
674
  },
645
675
  iconActive: {
676
+ border: `2px solid ${color.white}`,
646
677
  backgroundColor: color.offBlack64
647
678
  },
648
679
  outerWrapper: {
@@ -1161,26 +1192,6 @@ const ClassNames = {
1161
1192
  MOBILE: "perseus-mobile"
1162
1193
  };
1163
1194
 
1164
- /*
1165
- * A11y (Accessibility) style rules
1166
- * These are used to mark up areas on the site that should only be readable
1167
- * when using a screen reader.
1168
- */
1169
- var a11y = StyleSheet.create({
1170
- // Make content only visible to screen readers.
1171
- // Both collegeboard.org and Bootstrap 3 use this exact implementation.
1172
- srOnly: {
1173
- border: 0,
1174
- clip: "rect(0,0,0,0)",
1175
- height: 1,
1176
- margin: -1,
1177
- overflow: "hidden",
1178
- padding: 0,
1179
- position: "absolute",
1180
- width: 1
1181
- }
1182
- });
1183
-
1184
1195
  const KhanMath = {
1185
1196
  // Simplify formulas before display
1186
1197
  cleanMath: function (expr) {
@@ -2475,7 +2486,7 @@ class Expression extends React.Component {
2475
2486
  var _this$props$keypadCon, _this$props$analytics3;
2476
2487
  if (this.props.apiOptions.customKeypad) {
2477
2488
  return /*#__PURE__*/React.createElement(View, {
2478
- className: css(styles$D.mobileLabelInputWrapper)
2489
+ className: css(styles$C.mobileLabelInputWrapper)
2479
2490
  }, !!this.props.visibleLabel && /*#__PURE__*/React.createElement(LabelSmall, {
2480
2491
  htmlFor: this._textareaId,
2481
2492
  tag: "label"
@@ -2510,7 +2521,7 @@ class Expression extends React.Component {
2510
2521
  ERROR_TITLE
2511
2522
  } = this.context.strings;
2512
2523
  return /*#__PURE__*/React.createElement(View, {
2513
- className: css(styles$D.desktopLabelInputWrapper)
2524
+ className: css(styles$C.desktopLabelInputWrapper)
2514
2525
  }, !!this.props.visibleLabel && /*#__PURE__*/React.createElement(LabelSmall, {
2515
2526
  htmlFor: this._textareaId,
2516
2527
  tag: "label"
@@ -2562,7 +2573,7 @@ Expression.defaultProps = {
2562
2573
  apiOptions: ApiOptions.defaults,
2563
2574
  linterContext: linterContextDefault
2564
2575
  };
2565
- const styles$D = StyleSheet.create({
2576
+ const styles$C = StyleSheet.create({
2566
2577
  mobileLabelInputWrapper: {
2567
2578
  padding: "15px 4px 0"
2568
2579
  },
@@ -8942,8 +8953,8 @@ function findChildOrAdd(elem, className) {
8942
8953
  return $child;
8943
8954
  }
8944
8955
  var Tex$1 = {
8945
- // Process a node and add math inside of it. This attempts to use KaTeX to
8946
- // format the math, and if that fails it falls back to MathJax.
8956
+ // Process a node and add math inside of it. This uses MathJax to
8957
+ // format the math.
8947
8958
  //
8948
8959
  // elem: The element which the math should be added to.
8949
8960
  //
@@ -15305,7 +15316,7 @@ const DEFAULT_STATE$1 = {
15305
15316
  // TODO(benchristel): this is a memory leak. We add items to
15306
15317
  // tooltipResetFunctions, but never remove them.
15307
15318
  const tooltipResetFunctions = [];
15308
- class MovablePoint$4 {
15319
+ class MovablePoint$5 {
15309
15320
  constructor(graphie, movable, options) {
15310
15321
  this.graphie = void 0;
15311
15322
  this.movable = void 0;
@@ -15600,9 +15611,9 @@ class MovablePoint$4 {
15600
15611
  * Displays a tooltip above the point, replacing any previous contents. If
15601
15612
  * there is no tooltip initialized, adds the tooltip.
15602
15613
  *
15603
- * If the type of contents is string, the contents will be rendered with
15604
- * KaTeX. Otherwise, the content will be assumed to be a DOM node and will
15605
- * be appended inside the tooltip.
15614
+ * If the type of contents is string, the contents will be rendered as TeX
15615
+ * Otherwise, the content will be assumed to be a DOM node and will be
15616
+ * appended inside the tooltip.
15606
15617
  */
15607
15618
  _showTooltip(contents) {
15608
15619
  if (!this._tooltip) {
@@ -15823,15 +15834,15 @@ class MovablePoint$4 {
15823
15834
  this.movable.toFront();
15824
15835
  }
15825
15836
  }
15826
- MovablePoint$4.add = MovablePointOptions.add;
15827
- MovablePoint$4.modify = MovablePointOptions.modify;
15828
- MovablePoint$4.draw = MovablePointOptions.draw;
15829
- MovablePoint$4.remove = MovablePointOptions.remove;
15830
- MovablePoint$4.onMoveStart = MovablePointOptions.onMoveStart;
15831
- MovablePoint$4.constraints = MovablePointOptions.constraints;
15832
- MovablePoint$4.onMove = MovablePointOptions.onMove;
15833
- MovablePoint$4.onMoveEnd = MovablePointOptions.onMoveEnd;
15834
- MovablePoint$4.onClick = MovablePointOptions.onClick;
15837
+ MovablePoint$5.add = MovablePointOptions.add;
15838
+ MovablePoint$5.modify = MovablePointOptions.modify;
15839
+ MovablePoint$5.draw = MovablePointOptions.draw;
15840
+ MovablePoint$5.remove = MovablePointOptions.remove;
15841
+ MovablePoint$5.onMoveStart = MovablePointOptions.onMoveStart;
15842
+ MovablePoint$5.constraints = MovablePointOptions.constraints;
15843
+ MovablePoint$5.onMove = MovablePointOptions.onMove;
15844
+ MovablePoint$5.onMoveEnd = MovablePointOptions.onMoveEnd;
15845
+ MovablePoint$5.onClick = MovablePointOptions.onClick;
15835
15846
  function getKey(eventName, id) {
15836
15847
  return eventName + ":" + id;
15837
15848
  }
@@ -16425,10 +16436,10 @@ _.extend(MovablePolygon.prototype, {
16425
16436
  });
16426
16437
 
16427
16438
  const Interactive2 = {
16428
- MovablePoint: MovablePoint$4,
16439
+ MovablePoint: MovablePoint$5,
16429
16440
  addMovablePoint: function (graphie, options) {
16430
16441
  const movable = new Movable(graphie, {});
16431
- return new MovablePoint$4(graphie, movable, options);
16442
+ return new MovablePoint$5(graphie, movable, options);
16432
16443
  },
16433
16444
  MovableLine: MovableLine$4,
16434
16445
  addMovableLine: function (graphie, options) {
@@ -16469,7 +16480,7 @@ const Interactive2 = {
16469
16480
  }
16470
16481
  };
16471
16482
 
16472
- const MovablePoint$3 = GraphieClasses.createClass({
16483
+ const MovablePoint$4 = GraphieClasses.createClass({
16473
16484
  displayName: "MovablePoint",
16474
16485
  movableProps: ["children"],
16475
16486
  _getProps: function () {
@@ -16526,7 +16537,7 @@ const MovablePoint$3 = GraphieClasses.createClass({
16526
16537
  });
16527
16538
 
16528
16539
  // Include helper methods, such as MovablePoint.constrain.snap()
16529
- _.extend(MovablePoint$3, Interactive2.MovablePoint);
16540
+ _.extend(MovablePoint$4, Interactive2.MovablePoint);
16530
16541
  const MovableLine$3 = GraphieClasses.createClass({
16531
16542
  displayName: "MovableLine",
16532
16543
  movableProps: ["children"],
@@ -16696,7 +16707,7 @@ var Movables = {
16696
16707
  Label: Label$1,
16697
16708
  Line: Line$3,
16698
16709
  MovableLine: MovableLine$3,
16699
- MovablePoint: MovablePoint$3,
16710
+ MovablePoint: MovablePoint$4,
16700
16711
  Parabola: Parabola,
16701
16712
  Path: Path,
16702
16713
  Plot: Plot$2,
@@ -17025,17 +17036,15 @@ class ImageLoader extends React.Component {
17025
17036
  src,
17026
17037
  imgProps
17027
17038
  } = this.props;
17028
- let onKeyUp = null;
17029
- let onKeyDown = null;
17039
+ let onKeyUp;
17040
+ let onKeyDown;
17030
17041
  if (imgProps.onClick != null) {
17031
- // @ts-expect-error - TS2322 - Type '(e: React.KeyboardEvent) => void' is not assignable to type 'null'.
17032
17042
  onKeyUp = e => {
17033
17043
  // 13 is enter key, 32 is space key
17034
17044
  if (e.keyCode === 13 || e.keyCode === 32) {
17035
17045
  imgProps.onClick && imgProps.onClick(e);
17036
17046
  }
17037
17047
  };
17038
- // @ts-expect-error - TS2322 - Type '(e: React.KeyboardEvent) => void' is not assignable to type 'null'.
17039
17048
  onKeyDown = e => {
17040
17049
  // 32 is space key
17041
17050
  if (e.keyCode === 32) {
@@ -17046,12 +17055,8 @@ class ImageLoader extends React.Component {
17046
17055
  }
17047
17056
  const staticUrl = getDependencies().staticUrl;
17048
17057
  return /*#__PURE__*/React.createElement("img", _extends({
17049
- src: staticUrl(src)
17050
- // @ts-expect-error - TS2322 - Type 'null' is not assignable to type 'KeyboardEventHandler<HTMLImageElement> | undefined'.
17051
- ,
17052
- onKeyUp: onKeyUp
17053
- // @ts-expect-error - TS2322 - Type 'null' is not assignable to type 'KeyboardEventHandler<HTMLImageElement> | undefined'.
17054
- ,
17058
+ src: staticUrl(src),
17059
+ onKeyUp: onKeyUp,
17055
17060
  onKeyDown: onKeyDown
17056
17061
  }, imgProps));
17057
17062
  };
@@ -17549,18 +17554,14 @@ class SvgImage extends React.Component {
17549
17554
  allowFullBleed: this.props.allowFullBleed && isImageProbablyPhotograph(imageSrc)
17550
17555
  }, /*#__PURE__*/React.createElement(ImageLoader, {
17551
17556
  src: imageSrc,
17552
- imgProps: imageProps
17553
- // @ts-expect-error - TS2322 - Type '(() => Element) | null' is not assignable to type '() => ReactElement<any, string | JSXElementConstructor<any>> | null | undefined'.
17554
- ,
17557
+ imgProps: imageProps,
17555
17558
  preloader: preloader,
17556
17559
  onUpdate: this.handleUpdate
17557
17560
  }), extraGraphie);
17558
17561
  }
17559
17562
  imageProps.style = dimensions;
17560
17563
  return /*#__PURE__*/React.createElement(ImageLoader, {
17561
- src: imageSrc
17562
- // @ts-expect-error - TS2322 - Type '(() => Element) | null' is not assignable to type '() => ReactElement<any, string | JSXElementConstructor<any>> | null | undefined'.
17563
- ,
17564
+ src: imageSrc,
17564
17565
  preloader: preloader,
17565
17566
  imgProps: imageProps,
17566
17567
  onUpdate: this.handleUpdate
@@ -17610,9 +17611,7 @@ class SvgImage extends React.Component {
17610
17611
  }, /*#__PURE__*/React.createElement(ImageLoader, {
17611
17612
  src: imageUrl,
17612
17613
  onLoad: this.onImageLoad,
17613
- onUpdate: this.handleUpdate
17614
- // @ts-expect-error - TS2322 - Type '(() => Element) | null' is not assignable to type '() => ReactElement<any, string | JSXElementConstructor<any>> | null | undefined'.
17615
- ,
17614
+ onUpdate: this.handleUpdate,
17616
17615
  preloader: preloader,
17617
17616
  imgProps: imageProps
17618
17617
  }), graphie, extraGraphie);
@@ -17624,9 +17623,7 @@ class SvgImage extends React.Component {
17624
17623
  }, /*#__PURE__*/React.createElement(ImageLoader, {
17625
17624
  src: imageUrl,
17626
17625
  onLoad: this.onImageLoad,
17627
- onUpdate: this.handleUpdate
17628
- // @ts-expect-error - TS2322 - Type '(() => Element) | null' is not assignable to type '() => ReactElement<any, string | JSXElementConstructor<any>> | null | undefined'.
17629
- ,
17626
+ onUpdate: this.handleUpdate,
17630
17627
  preloader: preloader,
17631
17628
  imgProps: imageProps
17632
17629
  }), graphie);
@@ -18199,33 +18196,33 @@ class Lint extends React.Component {
18199
18196
  let warningText;
18200
18197
  let warningTextStyle;
18201
18198
  if (this.props.severity === Severity.Error) {
18202
- severityStyle = styles$C.indicatorError;
18199
+ severityStyle = styles$B.indicatorError;
18203
18200
  warningText = "Error";
18204
- warningTextStyle = styles$C.publishBlockingError;
18201
+ warningTextStyle = styles$B.publishBlockingError;
18205
18202
  } else if (this.props.severity === Severity.Warning) {
18206
- severityStyle = styles$C.indicatorWarning;
18203
+ severityStyle = styles$B.indicatorWarning;
18207
18204
  warningText = "Warning";
18208
- warningTextStyle = styles$C.warning;
18205
+ warningTextStyle = styles$B.warning;
18209
18206
  } else {
18210
- severityStyle = styles$C.indicatorGuideline;
18207
+ severityStyle = styles$B.indicatorGuideline;
18211
18208
  warningText = "Recommendation";
18212
- warningTextStyle = styles$C.warning;
18209
+ warningTextStyle = styles$B.warning;
18213
18210
  }
18214
18211
  return /*#__PURE__*/React.createElement("a", {
18215
18212
  href: `https://khanacademy.org/r/linter-rules#${this.props.ruleName}`,
18216
18213
  target: "lint-help-window",
18217
18214
  className: css(style)
18218
18215
  }, /*#__PURE__*/React.createElement("span", {
18219
- className: css(styles$C.indicator, severityStyle)
18216
+ className: css(styles$B.indicator, severityStyle)
18220
18217
  }, this.props.severity === 1 && /*#__PURE__*/React.createElement(InlineIcon, exclamationIcon)), /*#__PURE__*/React.createElement("div", {
18221
- className: css(styles$C.tooltip, tooltipAbove && styles$C.tooltipAbove)
18218
+ className: css(styles$B.tooltip, tooltipAbove && styles$B.tooltipAbove)
18222
18219
  }, this.props.message.split("\n\n").map((m, i) => /*#__PURE__*/React.createElement("p", {
18223
18220
  key: i,
18224
- className: css(styles$C.tooltipParagraph)
18221
+ className: css(styles$B.tooltipParagraph)
18225
18222
  }, /*#__PURE__*/React.createElement("span", {
18226
18223
  className: css(warningTextStyle)
18227
18224
  }, warningText, ":", " "), m)), /*#__PURE__*/React.createElement("div", {
18228
- className: css(styles$C.tail, tooltipAbove && styles$C.tailAbove)
18225
+ className: css(styles$B.tail, tooltipAbove && styles$B.tailAbove)
18229
18226
  })));
18230
18227
  };
18231
18228
  }
@@ -18263,20 +18260,20 @@ class Lint extends React.Component {
18263
18260
  }
18264
18261
  if (blockHighlight) {
18265
18262
  return /*#__PURE__*/React.createElement("span", {
18266
- className: css(styles$C.lintContainer, styles$C.lintContainerBlock)
18267
- }, this.renderLink(styles$C.radioWidgetHoverTarget), /*#__PURE__*/React.createElement("span", null, children));
18263
+ className: css(styles$B.lintContainer, styles$B.lintContainerBlock)
18264
+ }, this.renderLink(styles$B.radioWidgetHoverTarget), /*#__PURE__*/React.createElement("span", null, children));
18268
18265
  }
18269
18266
  if (inline) {
18270
18267
  return /*#__PURE__*/React.createElement("span", {
18271
- className: css(styles$C.lintContainer)
18272
- }, this.renderLink(styles$C.inlineHoverTarget), /*#__PURE__*/React.createElement("span", null, children));
18268
+ className: css(styles$B.lintContainer)
18269
+ }, this.renderLink(styles$B.inlineHoverTarget), /*#__PURE__*/React.createElement("span", null, children));
18273
18270
  }
18274
18271
  return /*#__PURE__*/React.createElement("div", {
18275
- className: css(styles$C.lintContainer)
18276
- }, this.renderLink(styles$C.hoverTarget), /*#__PURE__*/React.createElement("div", null, children));
18272
+ className: css(styles$B.lintContainer)
18273
+ }, this.renderLink(styles$B.hoverTarget), /*#__PURE__*/React.createElement("div", null, children));
18277
18274
  }
18278
18275
  }
18279
- const styles$C = StyleSheet.create({
18276
+ const styles$B = StyleSheet.create({
18280
18277
  // This is the class of the outermost element.
18281
18278
  // We use relative positioning so that the lint indicator can be
18282
18279
  // positioned absolutely relative to the position of the linty container.
@@ -18953,15 +18950,17 @@ class TranslationLinter {
18953
18950
  }
18954
18951
 
18955
18952
  /**
18956
- * Preprocess TeX code to convert things that KaTeX doesn't know how to handle
18953
+ * Preprocess TeX code to convert things that MathJax doesn't know how to handle
18957
18954
  * to things is does.
18958
18955
  */
18959
-
18960
18956
  var preprocessTex = (texCode => texCode
18961
18957
  // Replace uses of \begin{align}...\end{align} which KaTeX doesn't
18962
18958
  // support (yet) with \begin{aligned}...\end{aligned} which renders
18963
18959
  // the same is supported by KaTeX. It does the same for align*.
18964
18960
  // TODO(kevinb) update content to use aligned instead of align.
18961
+ // TODO(LEMS-1608) Remove this replacement as MathJax supports the
18962
+ // "align" macro correctly (and, in fact, it is not synonymous with
18963
+ // "aligned").
18965
18964
  .replace(/\{align[*]?\}/g, "{aligned}")
18966
18965
  // Replace non-breaking spaces with regular spaces.
18967
18966
  .replace(/[\u00a0]/g, " "));
@@ -20211,7 +20210,7 @@ class Renderer extends React.Component {
20211
20210
  paddingLeft: margin,
20212
20211
  paddingRight: margin
20213
20212
  };
20214
- const mobileInnerStyle = _extends({}, innerStyle, styles$B.mobileZoomableParentFix);
20213
+ const mobileInnerStyle = _extends({}, innerStyle, styles$A.mobileZoomableParentFix);
20215
20214
  return /*#__PURE__*/React.createElement("div", {
20216
20215
  key: state.key,
20217
20216
  className: "perseus-block-math",
@@ -20238,6 +20237,9 @@ class Renderer extends React.Component {
20238
20237
  // support (yet) with \begin{aligned}...\end{aligned} which renders
20239
20238
  // the same is supported by KaTeX. It does the same for align*.
20240
20239
  // TODO(kevinb) update content to use aligned instead of align.
20240
+ // TODO(LEMS-1608) Remove this replacement as MathJax supports the
20241
+ // "align" macro correctly (and, in fact, it is not synonymous with
20242
+ // "aligned").
20241
20243
  const tex = node.content.replace(/\{align[*]?\}/g, "{aligned}");
20242
20244
 
20243
20245
  // We render math here instead of in perseus-markdown.jsx
@@ -20332,7 +20334,7 @@ class Renderer extends React.Component {
20332
20334
  paddingLeft: 0,
20333
20335
  paddingRight: 0
20334
20336
  };
20335
- const mobileInnerStyle = _extends({}, innerStyle, styles$B.mobileZoomableParentFix);
20337
+ const mobileInnerStyle = _extends({}, innerStyle, styles$A.mobileZoomableParentFix);
20336
20338
  const wrappedOutput = /*#__PURE__*/React.createElement("div", {
20337
20339
  style: _extends({}, mobileInnerStyle, {
20338
20340
  overflowX: "auto"
@@ -20981,7 +20983,7 @@ Renderer.defaultProps = {
20981
20983
  onSerializedStateUpdated: () => {},
20982
20984
  linterContext: PerseusLinter.linterContextDefault
20983
20985
  };
20984
- const styles$B = {
20986
+ const styles$A = {
20985
20987
  mobileZoomableParentFix: {
20986
20988
  // NOTE(abdul): There is an issue where transform animations will
20987
20989
  // cause the Zoomable component to disappear when running on the
@@ -21052,28 +21054,24 @@ class TextInput extends React.Component {
21052
21054
  style
21053
21055
  } = this.props;
21054
21056
  const formattedValue = value === null ? "" : value.toString();
21055
- return (
21056
- /*#__PURE__*/
21057
- // $FlowIgnore
21058
- React.createElement(TextField, {
21059
- style: style,
21060
- disabled: disabled,
21061
- id: this.id,
21062
- value: formattedValue,
21063
- type: "text",
21064
- "aria-label": labelText,
21065
- onChange: value => this.props.onChange(value),
21066
- placeholder: placeholder,
21067
- onFocus: onFocus,
21068
- onBlur: onBlur,
21069
- onKeyDown: onKeyDown
21070
- // @ts-expect-error - TS2322 - Type '{ style: StyleType; disabled: boolean | undefined; id: string; value: string; type: "text"; "aria-label": string | undefined; onChange: (value: string) => void; placeholder: string | undefined; ... 5 more ...; autoComplete: string; }' is not assignable to type 'IntrinsicAttributes & ExportProps & RefAttributes<HTMLInputElement>'.
21071
- ,
21072
- autoCorrect: "off",
21073
- autoCapitalize: "off",
21074
- autoComplete: "off"
21075
- })
21076
- );
21057
+ return /*#__PURE__*/React.createElement(TextField, {
21058
+ style: style,
21059
+ disabled: disabled,
21060
+ id: this.id,
21061
+ value: formattedValue,
21062
+ type: "text",
21063
+ "aria-label": labelText,
21064
+ onChange: value => this.props.onChange(value),
21065
+ placeholder: placeholder,
21066
+ onFocus: onFocus,
21067
+ onBlur: onBlur,
21068
+ onKeyDown: onKeyDown
21069
+ // @ts-expect-error - TS2322 - Type '{ style: StyleType; disabled: boolean | undefined; id: string; value: string; type: "text"; "aria-label": string | undefined; onChange: (value: string) => void; placeholder: string | undefined; ... 5 more ...; autoComplete: string; }' is not assignable to type 'IntrinsicAttributes & ExportProps & RefAttributes<HTMLInputElement>'.
21070
+ ,
21071
+ autoCorrect: "off",
21072
+ autoCapitalize: "off",
21073
+ autoComplete: "off"
21074
+ });
21077
21075
  }
21078
21076
  }
21079
21077
  TextInput.defaultProps = {
@@ -21855,10 +21853,10 @@ class InputNumber extends React.Component {
21855
21853
  // Note: This is _very_ similar to what `numeric-input.jsx` does. If
21856
21854
  // you modify this, double-check if you also need to modify that
21857
21855
  // component.
21858
- const inputStyles = [styles$A.default, this.props.size === "small" ? styles$A.small : null, this.props.rightAlign ? styles$A.rightAlign : styles$A.leftAlign];
21856
+ const inputStyles = [styles$z.default, this.props.size === "small" ? styles$z.small : null, this.props.rightAlign ? styles$z.rightAlign : styles$z.leftAlign];
21859
21857
  // Unanswered
21860
21858
  if (rubric && !this.props.currentValue) {
21861
- inputStyles.push(styles$A.answerStateUnanswered);
21859
+ inputStyles.push(styles$z.answerStateUnanswered);
21862
21860
  }
21863
21861
  return /*#__PURE__*/React.createElement(InputWithExamples
21864
21862
  // eslint-disable-next-line react/no-string-refs
@@ -21943,7 +21941,7 @@ InputNumber.defaultProps = {
21943
21941
  apiOptions: ApiOptions.defaults,
21944
21942
  linterContext: linterContextDefault
21945
21943
  };
21946
- const styles$A = StyleSheet.create({
21944
+ const styles$z = StyleSheet.create({
21947
21945
  default: {
21948
21946
  width: 80,
21949
21947
  height: "auto"
@@ -22938,7 +22936,7 @@ const FocusRing = function FocusRing(props) {
22938
22936
  };
22939
22937
  return /*#__PURE__*/React.createElement("span", {
22940
22938
  "data-testid": "focus-ring",
22941
- className: css(styles$z.ring),
22939
+ className: css(styles$y.ring),
22942
22940
  style: style
22943
22941
  }, children);
22944
22942
  };
@@ -22947,7 +22945,7 @@ FocusRing.defaultProps = {
22947
22945
  color: kaGreen$1,
22948
22946
  multipleSelect: false
22949
22947
  };
22950
- const styles$z = StyleSheet.create({
22948
+ const styles$y = StyleSheet.create({
22951
22949
  ring: {
22952
22950
  margin: "auto",
22953
22951
  display: "inline-block",
@@ -22974,7 +22972,7 @@ function getChoiceLetter(pos, strings) {
22974
22972
  }
22975
22973
 
22976
22974
  const CHOICE_ICON_SIZE = 24;
22977
- const styles$y = StyleSheet.create({
22975
+ const styles$x = StyleSheet.create({
22978
22976
  iconWrapper: {
22979
22977
  display: "inline-block",
22980
22978
  position: "relative"
@@ -22997,7 +22995,7 @@ const CrossOutLine = function CrossOutLine(props) {
22997
22995
  width: CROSS_OUT_LINE_SIZE,
22998
22996
  height: CROSS_OUT_LINE_SIZE,
22999
22997
  viewBox: `0 0 ${CROSS_OUT_LINE_SIZE} ${CROSS_OUT_LINE_SIZE}`,
23000
- className: css(styles$x.crossOutLine),
22998
+ className: css(styles$w.crossOutLine),
23001
22999
  "data-testid": "choice-icon__cross-out-line"
23002
23000
  }, /*#__PURE__*/React.createElement("line", {
23003
23001
  // The line stretches from the bottom-left to top-right.
@@ -23012,7 +23010,7 @@ const CrossOutLine = function CrossOutLine(props) {
23012
23010
  strokeLinecap: "round"
23013
23011
  }));
23014
23012
  };
23015
- const styles$x = StyleSheet.create({
23013
+ const styles$w = StyleSheet.create({
23016
23014
  crossOutLine: {
23017
23015
  // Center the icon within the container.
23018
23016
  position: "absolute",
@@ -23096,7 +23094,7 @@ const ChoiceIcon = function ChoiceIcon(props) {
23096
23094
  } = props;
23097
23095
  const dynamicStyles = getDynamicStyles(checked, showCorrectness, pressed, multipleSelect, correct);
23098
23096
  return /*#__PURE__*/React.createElement("div", {
23099
- className: css(styles$y.iconWrapper)
23097
+ className: css(styles$x.iconWrapper)
23100
23098
  }, /*#__PURE__*/React.createElement(FocusRing, {
23101
23099
  color: color.blue,
23102
23100
  visible: focused || hovered,
@@ -23105,13 +23103,13 @@ const ChoiceIcon = function ChoiceIcon(props) {
23105
23103
  // @ts-expect-error - TS2322 - Type '{ backgroundColor: string | null | undefined; borderColor: string; color: string; borderRadius: number; }' is not assignable to type 'Properties<string | number, string & {}>'.
23106
23104
  style: dynamicStyles,
23107
23105
  "data-testid": "choice-icon__library-choice-icon",
23108
- className: css(styles$w.circle, showCorrectness && correct && styles$w.circleCorrect, showCorrectness && !correct && styles$w.circleIncorrect, showCorrectness && !correct && (checked || previouslyAnswered) && styles$w.circleIncorrectAnswered)
23106
+ className: css(styles$v.circle, showCorrectness && correct && styles$v.circleCorrect, showCorrectness && !correct && styles$v.circleIncorrect, showCorrectness && !correct && (checked || previouslyAnswered) && styles$v.circleIncorrectAnswered)
23109
23107
  // used in BaseRadio editMode to check
23110
23108
  // if we actually clicked on the radio icon
23111
23109
  ,
23112
23110
  "data-is-radio-icon": true
23113
23111
  }, /*#__PURE__*/React.createElement("div", {
23114
- className: css(styles$w.innerWrapper)
23112
+ className: css(styles$v.innerWrapper)
23115
23113
  }, /*#__PURE__*/React.createElement(ChoiceInner, {
23116
23114
  pos: pos,
23117
23115
  showCorrectness: showCorrectness,
@@ -23120,7 +23118,7 @@ const ChoiceIcon = function ChoiceIcon(props) {
23120
23118
  color: dynamicStyles.borderColor
23121
23119
  }));
23122
23120
  };
23123
- const styles$w = StyleSheet.create({
23121
+ const styles$v = StyleSheet.create({
23124
23122
  innerWrapper: {
23125
23123
  width: CHOICE_ICON_SIZE,
23126
23124
  height: CHOICE_ICON_SIZE,
@@ -23215,19 +23213,19 @@ const OptionStatus = function OptionStatus(props) {
23215
23213
  }
23216
23214
  let textStyle;
23217
23215
  if (correct) {
23218
- textStyle = styles$v.correct;
23216
+ textStyle = styles$u.correct;
23219
23217
  } else {
23220
23218
  if (checked || previouslyAnswered) {
23221
- textStyle = styles$v.incorrectAnswered;
23219
+ textStyle = styles$u.incorrectAnswered;
23222
23220
  } else {
23223
- textStyle = styles$v.incorrect;
23221
+ textStyle = styles$u.incorrect;
23224
23222
  }
23225
23223
  }
23226
23224
  return /*#__PURE__*/React.createElement("div", {
23227
- className: css(styles$v.text, textStyle)
23225
+ className: css(styles$u.text, textStyle)
23228
23226
  }, renderText(checked, correct, crossedOut, strings));
23229
23227
  };
23230
- const styles$v = StyleSheet.create({
23228
+ const styles$u = StyleSheet.create({
23231
23229
  text: {
23232
23230
  alignItems: "center",
23233
23231
  display: "flex",
@@ -23297,8 +23295,8 @@ const Choice = function Choice(props) {
23297
23295
  crossedOut: updatedCrossedOut
23298
23296
  });
23299
23297
  }
23300
- const descriptionClassName = classNames("description", css(styles$u.description));
23301
- const rationaleClassName = classNames("perseus-radio-rationale-content", css(styles$u.rationale));
23298
+ const descriptionClassName = classNames("description", css(styles$t.description));
23299
+ const rationaleClassName = classNames("perseus-radio-rationale-content", css(styles$t.rationale));
23302
23300
 
23303
23301
  // We want to show the choices as dimmed out when the choices are disabled.
23304
23302
  // However, we don't want to do this when we're in review mode in the
@@ -23458,7 +23456,7 @@ const Choice = function Choice(props) {
23458
23456
  "data-testid": `perseus-radio-rationale-content-${pos}`
23459
23457
  }, rationale));
23460
23458
  };
23461
- const styles$u = StyleSheet.create({
23459
+ const styles$t = StyleSheet.create({
23462
23460
  description: {
23463
23461
  display: "inline-block",
23464
23462
  width: "100%"
@@ -23657,10 +23655,10 @@ const BaseRadio = function BaseRadio(props) {
23657
23655
  const isMobile = apiOptions.isMobile;
23658
23656
  const firstChoiceHighlighted = choices[0].highlighted;
23659
23657
  const lastChoiceHighlighted = choices[choices.length - 1].highlighted;
23660
- const className = classNames("perseus-widget-radio", !editMode && "perseus-rendered-radio", css(styles$t.radio, styles$t.responsiveRadioContainer, firstChoiceHighlighted && isMobile && styles$t.radioContainerFirstHighlighted, lastChoiceHighlighted && isMobile && styles$t.radioContainerLastHighlighted));
23661
- const instructionsClassName = classNames("instructions", css(styles$t.instructions, isMobile && styles$t.instructionsMobile));
23658
+ const className = classNames("perseus-widget-radio", !editMode && "perseus-rendered-radio", css(styles$s.radio, styles$s.responsiveRadioContainer, firstChoiceHighlighted && isMobile && styles$s.radioContainerFirstHighlighted, lastChoiceHighlighted && isMobile && styles$s.radioContainerLastHighlighted));
23659
+ const instructionsClassName = classNames("instructions", css(styles$s.instructions, isMobile && styles$s.instructionsMobile));
23662
23660
  const instructions = getInstructionsText(multipleSelect, countChoices, numCorrect, strings);
23663
- const responsiveClassName = css(styles$t.responsiveFieldset);
23661
+ const responsiveClassName = css(styles$s.responsiveFieldset);
23664
23662
  const fieldset = /*#__PURE__*/React.createElement("fieldset", {
23665
23663
  className: `perseus-widget-radio-fieldset ${responsiveClassName}`
23666
23664
  }, /*#__PURE__*/React.createElement("legend", {
@@ -23711,7 +23709,7 @@ const BaseRadio = function BaseRadio(props) {
23711
23709
  const aphroditeClassName = checked => {
23712
23710
  // Whether or not to show correctness borders
23713
23711
  // for this choice and the next choice.
23714
- return css(sharedStyles.aboveScratchpad, styles$t.item, styles$t.responsiveItem, checked && styles$t.selectedItem, checked && choice.highlighted && styles$t.aboveBackdrop, checked && choice.highlighted && apiOptions.isMobile && styles$t.aboveBackdropMobile, nextChoiceHighlighted && apiOptions.isMobile && styles$t.nextHighlighted);
23712
+ return css(sharedStyles.aboveScratchpad, styles$s.item, styles$s.responsiveItem, checked && styles$s.selectedItem, checked && choice.highlighted && styles$s.aboveBackdrop, checked && choice.highlighted && apiOptions.isMobile && styles$s.aboveBackdropMobile, nextChoiceHighlighted && apiOptions.isMobile && styles$s.nextHighlighted);
23715
23713
  };
23716
23714
 
23717
23715
  // HACK(abdulrahman): Preloads the selection-state
@@ -23785,14 +23783,14 @@ const BaseRadio = function BaseRadio(props) {
23785
23783
  // Allow for horizontal scrolling if content is too wide, which may be
23786
23784
  // an issue especially on phones.
23787
23785
  return /*#__PURE__*/React.createElement("div", {
23788
- className: css(styles$t.responsiveContainer)
23786
+ className: css(styles$s.responsiveContainer)
23789
23787
  }, fieldset);
23790
23788
  };
23791
23789
  BaseRadio.defaultProps = {
23792
23790
  editMode: false,
23793
23791
  multipleSelect: false
23794
23792
  };
23795
- const styles$t = StyleSheet.create({
23793
+ const styles$s = StyleSheet.create({
23796
23794
  instructions: {
23797
23795
  display: "block",
23798
23796
  color: gray17,
@@ -24454,9 +24452,9 @@ class Categorizer extends React.Component {
24454
24452
  indexedItems = Util.shuffle(indexedItems, this.props.problemNum);
24455
24453
  }
24456
24454
  const table = /*#__PURE__*/React.createElement("table", {
24457
- className: "categorizer-table " + css(styles$s.mobileTable)
24455
+ className: "categorizer-table " + css(styles$r.mobileTable)
24458
24456
  }, /*#__PURE__*/React.createElement("thead", null, /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("td", {
24459
- className: css(styles$s.emptyHeaderCell)
24457
+ className: css(styles$r.emptyHeaderCell)
24460
24458
  }), this.props.categories.map((category, i) => {
24461
24459
  // Array index is the correct key here, as that's
24462
24460
  // how category grading actually works -- no way
@@ -24464,7 +24462,7 @@ class Categorizer extends React.Component {
24464
24462
  // middle. (If we later add that, this should be
24465
24463
  // fixed.)
24466
24464
  return /*#__PURE__*/React.createElement("th", {
24467
- className: css(styles$s.header),
24465
+ className: css(styles$r.header),
24468
24466
  key: i
24469
24467
  }, /*#__PURE__*/React.createElement(Renderer, {
24470
24468
  content: category,
@@ -24486,7 +24484,7 @@ class Categorizer extends React.Component {
24486
24484
  })), self.props.categories.map((catName, catNum) => {
24487
24485
  const selected = self.props.values[itemNum] === catNum;
24488
24486
  return /*#__PURE__*/React.createElement("td", {
24489
- className: "category " + css(styles$s.cell),
24487
+ className: "category " + css(styles$r.cell),
24490
24488
  key: catNum
24491
24489
  }, /*#__PURE__*/React.createElement("div", {
24492
24490
  className: ClassNames.INTERACTIVE,
@@ -24501,7 +24499,7 @@ class Categorizer extends React.Component {
24501
24499
  onChange: () => this.onChange(itemNum, catNum),
24502
24500
  onClick: e => e.stopPropagation()
24503
24501
  }), !isMobile && /*#__PURE__*/React.createElement("span", {
24504
- className: css(styles$s.radioSpan, selected && styles$s.checkedRadioSpan, this.props.static && selected && styles$s.staticCheckedRadioSpan)
24502
+ className: css(styles$r.radioSpan, selected && styles$r.checkedRadioSpan, this.props.static && selected && styles$r.staticCheckedRadioSpan)
24505
24503
  }, selected ? /*#__PURE__*/React.createElement(InlineIcon, iconCircle) : /*#__PURE__*/React.createElement(InlineIcon, iconCircleThin))));
24506
24504
  }));
24507
24505
  /* eslint-enable max-len */
@@ -24513,7 +24511,7 @@ class Categorizer extends React.Component {
24513
24511
  "categorizer-container": true,
24514
24512
  "static-mode": this.props.static
24515
24513
  });
24516
- const inlineStyles = this.props.apiOptions.isMobile ? [styles$s.fullBleedContainer] : [];
24514
+ const inlineStyles = this.props.apiOptions.isMobile ? [styles$r.fullBleedContainer] : [];
24517
24515
  return /*#__PURE__*/React.createElement("div", {
24518
24516
  className: extraClassNames + " " + css(...inlineStyles)
24519
24517
  }, table);
@@ -24566,7 +24564,7 @@ const pageMargin = 16;
24566
24564
 
24567
24565
  // Stylesheets aren't directly testable
24568
24566
  /* c8 ignore next */
24569
- const styles$s = StyleSheet.create({
24567
+ const styles$r = StyleSheet.create({
24570
24568
  mobileTable: {
24571
24569
  [mediaQueries.smOrSmaller]: {
24572
24570
  minWidth: "auto"
@@ -24802,7 +24800,7 @@ class CSProgram extends React.Component {
24802
24800
  // creator "went wild".
24803
24801
  // http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
24804
24802
  return /*#__PURE__*/React.createElement("div", {
24805
- className: css(styles$r.widthOverride, styleContainer && styles$r.container)
24803
+ className: css(styles$q.widthOverride, styleContainer && styles$q.container)
24806
24804
  }, /*#__PURE__*/React.createElement("iframe", {
24807
24805
  sandbox: sandboxOptions,
24808
24806
  src: url,
@@ -24831,7 +24829,7 @@ CSProgram.defaultProps = {
24831
24829
  // optional message
24832
24830
  message: null
24833
24831
  };
24834
- const styles$r = StyleSheet.create({
24832
+ const styles$q = StyleSheet.create({
24835
24833
  // Override the inherited width from the perseus paragraph class
24836
24834
  // 820 is minimum width for 420px editor and 400px canvas
24837
24835
  widthOverride: {
@@ -24874,7 +24872,7 @@ class Definition extends React.Component {
24874
24872
  }) => /*#__PURE__*/React.createElement(Popover, {
24875
24873
  content: /*#__PURE__*/React.createElement(PopoverContentCore, {
24876
24874
  color: "white",
24877
- style: styles$q.tooltipBody,
24875
+ style: styles$p.tooltipBody,
24878
24876
  closeButtonVisible: true
24879
24877
  }, /*#__PURE__*/React.createElement(Renderer, {
24880
24878
  apiOptions: this.props.apiOptions,
@@ -24907,7 +24905,7 @@ Definition.defaultProps = {
24907
24905
  togglePrompt: "define me",
24908
24906
  definition: "definition goes here"
24909
24907
  };
24910
- const styles$q = {
24908
+ const styles$p = {
24911
24909
  tooltipBody: {
24912
24910
  color: color.offBlack,
24913
24911
  fontSize: 20,
@@ -25135,7 +25133,7 @@ class Explanation extends React.Component {
25135
25133
  marginRight: "-6px",
25136
25134
  "white-space": "normal"
25137
25135
  };
25138
- const contentStyling = [styles$p.content, this.state.expanded ? styles$p.contentExpanded : styles$p.contentCollapsed, allowTransition && (this.state.expanded ? styles$p.transitionExpanded : styles$p.transitionCollapsed)];
25136
+ const contentStyling = [styles$o.content, this.state.expanded ? styles$o.contentExpanded : styles$o.contentCollapsed, allowTransition && (this.state.expanded ? styles$o.transitionExpanded : styles$o.transitionCollapsed)];
25139
25137
  return /*#__PURE__*/React.createElement(UniqueIDProvider, {
25140
25138
  mockOnFirstRender: true,
25141
25139
  scope: "explanation-widget"
@@ -25154,7 +25152,7 @@ class Explanation extends React.Component {
25154
25152
  "aria-hidden": !this.state.expanded,
25155
25153
  testId: "content-container"
25156
25154
  }, /*#__PURE__*/React.createElement(View, {
25157
- style: styles$p.contentWrapper
25155
+ style: styles$o.contentWrapper
25158
25156
  }, /*#__PURE__*/React.createElement(Renderer, {
25159
25157
  apiOptions: this.props.apiOptions,
25160
25158
  content: this.props.explanation,
@@ -25175,7 +25173,7 @@ Explanation.defaultProps = {
25175
25173
  const leftBorderSpacing = 23;
25176
25174
  const verticalContentPadding = 10;
25177
25175
  const arrowHeight = 14;
25178
- const styles$p = StyleSheet.create({
25176
+ const styles$o = StyleSheet.create({
25179
25177
  content: {
25180
25178
  borderLeft: "0px solid #ccc",
25181
25179
  display: "inline-grid",
@@ -25239,15 +25237,15 @@ class GradedGroupAnswerBar extends React.Component {
25239
25237
  correctExcited,
25240
25238
  nextQuestion
25241
25239
  } = this.context.strings;
25242
- const answerBarStyle = _extends({}, styles$o.answerBar, {
25240
+ const answerBarStyle = _extends({}, styles$n.answerBar, {
25243
25241
  backgroundColor: answerBarState === "CORRECT" ? color.offWhite : "white",
25244
25242
  // Center the "Correct!" message only when there's no next question
25245
25243
  justifyContent: answerBarState === "CORRECT" && !onNextQuestion ? "center" : "space-between"
25246
25244
  });
25247
25245
  const message = answerBarState === "INCORRECT" ? /*#__PURE__*/React.createElement("span", {
25248
- style: styles$o.text
25246
+ style: styles$n.text
25249
25247
  }, /*#__PURE__*/React.createElement("span", {
25250
- style: styles$o.tryAgainIcon
25248
+ style: styles$n.tryAgainIcon
25251
25249
  }, /*#__PURE__*/React.createElement(InlineIcon, iconTryAgain)), /*#__PURE__*/React.createElement("span", {
25252
25250
  style: {
25253
25251
  marginLeft: 8
@@ -25266,7 +25264,7 @@ class GradedGroupAnswerBar extends React.Component {
25266
25264
  return /*#__PURE__*/React.createElement("div", {
25267
25265
  style: answerBarStyle
25268
25266
  }, /*#__PURE__*/React.createElement("span", {
25269
- style: styles$o.text
25267
+ style: styles$n.text
25270
25268
  }, /*#__PURE__*/React.createElement("span", {
25271
25269
  style: {
25272
25270
  fontSize: 28,
@@ -25289,7 +25287,7 @@ class GradedGroupAnswerBar extends React.Component {
25289
25287
  }
25290
25288
  GradedGroupAnswerBar.contextType = PerseusI18nContext;
25291
25289
  const fontSize = 17;
25292
- const styles$o = {
25290
+ const styles$n = {
25293
25291
  answerBar: {
25294
25292
  display: "flex",
25295
25293
  alignItems: "center",
@@ -25474,7 +25472,7 @@ class GradedGroup extends React.Component {
25474
25472
  }));
25475
25473
  gradeStatus = this.context.strings.incorrect;
25476
25474
  }
25477
- const mobileClass = this.props.inGradedGroupSet ? css(styles$n.gradedGroupInSet) : css(styles$n.gradedGroup);
25475
+ const mobileClass = this.props.inGradedGroupSet ? css(styles$m.gradedGroupInSet) : css(styles$m.gradedGroup);
25478
25476
  const classes = classNames({
25479
25477
  [mobileClass]: apiOptions.isMobile,
25480
25478
  "perseus-graded-group": true,
@@ -25493,7 +25491,7 @@ class GradedGroup extends React.Component {
25493
25491
  return /*#__PURE__*/React.createElement("div", {
25494
25492
  className: classes
25495
25493
  }, !!this.props.title && /*#__PURE__*/React.createElement("div", {
25496
- className: css(styles$n.title)
25494
+ className: css(styles$m.title)
25497
25495
  }, this.props.title), /*#__PURE__*/React.createElement(Renderer, _extends({}, this.props, {
25498
25496
  // eslint-disable-next-line react/no-string-refs
25499
25497
  ref: "renderer",
@@ -25526,7 +25524,7 @@ class GradedGroup extends React.Component {
25526
25524
  }, this.context.strings.nextQuestion), this.props.hint && this.props.hint.content && (this.state.showHint ? /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("button", {
25527
25525
  // @ts-expect-error - TS2322 - Type 'string' is not assignable to type 'number | undefined'.
25528
25526
  tabIndex: "0",
25529
- className: css(styles$n.explanationTitle),
25527
+ className: css(styles$m.explanationTitle),
25530
25528
  onClick: () => this.setState({
25531
25529
  showHint: false
25532
25530
  }),
@@ -25559,7 +25557,7 @@ class GradedGroup extends React.Component {
25559
25557
  showHint: true
25560
25558
  });
25561
25559
  },
25562
- className: css(styles$n.showHintLink)
25560
+ className: css(styles$m.showHintLink)
25563
25561
  }, this.context.strings.explain)), apiOptions.isMobile && answerBarState !== "HIDDEN" && /*#__PURE__*/React.createElement(GradedGroupAnswerBar, {
25564
25562
  apiOptions: apiOptions,
25565
25563
  answerBarState: answerBarState,
@@ -25581,7 +25579,7 @@ GradedGroup.defaultProps = {
25581
25579
  const traverseChildWidgets$2 = function traverseChildWidgets(props, traverseRenderer) {
25582
25580
  return _.extend({}, props, traverseRenderer(props));
25583
25581
  };
25584
- const styles$n = StyleSheet.create({
25582
+ const styles$m = StyleSheet.create({
25585
25583
  gradedGroupInSet: {
25586
25584
  // Reset a few desktop-only styles that come from graded-group.less
25587
25585
  marginLeft: 0,
@@ -25654,18 +25652,18 @@ class Indicators extends React.Component {
25654
25652
  return /*#__PURE__*/React.createElement("ul", {
25655
25653
  // reduntantly add class name for use in .less files--
25656
25654
  // the styles object key gets hashed
25657
- className: classNames(css(styles$m.indicatorContainer), "indicatorContainer")
25655
+ className: classNames(css(styles$l.indicatorContainer), "indicatorContainer")
25658
25656
  }, this.props.gradedGroups.map(({
25659
25657
  title
25660
25658
  }, i) => /*#__PURE__*/React.createElement("li", {
25661
- className: css(styles$m.indicator),
25659
+ className: css(styles$l.indicator),
25662
25660
  key: title
25663
25661
  }, /*#__PURE__*/React.createElement(Clickable, {
25664
25662
  role: "button",
25665
25663
  "aria-label": this.context.strings.skipToTitle({
25666
25664
  title
25667
25665
  }),
25668
- style: styles$m.indicatorButton,
25666
+ style: styles$l.indicatorButton,
25669
25667
  onClick: () => this.props.onChangeCurrentGroup(i),
25670
25668
  onKeyDown: e => this.handleKeyDown(e, i)
25671
25669
  }, ({
@@ -25673,9 +25671,9 @@ class Indicators extends React.Component {
25673
25671
  focused,
25674
25672
  pressed
25675
25673
  }) => /*#__PURE__*/React.createElement(View, {
25676
- style: [styles$m.indicatorDot, (hovered || focused || pressed) && styles$m.indicatorDotFocused]
25674
+ style: [styles$l.indicatorDot, (hovered || focused || pressed) && styles$l.indicatorDotFocused]
25677
25675
  }, i === this.props.currentGroup && /*#__PURE__*/React.createElement(View, {
25678
- style: styles$m.indicatorDotActive
25676
+ style: styles$l.indicatorDotActive
25679
25677
  }, /*#__PURE__*/React.createElement("span", {
25680
25678
  className: css(a11y.srOnly)
25681
25679
  }, this.context.strings.current)))))));
@@ -25739,7 +25737,7 @@ class GradedGroupSet extends React.Component {
25739
25737
  } = getDependencies();
25740
25738
  if (JIPT.useJIPT && this.props.gradedGroups.length > 1) {
25741
25739
  return /*#__PURE__*/React.createElement("div", {
25742
- className: css(styles$m.container)
25740
+ className: css(styles$l.container)
25743
25741
  }, this.props.gradedGroups.map((gradedGroup, i) => {
25744
25742
  return (
25745
25743
  /*#__PURE__*/
@@ -25762,13 +25760,13 @@ class GradedGroupSet extends React.Component {
25762
25760
  const numGroups = this.props.gradedGroups.length;
25763
25761
  const handleNextQuestion = this.state.currentGroup < numGroups - 1 ? this.handleNextQuestion : null;
25764
25762
  return /*#__PURE__*/React.createElement("div", {
25765
- className: css(styles$m.container)
25763
+ className: css(styles$l.container)
25766
25764
  }, /*#__PURE__*/React.createElement("div", {
25767
- className: css(styles$m.top)
25765
+ className: css(styles$l.top)
25768
25766
  }, /*#__PURE__*/React.createElement("div", {
25769
- className: css(styles$m.title)
25767
+ className: css(styles$l.title)
25770
25768
  }, currentGroup.title), /*#__PURE__*/React.createElement("div", {
25771
- className: css(styles$m.spacer)
25769
+ className: css(styles$l.spacer)
25772
25770
  }), /*#__PURE__*/React.createElement(Indicators, {
25773
25771
  currentGroup: this.state.currentGroup,
25774
25772
  gradedGroups: this.props.gradedGroups,
@@ -25810,7 +25808,7 @@ var GradedGroupSet$1 = {
25810
25808
  tracking: "all",
25811
25809
  isLintable: true
25812
25810
  };
25813
- const styles$m = StyleSheet.create({
25811
+ const styles$l = StyleSheet.create({
25814
25812
  top: {
25815
25813
  display: "flex",
25816
25814
  flexDirection: "row"
@@ -25915,7 +25913,7 @@ class ButtonGroup extends React.Component {
25915
25913
  id: "" + i,
25916
25914
  ref: "button" + i,
25917
25915
  key: "" + i,
25918
- className: css(styles$l.buttonStyle, button.value === value && styles$l.selectedStyle),
25916
+ className: css(styles$k.buttonStyle, button.value === value && styles$k.selectedStyle),
25919
25917
  onClick: () => this.toggleSelect(button.value)
25920
25918
  }, button.content || "" + button.value);
25921
25919
  });
@@ -25932,7 +25930,7 @@ ButtonGroup.defaultProps = {
25932
25930
  value: null,
25933
25931
  allowEmpty: true
25934
25932
  };
25935
- const styles$l = StyleSheet.create({
25933
+ const styles$k = StyleSheet.create({
25936
25934
  buttonStyle: {
25937
25935
  backgroundColor: "white",
25938
25936
  border: "1px solid #ccc",
@@ -26536,7 +26534,7 @@ var util = /*#__PURE__*/Object.freeze({
26536
26534
  });
26537
26535
 
26538
26536
  // @ts-expect-error - TS2339 - Property 'MovablePoint' does not exist on type 'typeof Graphie'.
26539
- const MovablePoint$2 = Graphie.MovablePoint;
26537
+ const MovablePoint$3 = Graphie.MovablePoint;
26540
26538
  // @ts-expect-error - TS2339 - Property 'MovableLine' does not exist on type 'typeof Graphie'.
26541
26539
  const MovableLine$2 = Graphie.MovableLine;
26542
26540
  function isFlipped(newCoord, oldCoord, line) {
@@ -26618,7 +26616,7 @@ class FunctionGrapher extends React.Component {
26618
26616
  }],
26619
26617
  normalStyle: dashed,
26620
26618
  highlightStyle: dashed
26621
- }, _.map(asymptote, (coord, i) => /*#__PURE__*/React.createElement(MovablePoint$2, {
26619
+ }, _.map(asymptote, (coord, i) => /*#__PURE__*/React.createElement(MovablePoint$3, {
26622
26620
  key: `asymptoteCoord-${i}`,
26623
26621
  coord: coord,
26624
26622
  static: true,
@@ -26633,7 +26631,7 @@ class FunctionGrapher extends React.Component {
26633
26631
  }
26634
26632
  render() {
26635
26633
  const pointForCoord = (coord, i) => {
26636
- return /*#__PURE__*/React.createElement(MovablePoint$2, {
26634
+ return /*#__PURE__*/React.createElement(MovablePoint$3, {
26637
26635
  key: i,
26638
26636
  coord: coord,
26639
26637
  static: this.props.static,
@@ -27442,7 +27440,7 @@ const Label = Graphie.Label;
27442
27440
  // @ts-expect-error - TS2339 - Property 'Line' does not exist on type 'typeof Graphie'.
27443
27441
  const Line$2 = Graphie.Line;
27444
27442
  // @ts-expect-error - TS2339 - Property 'MovablePoint' does not exist on type 'typeof Graphie'.
27445
- const MovablePoint$1 = Graphie.MovablePoint;
27443
+ const MovablePoint$2 = Graphie.MovablePoint;
27446
27444
  // @ts-expect-error - TS2339 - Property 'MovableLine' does not exist on type 'typeof Graphie'.
27447
27445
  const MovableLine$1 = Graphie.MovableLine;
27448
27446
  // @ts-expect-error - TS2339 - Property 'Plot' does not exist on type 'typeof Graphie'.
@@ -27691,7 +27689,7 @@ class Interaction extends React.Component {
27691
27689
  return [coordX, coordY];
27692
27690
  }];
27693
27691
  if (element.options.constraint === "snap") {
27694
- constraints.push(MovablePoint$1.constraints.snap(element.options.snap));
27692
+ constraints.push(MovablePoint$2.constraints.snap(element.options.snap));
27695
27693
  } else if (element.options.constraint === "x") {
27696
27694
  constraints.push(coord => {
27697
27695
  return [
@@ -27713,7 +27711,7 @@ class Interaction extends React.Component {
27713
27711
  // TODO(eater): foo_[xyz] are hacky non-props to
27714
27712
  // get the component to update when constraints
27715
27713
  // change
27716
- return /*#__PURE__*/React.createElement(MovablePoint$1, {
27714
+ return /*#__PURE__*/React.createElement(MovablePoint$2, {
27717
27715
  key: element.key,
27718
27716
  coord: [
27719
27717
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
@@ -27748,7 +27746,7 @@ class Interaction extends React.Component {
27748
27746
  return [coordX, coordY];
27749
27747
  }];
27750
27748
  if (element.options.constraint === "snap") {
27751
- constraints.push(MovablePoint$1.constraints.snap(element.options.snap));
27749
+ constraints.push(MovablePoint$2.constraints.snap(element.options.snap));
27752
27750
  } else if (element.options.constraint === "x") {
27753
27751
  constraints.push(coord => {
27754
27752
  return [
@@ -27789,14 +27787,14 @@ class Interaction extends React.Component {
27789
27787
  foo_x: element.options.constraint,
27790
27788
  foo_y: element.options.constraintFn,
27791
27789
  foo_z: element.options.snap
27792
- }, /*#__PURE__*/React.createElement(MovablePoint$1, {
27790
+ }, /*#__PURE__*/React.createElement(MovablePoint$2, {
27793
27791
  coord: start,
27794
27792
  static: true,
27795
27793
  normalStyle: {
27796
27794
  stroke: "none",
27797
27795
  fill: "none"
27798
27796
  }
27799
- }), /*#__PURE__*/React.createElement(MovablePoint$1, {
27797
+ }), /*#__PURE__*/React.createElement(MovablePoint$2, {
27800
27798
  coord: end,
27801
27799
  static: true,
27802
27800
  normalStyle: {
@@ -28353,7 +28351,7 @@ const defaultGraphConfig = {
28353
28351
  width: 0,
28354
28352
  height: 0,
28355
28353
  labels: [],
28356
- hintMode: false
28354
+ disableKeyboardInteraction: false
28357
28355
  };
28358
28356
  const GraphConfigContext = /*#__PURE__*/createContext(defaultGraphConfig);
28359
28357
  function useGraphConfig() {
@@ -28490,6 +28488,28 @@ const segmentsIntersect = ([[a, b], [c, d]], [[p, q], [r, s]]) => {
28490
28488
  }
28491
28489
  };
28492
28490
 
28491
+ // Finds the unique intersection point between two rays. Returns undefined if
28492
+ // there is not exactly one intersection point. The first point in each of the
28493
+ // given ray "segments" should be the initial point of the ray. The second point
28494
+ // can be any arbitrary point on the ray different from the initial point.
28495
+ function findIntersectionOfRays([[a, b], [c, d]], [[p, q], [r, s]]) {
28496
+ // See https://stackoverflow.com/a/24392281/7347484 for an explanation of
28497
+ // the determinant and lambda values.
28498
+ const determinant = (c - a) * (s - q) - (r - p) * (d - b);
28499
+ if (determinant === 0) {
28500
+ return undefined;
28501
+ } else {
28502
+ const lambda = ((s - q) * (r - a) + (p - r) * (s - b)) / determinant;
28503
+ const gamma = ((b - d) * (r - a) + (c - a) * (s - b)) / determinant;
28504
+ if (lambda <= 0 || gamma >= 1) {
28505
+ return undefined;
28506
+ }
28507
+ const initialPoint = [a, b];
28508
+ const direction = vec.sub([c, d], initialPoint);
28509
+ return vec.add(initialPoint, vec.scale(direction, lambda));
28510
+ }
28511
+ }
28512
+
28493
28513
  // This file contains helper functions for working with angles.
28494
28514
 
28495
28515
  function convertDegreesToRadians(degrees) {
@@ -28501,6 +28521,8 @@ function convertDegreesToRadians(degrees) {
28501
28521
  function calculateAngleInDegrees([x, y]) {
28502
28522
  return Math.atan2(y, x) * 180 / Math.PI;
28503
28523
  }
28524
+
28525
+ // Converts polar coordinates to cartesian. The th(eta) parameter is in degrees.
28504
28526
  function polar(r, th) {
28505
28527
  if (typeof r === "number") {
28506
28528
  r = [r, r];
@@ -29106,7 +29128,7 @@ const VisibleIcon = () => /*#__PURE__*/React.createElement("svg", {
29106
29128
  width: "24",
29107
29129
  height: "24",
29108
29130
  viewBox: "0 0 24 24",
29109
- className: css(styles$k.icon)
29131
+ className: css(styles$j.icon)
29110
29132
  }, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("path", {
29111
29133
  id: "a",
29112
29134
  d: "M7.401 10.035c-1.424.748-2.599 1.905-3.544 " + "3.48a1 1 0 0 1-1.714-1.03C4.325 8.849 7.652 7 " + "12 7c4.348 0 7.675 1.848 9.857 5.486a1 1 0 0 " + "1-1.714 1.028c-.945-1.574-2.12-2.73-3.544-" + "3.48a5 5 0 1 1-9.198 0zM12 15a3 3 0 1 0 0-6 3 3 " + "0 0 0 0 6z"
@@ -29137,7 +29159,7 @@ const HiddenIcon = () => /*#__PURE__*/React.createElement("svg", {
29137
29159
  width: "24",
29138
29160
  height: "24",
29139
29161
  viewBox: "0 0 24 24",
29140
- className: css(styles$k.icon)
29162
+ className: css(styles$j.icon)
29141
29163
  }, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("path", {
29142
29164
  id: "a",
29143
29165
  d: "M8.794 7.38C9.791 7.127 10.86 7 12 7c4.348 0 " + "7.675 1.848 9.857 5.486a1 1 0 0 1-1.714 " + "1.028c-.945-1.574-2.12-2.73-3.544-3.48.258." + "604.401 1.268.401 1.966 0 1.02-.305 " + "1.967-.828 2.757l2.535 2.536a1 1 0 0 " + "1-1.414 1.414l-12-12a1 1 0 0 1 " + "1.414-1.414L8.794 7.38zm5.914 5.913a3 3 0 0 " + "0-4.001-4.001l4 4.001zM6.072 8.486l2.976 " + "2.976a3 3 0 0 0 3.49 3.49l1.579 1.58A5 5 0 " + "0 1 7.4 10.035c-1.424.747-2.599 1.904-3.544 " + "3.478a1 1 0 0 1-1.714-1.028c1.049-1.75 " + "2.363-3.085 3.929-4z"
@@ -29171,20 +29193,20 @@ const HUD = ({
29171
29193
  let state;
29172
29194
  let icon;
29173
29195
  if (enabled) {
29174
- state = styles$k.enabled;
29196
+ state = styles$j.enabled;
29175
29197
  icon = /*#__PURE__*/React.createElement(VisibleIcon, null);
29176
29198
  } else {
29177
- state = styles$k.disabled;
29199
+ state = styles$j.disabled;
29178
29200
  icon = /*#__PURE__*/React.createElement(HiddenIcon, null);
29179
29201
  }
29180
29202
  return /*#__PURE__*/React.createElement("button", {
29181
- className: css(styles$k.hud, _fixedPosition && styles$k.hudFixedPosition, state),
29203
+ className: css(styles$j.hud, _fixedPosition && styles$j.hudFixedPosition, state),
29182
29204
  onClick: e => {
29183
29205
  onClick();
29184
29206
  }
29185
29207
  }, icon, message);
29186
29208
  };
29187
- const styles$k = StyleSheet.create({
29209
+ const styles$j = StyleSheet.create({
29188
29210
  hud: {
29189
29211
  boxSizing: "border-box",
29190
29212
  height: 36,
@@ -29234,19 +29256,19 @@ const styles$k = StyleSheet.create({
29234
29256
  }
29235
29257
  });
29236
29258
 
29237
- const InfoTip$1 = props => {
29259
+ const InfoTipBase = props => {
29238
29260
  return /*#__PURE__*/React.createElement(Tooltip$1, {
29239
29261
  content: /*#__PURE__*/React.createElement(Body, {
29240
- style: styles$j.body
29262
+ style: styles$i.body
29241
29263
  }, props.children),
29242
29264
  placement: "right"
29243
29265
  }, /*#__PURE__*/React.createElement(PhosphorIcon, {
29244
29266
  size: "small",
29245
29267
  icon: questionIcon,
29246
- style: styles$j.spacingLeft
29268
+ style: styles$i.spacingLeft
29247
29269
  }));
29248
29270
  };
29249
- const styles$j = StyleSheet.create({
29271
+ const styles$i = StyleSheet.create({
29250
29272
  spacingLeft: {
29251
29273
  marginInlineStart: spacing.xxxSmall_4
29252
29274
  },
@@ -29278,7 +29300,7 @@ class InfoTip extends React.Component {
29278
29300
 
29279
29301
  render() {
29280
29302
  if (this.state.didMount) {
29281
- return /*#__PURE__*/React.createElement(InfoTip$1, this.props);
29303
+ return /*#__PURE__*/React.createElement(InfoTipBase, this.props);
29282
29304
  }
29283
29305
  return /*#__PURE__*/React.createElement("div", null);
29284
29306
  }
@@ -29325,7 +29347,7 @@ class MultiButtonGroup extends React.Component {
29325
29347
  id: "" + i,
29326
29348
  key: "" + i,
29327
29349
  ref: "button" + i,
29328
- className: css(styles$i.buttonStyle, selected && styles$i.selectedStyle),
29350
+ className: css(styles$h.buttonStyle, selected && styles$h.selectedStyle),
29329
29351
  onClick: () => this.toggleSelect(button.value)
29330
29352
  }, button.content || "" + button.value);
29331
29353
  });
@@ -29341,7 +29363,7 @@ MultiButtonGroup.defaultProps = {
29341
29363
  values: [],
29342
29364
  allowEmpty: true
29343
29365
  };
29344
- const styles$i = StyleSheet.create({
29366
+ const styles$h = StyleSheet.create({
29345
29367
  buttonStyle: {
29346
29368
  backgroundColor: "white",
29347
29369
  border: "1px solid #ccc",
@@ -29566,62 +29588,6 @@ NumberInput.defaultProps = {
29566
29588
  useArrowKeys: false
29567
29589
  };
29568
29590
 
29569
- /* eslint-disable @babel/no-invalid-this */
29570
-
29571
- /* A checkbox that syncs its value to props using the
29572
- * renderer's onChange method, and gets the prop name
29573
- * dynamically from its props list
29574
- */
29575
- const PropCheckBox = createReactClass({
29576
- displayName: "PropCheckBox",
29577
- propTypes: {
29578
- labelAlignment: PropTypes.oneOf(["left", "right"])
29579
- },
29580
- DEFAULT_PROPS: {
29581
- label: null,
29582
- onChange: null,
29583
- labelAlignment: "left"
29584
- },
29585
- getDefaultProps: function () {
29586
- return this.DEFAULT_PROPS;
29587
- },
29588
- propName: function () {
29589
- const propName = _.find(_.keys(this.props), function (localPropName) {
29590
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
29591
- return !_.has(this.DEFAULT_PROPS, localPropName);
29592
- }, this);
29593
- if (!propName) {
29594
- throw new PerseusError("Attempted to create a PropCheckBox with no prop!", Errors.InvalidInput);
29595
- }
29596
- return propName;
29597
- },
29598
- _labelAlignLeft: function () {
29599
- return this.props.labelAlignment === "left";
29600
- },
29601
- render: function () {
29602
- const propName = this.propName();
29603
- return /*#__PURE__*/React.createElement("label", {
29604
- className: css(styles$h.labeledCheckbox)
29605
- }, this._labelAlignLeft() && /*#__PURE__*/React.createElement(LabelSmall, null, this.props.label), /*#__PURE__*/React.createElement(Checkbox, {
29606
- checked: this.props[propName],
29607
- onChange: this.toggle
29608
- }), !this._labelAlignLeft() && /*#__PURE__*/React.createElement(LabelSmall, null, this.props.label));
29609
- },
29610
- toggle: function () {
29611
- const propName = this.propName();
29612
- const changes = {};
29613
- changes[propName] = !this.props[propName];
29614
- this.props.onChange(changes);
29615
- }
29616
- });
29617
- const styles$h = StyleSheet.create({
29618
- labeledCheckbox: {
29619
- display: "flex",
29620
- flexDirection: "row",
29621
- alignItems: "center"
29622
- }
29623
- });
29624
-
29625
29591
  const truth = () => true;
29626
29592
 
29627
29593
  /* A minor abstraction on top of NumberInput for ranges
@@ -29683,7 +29649,6 @@ var components = /*#__PURE__*/Object.freeze({
29683
29649
  MathInput: MathInput,
29684
29650
  MultiButtonGroup: MultiButtonGroup,
29685
29651
  NumberInput: NumberInput,
29686
- PropCheckBox: PropCheckBox,
29687
29652
  RangeInput: RangeInput,
29688
29653
  SvgImage: SvgImage,
29689
29654
  TextInput: TextInput,
@@ -29837,8 +29802,9 @@ const lockedFigureColors = {
29837
29802
  };
29838
29803
  const lockedFigureFillStyles = {
29839
29804
  none: 0,
29840
- solid: 1,
29841
- translucent: 0.4
29805
+ white: 1,
29806
+ translucent: 0.4,
29807
+ solid: 1
29842
29808
  };
29843
29809
  const plotterPlotTypes = ["bar", "line", "pic", "histogram", "dotplot"];
29844
29810
 
@@ -29847,7 +29813,7 @@ const LockedEllipse = props => {
29847
29813
  center,
29848
29814
  radius,
29849
29815
  angle,
29850
- color,
29816
+ color: color$1,
29851
29817
  fillStyle,
29852
29818
  strokeStyle
29853
29819
  } = props;
@@ -29857,11 +29823,21 @@ const LockedEllipse = props => {
29857
29823
  angle: angle,
29858
29824
  fillOpacity: lockedFigureFillStyles[fillStyle],
29859
29825
  strokeStyle: strokeStyle,
29860
- color: lockedFigureColors[color]
29826
+ color: lockedFigureColors[color$1]
29827
+ // We need to override the svg props if we want to have a
29828
+ // different fill color than the stroke color (specifically,
29829
+ // in the case where the fillStyle is "white").
29830
+ ,
29831
+ svgEllipseProps: {
29832
+ style: {
29833
+ fill: fillStyle === "white" ? color.white : lockedFigureColors[color$1]
29834
+ }
29835
+ }
29861
29836
  });
29862
29837
  };
29863
29838
 
29864
29839
  const LockedFunction = props => {
29840
+ const [equation, setEquation] = useState();
29865
29841
  const {
29866
29842
  color,
29867
29843
  strokeStyle,
@@ -29873,7 +29849,14 @@ const LockedFunction = props => {
29873
29849
  style: strokeStyle,
29874
29850
  domain
29875
29851
  };
29876
- const equation = props.equationParsed || KAS.parse(props.equation).expr;
29852
+ useEffect(() => {
29853
+ // Parsing the equation in a "useEffect" hook saves about 2ms each frame
29854
+ // when the learner is interacting with the graph (i.e. moving points).
29855
+ setEquation(KAS.parse(props.equation).expr);
29856
+ }, [props.equation]);
29857
+ if (typeof equation === "undefined") {
29858
+ return null;
29859
+ }
29877
29860
  return /*#__PURE__*/React.createElement("g", {
29878
29861
  className: "locked-function"
29879
29862
  }, directionalAxis === "x" && /*#__PURE__*/React.createElement(Plot$3.OfX, _extends({
@@ -30064,7 +30047,7 @@ const LockedPoint = props => {
30064
30047
  const LockedPolygon = props => {
30065
30048
  const {
30066
30049
  points,
30067
- color,
30050
+ color: color$1,
30068
30051
  showVertices,
30069
30052
  fillStyle,
30070
30053
  strokeStyle
@@ -30075,12 +30058,21 @@ const LockedPolygon = props => {
30075
30058
  points: [...points],
30076
30059
  fillOpacity: lockedFigureFillStyles[fillStyle],
30077
30060
  strokeStyle: strokeStyle,
30078
- color: lockedFigureColors[color]
30061
+ color: lockedFigureColors[color$1]
30062
+ // We need to override the svg props if we want to have a
30063
+ // different fill color than the stroke color (specifically,
30064
+ // in the case where the fillStyle is "white").
30065
+ ,
30066
+ svgPolygonProps: {
30067
+ style: {
30068
+ fill: fillStyle === "white" ? color.white : lockedFigureColors[color$1]
30069
+ }
30070
+ }
30079
30071
  }), showVertices && points.map((point, index) => /*#__PURE__*/React.createElement(Point$2, {
30080
30072
  key: `locked-polygon-point-${index}`,
30081
30073
  x: point[X],
30082
30074
  y: point[Y],
30083
- color: lockedFigureColors[color]
30075
+ color: lockedFigureColors[color$1]
30084
30076
  })));
30085
30077
  };
30086
30078
 
@@ -30321,7 +30313,7 @@ function useDraggable(args) {
30321
30313
  gestureTarget: target,
30322
30314
  onMove,
30323
30315
  point,
30324
- constrain
30316
+ constrainKeyboardMovement
30325
30317
  } = args;
30326
30318
  const [dragging, setDragging] = React.useState(false);
30327
30319
  const {
@@ -30344,6 +30336,7 @@ function useDraggable(args) {
30344
30336
  event == null || event.stopPropagation();
30345
30337
  const isKeyboard = type.includes("key");
30346
30338
  if (isKeyboard) {
30339
+ invariant(event instanceof KeyboardEvent);
30347
30340
  event == null || event.preventDefault();
30348
30341
 
30349
30342
  // When a key is held down, we see multiple "keydown" events,
@@ -30355,6 +30348,11 @@ function useDraggable(args) {
30355
30348
  if (type === "keyup") {
30356
30349
  return;
30357
30350
  }
30351
+ if (typeof constrainKeyboardMovement === "object") {
30352
+ const destination = constrainKeyboardMovement[directionForKey[event.key]];
30353
+ onMove(destination != null ? destination : point);
30354
+ return;
30355
+ }
30358
30356
  const {
30359
30357
  direction: yDownDirection,
30360
30358
  altKey,
@@ -30375,7 +30373,7 @@ function useDraggable(args) {
30375
30373
  for (const dx of tests) {
30376
30374
  // Transform the test back into the point's coordinate system
30377
30375
  const testMovement = vec.scale(direction, dx);
30378
- const testPoint = constrain(vec.transform(vec.add(vec.transform(point, userTransform), testMovement), inverseTransform));
30376
+ const testPoint = constrainKeyboardMovement(vec.transform(vec.add(vec.transform(point, userTransform), testMovement), inverseTransform));
30379
30377
  if (vec.dist(testPoint, point) > min) {
30380
30378
  onMove(testPoint);
30381
30379
  break;
@@ -30395,7 +30393,7 @@ function useDraggable(args) {
30395
30393
  return;
30396
30394
  }
30397
30395
  const movement = vec.transform(pixelMovement, inverseViewTransform);
30398
- onMove(constrain(vec.transform(vec.add(pickup.current, movement), inverseTransform)));
30396
+ onMove(vec.transform(vec.add(pickup.current, movement), inverseTransform));
30399
30397
  }
30400
30398
  }, {
30401
30399
  target,
@@ -30407,6 +30405,12 @@ function useDraggable(args) {
30407
30405
  dragging
30408
30406
  };
30409
30407
  }
30408
+ const directionForKey = {
30409
+ ArrowLeft: "left",
30410
+ ArrowRight: "right",
30411
+ ArrowUp: "up",
30412
+ ArrowDown: "down"
30413
+ };
30410
30414
  function getInverseTransform(transform) {
30411
30415
  const invert = vec.matrixInvert(transform);
30412
30416
  invariant(invert !== null, "Could not invert transform matrix. A parent transformation matrix might be degenerative (mapping 2D space to a line).");
@@ -30456,7 +30460,7 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
30456
30460
  range,
30457
30461
  markings,
30458
30462
  showTooltips,
30459
- hintMode
30463
+ disableKeyboardInteraction
30460
30464
  } = useGraphConfig();
30461
30465
  const {
30462
30466
  point,
@@ -30498,7 +30502,7 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
30498
30502
  cursor
30499
30503
  },
30500
30504
  "data-testid": "movable-point",
30501
- tabIndex: hintMode ? -1 : tabIndex(focusBehavior)
30505
+ tabIndex: disableKeyboardInteraction ? -1 : tabIndex(focusBehavior)
30502
30506
  }, /*#__PURE__*/React.createElement("circle", {
30503
30507
  className: "movable-point-hitbox",
30504
30508
  r: hitboxSizePx / 2,
@@ -30587,7 +30591,7 @@ const MovableLine = props => {
30587
30591
  function useControlPoint(point, color, onMovePoint) {
30588
30592
  const {
30589
30593
  snapStep,
30590
- hintMode
30594
+ disableKeyboardInteraction
30591
30595
  } = useGraphConfig();
30592
30596
  const [focused, setFocused] = useState(false);
30593
30597
  const keyboardHandleRef = useRef(null);
@@ -30595,7 +30599,7 @@ function useControlPoint(point, color, onMovePoint) {
30595
30599
  gestureTarget: keyboardHandleRef,
30596
30600
  point,
30597
30601
  onMove: onMovePoint,
30598
- constrain: p => snap(snapStep, p)
30602
+ constrainKeyboardMovement: p => snap(snapStep, p)
30599
30603
  });
30600
30604
  const visiblePointRef = useRef(null);
30601
30605
  const {
@@ -30604,12 +30608,12 @@ function useControlPoint(point, color, onMovePoint) {
30604
30608
  gestureTarget: visiblePointRef,
30605
30609
  point,
30606
30610
  onMove: onMovePoint,
30607
- constrain: p => snap(snapStep, p)
30611
+ constrainKeyboardMovement: p => snap(snapStep, p)
30608
30612
  });
30609
30613
  const focusableHandle = /*#__PURE__*/React.createElement("g", {
30610
30614
  "data-testid": "movable-point__focusable-handle",
30611
30615
  className: "movable-point__focusable-handle",
30612
- tabIndex: hintMode ? -1 : 0,
30616
+ tabIndex: disableKeyboardInteraction ? -1 : 0,
30613
30617
  ref: keyboardHandleRef,
30614
30618
  onFocus: () => setFocused(true),
30615
30619
  onBlur: () => setFocused(false)
@@ -30643,7 +30647,7 @@ const Line$1 = props => {
30643
30647
  range,
30644
30648
  graphDimensionsInPixels,
30645
30649
  snapStep,
30646
- hintMode
30650
+ disableKeyboardInteraction
30647
30651
  } = useGraphConfig();
30648
30652
  let startExtend = undefined;
30649
30653
  let endExtend = undefined;
@@ -30661,11 +30665,11 @@ const Line$1 = props => {
30661
30665
  onMove: newPoint => {
30662
30666
  onMove(vec.sub(newPoint, start));
30663
30667
  },
30664
- constrain: p => snap(snapStep, p)
30668
+ constrainKeyboardMovement: p => snap(snapStep, p)
30665
30669
  });
30666
30670
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("g", {
30667
30671
  ref: line,
30668
- tabIndex: hintMode ? -1 : 0,
30672
+ tabIndex: disableKeyboardInteraction ? -1 : 0,
30669
30673
  className: "movable-line",
30670
30674
  "data-testid": "movable-line",
30671
30675
  style: {
@@ -31128,7 +31132,7 @@ function calculateBisectorPoint(point1, point2, vertex, isReflex, allowReflex, a
31128
31132
  return scaledBisector;
31129
31133
  }
31130
31134
 
31131
- const StyledMovablePoint = props => {
31135
+ const MovablePoint$1 = props => {
31132
31136
  const {
31133
31137
  snapStep
31134
31138
  } = useGraphConfig();
@@ -31138,16 +31142,15 @@ const StyledMovablePoint = props => {
31138
31142
  onMove,
31139
31143
  cursor,
31140
31144
  color: color$1 = color.blue,
31141
- snapTo
31145
+ constrain = p => snap(snapStep, p)
31142
31146
  } = props;
31143
- const snapToValue = snapTo != null ? snapTo : "grid";
31144
31147
  const {
31145
31148
  dragging
31146
31149
  } = useDraggable({
31147
31150
  gestureTarget: elementRef,
31148
31151
  point,
31149
31152
  onMove,
31150
- constrain: p => ["angles", "sides"].includes(snapToValue) ? p : snap(snapStep, p)
31153
+ constrainKeyboardMovement: constrain
31151
31154
  });
31152
31155
  return /*#__PURE__*/React.createElement(MovablePointView, {
31153
31156
  ref: elementRef,
@@ -31163,8 +31166,10 @@ const StyledMovablePoint = props => {
31163
31166
  };
31164
31167
 
31165
31168
  const PolygonGraph = props => {
31166
- const [focused, setFocused] = React.useState(false);
31167
31169
  const [hovered, setHovered] = React.useState(false);
31170
+ // This is more so required for the re-rendering that occurs when state
31171
+ // updates; specifically with regard to line weighting and polygon focus.
31172
+ const [focusVisible, setFocusVisible] = React.useState(false);
31168
31173
  const {
31169
31174
  dispatch
31170
31175
  } = props;
@@ -31174,10 +31179,10 @@ const PolygonGraph = props => {
31174
31179
  showSides,
31175
31180
  range,
31176
31181
  snapStep,
31177
- snapTo
31182
+ snapTo = "grid"
31178
31183
  } = props.graphState;
31179
31184
  const {
31180
- hintMode
31185
+ disableKeyboardInteraction
31181
31186
  } = useGraphConfig();
31182
31187
 
31183
31188
  // TODO(benchristel): can the default set of points be removed here? I don't
@@ -31185,7 +31190,7 @@ const PolygonGraph = props => {
31185
31190
  const points = coords != null ? coords : [[0, 0]];
31186
31191
  const ref = React.useRef(null);
31187
31192
  const dragReferencePoint = points[0];
31188
- const snapToValue = snapTo != null ? snapTo : "grid";
31193
+ const constrain = ["angles", "sides"].includes(snapTo) ? p => p : p => snap(snapStep, p);
31189
31194
  const {
31190
31195
  dragging
31191
31196
  } = useDraggable({
@@ -31195,16 +31200,15 @@ const PolygonGraph = props => {
31195
31200
  const delta = vec.sub(newPoint, dragReferencePoint);
31196
31201
  dispatch(actions.polygon.moveAll(delta));
31197
31202
  },
31198
- constrain: p => ["angles", "sides"].includes(snapToValue) ? p : snap(snapStep, p)
31203
+ constrainKeyboardMovement: constrain
31199
31204
  });
31200
- const active = hovered || focused || dragging;
31201
31205
  const lastMoveTime = React.useRef(0);
31202
31206
  const lines = getLines(points);
31203
31207
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Polygon, {
31204
31208
  points: [...points],
31205
31209
  color: "var(--movable-line-stroke-color)",
31206
31210
  svgPolygonProps: {
31207
- strokeWidth: active ? "var(--movable-line-stroke-weight-active)" : "var(--movable-line-stroke-weight)",
31211
+ strokeWidth: focusVisible ? "var(--movable-line-stroke-weight-active)" : "var(--movable-line-stroke-weight)",
31208
31212
  style: {
31209
31213
  fill: "transparent"
31210
31214
  }
@@ -31219,11 +31223,10 @@ const PolygonGraph = props => {
31219
31223
  key: "angle-" + i,
31220
31224
  centerPoint: point,
31221
31225
  endPoints: [pt1, pt2],
31222
- active: active,
31223
31226
  range: range,
31224
31227
  polygonLines: lines,
31225
31228
  showAngles: !!showAngles,
31226
- snapTo: snapToValue
31229
+ snapTo: snapTo
31227
31230
  });
31228
31231
  }), showSides && lines.map(([start, end], i) => {
31229
31232
  const [x, y] = vec.midpoint(start, end);
@@ -31238,21 +31241,29 @@ const PolygonGraph = props => {
31238
31241
  color: "transparent",
31239
31242
  svgPolygonProps: {
31240
31243
  ref,
31241
- tabIndex: hintMode ? -1 : 0,
31244
+ tabIndex: disableKeyboardInteraction ? -1 : 0,
31242
31245
  strokeWidth: TARGET_SIZE,
31243
31246
  style: {
31244
31247
  cursor: dragging ? "grabbing" : "grab",
31245
31248
  fill: hovered ? "var(--mafs-blue)" : "transparent"
31246
31249
  },
31247
- onFocus: () => setFocused(true),
31248
- onBlur: () => setFocused(false),
31249
31250
  onMouseEnter: () => setHovered(true),
31250
31251
  onMouseLeave: () => setHovered(false),
31252
+ // Required to remove line weighting when user clicks away
31253
+ // from the focused polygon
31254
+ onKeyDownCapture: () => {
31255
+ setFocusVisible(hasFocusVisible(ref.current));
31256
+ },
31257
+ // Required for lines to darken on focus
31258
+ onFocus: () => setFocusVisible(hasFocusVisible(ref.current)),
31259
+ // Required for line weighting to update on blur. Without this,
31260
+ // the user has to hover over the shape for it to update
31261
+ onBlur: () => setFocusVisible(hasFocusVisible(ref.current)),
31251
31262
  className: "movable-polygon"
31252
31263
  }
31253
- }), points.map((point, i) => /*#__PURE__*/React.createElement(StyledMovablePoint, {
31264
+ }), points.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
31254
31265
  key: "point-" + i,
31255
- snapTo: snapTo,
31266
+ constrain: constrain,
31256
31267
  point: point,
31257
31268
  onMove: destination => {
31258
31269
  const now = Date.now();
@@ -31271,6 +31282,20 @@ function getLines(points) {
31271
31282
  return [point, next];
31272
31283
  });
31273
31284
  }
31285
+ const hasFocusVisible = element => {
31286
+ const matches = selector => {
31287
+ var _element$matches;
31288
+ return (_element$matches = element == null ? void 0 : element.matches(selector)) != null ? _element$matches : false;
31289
+ };
31290
+ try {
31291
+ return matches(":focus-visible");
31292
+ } catch (e) {
31293
+ // jsdom doesn't support :focus-visible
31294
+ // (see https://github.com/jsdom/jsdom/issues/3426),
31295
+ // so the call to matches(":focus-visible") will fail in tests.
31296
+ return matches(":focus");
31297
+ }
31298
+ };
31274
31299
 
31275
31300
  function getGradableGraph(state, initialGraph) {
31276
31301
  if (!state.hasBeenInteractedWith) {
@@ -31364,7 +31389,7 @@ function CircleGraph(props) {
31364
31389
  center: center,
31365
31390
  radius: getRadius(graphState),
31366
31391
  onMove: c => dispatch(actions.circle.moveCenter(c))
31367
- }), /*#__PURE__*/React.createElement(StyledMovablePoint, {
31392
+ }), /*#__PURE__*/React.createElement(MovablePoint$1, {
31368
31393
  point: radiusPoint,
31369
31394
  cursor: "ew-resize",
31370
31395
  onMove: newRadiusPoint => {
@@ -31380,7 +31405,7 @@ function MovableCircle(props) {
31380
31405
  } = props;
31381
31406
  const {
31382
31407
  snapStep,
31383
- hintMode
31408
+ disableKeyboardInteraction
31384
31409
  } = useGraphConfig();
31385
31410
  const draggableRef = useRef(null);
31386
31411
  const {
@@ -31389,13 +31414,13 @@ function MovableCircle(props) {
31389
31414
  gestureTarget: draggableRef,
31390
31415
  point: center,
31391
31416
  onMove,
31392
- constrain: p => snap(snapStep, p)
31417
+ constrainKeyboardMovement: p => snap(snapStep, p)
31393
31418
  });
31394
31419
  const [centerPx] = useTransformVectorsToPixels(center);
31395
31420
  const [radiiPx] = useTransformDimensionsToPixels([radius, radius]);
31396
31421
  return /*#__PURE__*/React.createElement("g", {
31397
31422
  ref: draggableRef,
31398
- tabIndex: hintMode ? -1 : 0,
31423
+ tabIndex: disableKeyboardInteraction ? -1 : 0,
31399
31424
  className: `movable-circle ${dragging ? "movable-circle--dragging" : ""}`
31400
31425
  }, /*#__PURE__*/React.createElement("ellipse", {
31401
31426
  className: "focus-ring",
@@ -31476,7 +31501,7 @@ function QuadraticGraph(props) {
31476
31501
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Plot$3.OfX, {
31477
31502
  y: y,
31478
31503
  color: color.blue
31479
- }), coords.map((coord, i) => /*#__PURE__*/React.createElement(StyledMovablePoint, {
31504
+ }), coords.map((coord, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
31480
31505
  key: "point-" + i,
31481
31506
  point: coord,
31482
31507
  onMove: destination => dispatch(actions.quadratic.movePoint(i, destination))
@@ -31536,7 +31561,7 @@ function SinusoidGraph(props) {
31536
31561
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Plot$3.OfX, {
31537
31562
  y: x => computeSine(x, coeffRef.current),
31538
31563
  color: color.blue
31539
- }), coords.map((coord, i) => /*#__PURE__*/React.createElement(StyledMovablePoint, {
31564
+ }), coords.map((coord, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
31540
31565
  key: "point-" + i,
31541
31566
  point: coord,
31542
31567
  onMove: destination => dispatch(actions.sinusoid.movePoint(i, destination))
@@ -31592,7 +31617,6 @@ function AngleGraph(props) {
31592
31617
  showAngles,
31593
31618
  range,
31594
31619
  allowReflexAngles,
31595
- angleOffsetDeg,
31596
31620
  snapDegrees
31597
31621
  } = graphState;
31598
31622
 
@@ -31630,8 +31654,6 @@ function AngleGraph(props) {
31630
31654
  coords: endPoints,
31631
31655
  allowReflexAngles: allowReflexAngles || false,
31632
31656
  // Whether to allow reflex angles or not
31633
- angleOffsetDeg: angleOffsetDeg || 0,
31634
- // The angle offset from the x-axis
31635
31657
  snapDegrees: snapDegrees || 1,
31636
31658
  // The multiple of degrees to snap to
31637
31659
  range: range,
@@ -31639,19 +31661,66 @@ function AngleGraph(props) {
31639
31661
  };
31640
31662
 
31641
31663
  // Render the lines, angle, and then movable points
31642
- return /*#__PURE__*/React.createElement(React.Fragment, null, svgLines, /*#__PURE__*/React.createElement(Angle, angleParams), coords.map((point, i) => /*#__PURE__*/React.createElement(StyledMovablePoint, {
31643
- key: "point-" + i,
31644
- snapTo: "angles",
31645
- point: point,
31646
- onMove: destination => dispatch(actions.angle.movePoint(i, destination))
31647
- })));
31664
+ return /*#__PURE__*/React.createElement(React.Fragment, null, svgLines, /*#__PURE__*/React.createElement(Angle, angleParams), /*#__PURE__*/React.createElement(MovablePoint$1, {
31665
+ point: coords[1],
31666
+ constrain: p => p,
31667
+ onMove: destination => dispatch(actions.angle.movePoint(1, destination))
31668
+ }), /*#__PURE__*/React.createElement(MovablePoint$1, {
31669
+ point: coords[0],
31670
+ constrain: getAngleSideConstraint(coords[0], coords[1], snapDegrees || 1),
31671
+ onMove: destination => dispatch(actions.angle.movePoint(0, destination))
31672
+ }), /*#__PURE__*/React.createElement(MovablePoint$1, {
31673
+ point: coords[2],
31674
+ constrain: getAngleSideConstraint(coords[2], coords[1], snapDegrees || 1),
31675
+ onMove: destination => dispatch(actions.angle.movePoint(2, destination))
31676
+ }));
31677
+ }
31678
+ const positiveX = [1, 0];
31679
+ const negativeX = [-1, 0];
31680
+ const positiveY = [0, 1];
31681
+ const negativeY = [0, -1];
31682
+ function getAngleSideConstraint(sidePoint, vertex, snapDegrees) {
31683
+ var _findIntersectionOfRa, _findIntersectionOfRa2, _findIntersectionOfRa3, _findIntersectionOfRa4;
31684
+ const currentAngle = calculateAngleInDegrees(vec.sub(sidePoint, vertex));
31685
+
31686
+ // Find the rays that start at the current point and point up, down, left
31687
+ // and right.
31688
+ const leftRay = [sidePoint, vec.add(sidePoint, negativeX)];
31689
+ const rightRay = [sidePoint, vec.add(sidePoint, positiveX)];
31690
+ const upRay = [sidePoint, vec.add(sidePoint, positiveY)];
31691
+ const downRay = [sidePoint, vec.add(sidePoint, negativeY)];
31692
+
31693
+ // find the angles that lie one snap step clockwise and counter-clockwise
31694
+ // from the current angle. These are the angles to which the side can be
31695
+ // moved.
31696
+ const oneStepCounterClockwise = currentAngle + snapDegrees;
31697
+ const oneStepClockwise = currentAngle - snapDegrees;
31698
+
31699
+ // find the rays that start from the vertex and point in the direction of
31700
+ // the angles we just computed.
31701
+ const counterClockwiseRay = [vertex, vec.add(vertex, polar(1, oneStepCounterClockwise))];
31702
+ const clockwiseRay = [vertex, vec.add(vertex, polar(1, oneStepClockwise))];
31703
+
31704
+ // find the intersections of those rays with the horizontal and vertical
31705
+ // rays extending from the sidePoint. These intersections are the points to
31706
+ // which sidePoint can move that will result in a rotation of `snapDegrees`.
31707
+ const left = (_findIntersectionOfRa = findIntersectionOfRays(leftRay, counterClockwiseRay)) != null ? _findIntersectionOfRa : findIntersectionOfRays(leftRay, clockwiseRay);
31708
+ const right = (_findIntersectionOfRa2 = findIntersectionOfRays(rightRay, counterClockwiseRay)) != null ? _findIntersectionOfRa2 : findIntersectionOfRays(rightRay, clockwiseRay);
31709
+ const up = (_findIntersectionOfRa3 = findIntersectionOfRays(upRay, counterClockwiseRay)) != null ? _findIntersectionOfRa3 : findIntersectionOfRays(upRay, clockwiseRay);
31710
+ const down = (_findIntersectionOfRa4 = findIntersectionOfRays(downRay, counterClockwiseRay)) != null ? _findIntersectionOfRa4 : findIntersectionOfRays(downRay, clockwiseRay);
31711
+ return {
31712
+ up: up != null ? up : sidePoint,
31713
+ down: down != null ? down : sidePoint,
31714
+ left: left != null ? left : sidePoint,
31715
+ right: right != null ? right : sidePoint
31716
+ };
31648
31717
  }
31649
31718
 
31650
31719
  function PointGraph(props) {
31651
31720
  const {
31652
31721
  dispatch
31653
31722
  } = props;
31654
- return /*#__PURE__*/React.createElement(React.Fragment, null, props.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(StyledMovablePoint, {
31723
+ return /*#__PURE__*/React.createElement(React.Fragment, null, props.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
31655
31724
  key: i,
31656
31725
  point: point,
31657
31726
  onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination))
@@ -31683,7 +31752,7 @@ function Protractor() {
31683
31752
  gestureTarget: draggableRef,
31684
31753
  onMove: setCenter,
31685
31754
  point: center,
31686
- constrain: point => bound$1({
31755
+ constrainKeyboardMovement: point => bound$1({
31687
31756
  snapStep,
31688
31757
  range,
31689
31758
  point
@@ -31799,7 +31868,8 @@ const MafsGraph = props => {
31799
31868
  state,
31800
31869
  dispatch,
31801
31870
  labels,
31802
- hintMode
31871
+ hintMode,
31872
+ readOnly
31803
31873
  } = props;
31804
31874
  const [width, height] = props.box;
31805
31875
  const tickStep = props.step;
@@ -31815,7 +31885,7 @@ const MafsGraph = props => {
31815
31885
  width,
31816
31886
  height,
31817
31887
  labels,
31818
- hintMode
31888
+ disableKeyboardInteraction: hintMode || readOnly
31819
31889
  }
31820
31890
  }, /*#__PURE__*/React.createElement(View, {
31821
31891
  className: "mafs-graph",
@@ -32173,6 +32243,9 @@ const getAngleCoords = params => {
32173
32243
  if (graph.coords) {
32174
32244
  return graph.coords;
32175
32245
  }
32246
+ if (graph.startCoords) {
32247
+ return graph.startCoords;
32248
+ }
32176
32249
  const {
32177
32250
  snapDegrees,
32178
32251
  angleOffsetDeg
@@ -32202,6 +32275,7 @@ const getAngleCoords = params => {
32202
32275
  return coords;
32203
32276
  };
32204
32277
 
32278
+ const minDistanceBetweenAngleVertexAndSidePoint = 2;
32205
32279
  function interactiveGraphReducer(state, action) {
32206
32280
  switch (action.type) {
32207
32281
  case REINITIALIZE:
@@ -32367,21 +32441,28 @@ function doMovePoint(state, action) {
32367
32441
  case "angle":
32368
32442
  // If the index is 1, we are moving the vertex of the angle,
32369
32443
  // which will move the other two points as well
32370
- if (action.index === 1) {
32371
- const updatedCoords = boundAndSnapAngleVertex(state, action);
32444
+ const newState = (() => {
32445
+ if (action.index === 1) {
32446
+ const updatedCoords = boundAndSnapAngleVertex(state, action);
32447
+ return _extends({}, state, {
32448
+ hasBeenInteractedWith: true,
32449
+ coords: updatedCoords
32450
+ });
32451
+ }
32372
32452
  return _extends({}, state, {
32373
32453
  hasBeenInteractedWith: true,
32374
- coords: updatedCoords
32454
+ coords: setAtIndex({
32455
+ array: state.coords,
32456
+ index: action.index,
32457
+ newValue: boundAndSnapAngleEndPoints(action.destination, state, action.index)
32458
+ })
32375
32459
  });
32460
+ })();
32461
+ if (angleSidePointsTooCloseToVertex(newState)) {
32462
+ // cancel the move
32463
+ return state;
32376
32464
  }
32377
- return _extends({}, state, {
32378
- hasBeenInteractedWith: true,
32379
- coords: setAtIndex({
32380
- array: state.coords,
32381
- index: action.index,
32382
- newValue: boundAndSnapAngleEndPoints(action.destination, state, action.index)
32383
- })
32384
- });
32465
+ return newState;
32385
32466
  case "polygon":
32386
32467
  let newValue;
32387
32468
  if (state.snapTo === "sides") {
@@ -32470,11 +32551,7 @@ function doMoveCenter(state, action) {
32470
32551
  case "circle":
32471
32552
  {
32472
32553
  // Constrain the center of the circle to the chart range
32473
- const constrainedCenter = bound$1({
32474
- snapStep: state.snapStep,
32475
- range: state.range,
32476
- point: action.destination
32477
- });
32554
+ const constrainedCenter = boundAndSnapToGrid(action.destination, state);
32478
32555
 
32479
32556
  // Reposition the radius point based on the new center
32480
32557
  // (spread to make sure we're not going to mutate anything)
@@ -32508,10 +32585,10 @@ function doMoveRadiusPoint(state, action) {
32508
32585
  case "circle":
32509
32586
  {
32510
32587
  const [xMin, xMax] = state.range[X];
32511
- const nextRadiusPoint = [
32588
+ const nextRadiusPoint = snap(state.snapStep, [
32512
32589
  // Constrain to graph range
32513
32590
  // The +0 is to convert -0 to +0
32514
- clamp(action.destination[X] + 0, xMin, xMax), state.center[1]];
32591
+ clamp(action.destination[X] + 0, xMin, xMax), state.center[1]]);
32515
32592
  if (_.isEqual(nextRadiusPoint, state.center)) {
32516
32593
  return state;
32517
32594
  }
@@ -32593,17 +32670,16 @@ function boundAndSnapAngleVertex({
32593
32670
 
32594
32671
  // Get the current and upcoming positions of the vertex
32595
32672
  const startingVertex = coordsCopy[1];
32596
- const newVertex = bound$1({
32597
- snapStep,
32598
- range,
32599
- point: snap(snapStep, destination)
32600
- });
32673
+
32674
+ // Prevent the vertex from getting too close to the edge, so that all
32675
+ // points of the angle can fit within the graph bounds.
32676
+ const insetAmount = vec.add(snapStep, [minDistanceBetweenAngleVertexAndSidePoint, minDistanceBetweenAngleVertexAndSidePoint]);
32677
+ const newVertex = clampToBox(inset(insetAmount, range), snap(snapStep, destination));
32601
32678
 
32602
32679
  // Get the vector from the starting vertex to the new vertex
32603
32680
  const delta = vec.add(newVertex, reverseVector(startingVertex));
32604
32681
 
32605
32682
  // Apply the delta to each of the other two points so that the angle is maintained
32606
- let valid = true;
32607
32683
  const newPoints = {};
32608
32684
  for (const i of [0, 2]) {
32609
32685
  const oldPoint = coordsCopy[i];
@@ -32612,21 +32688,13 @@ function boundAndSnapAngleVertex({
32612
32688
  angle *= Math.PI / 180;
32613
32689
  newPoint = constrainToBoundsOnAngle(newPoint, angle, range, snapStep);
32614
32690
  newPoints[i] = newPoint;
32615
-
32616
- // Check if the new point is too close to the vertex
32617
- if (tooClose(newVertex, newPoint)) {
32618
- valid = false;
32619
- }
32620
32691
  }
32621
32692
 
32622
32693
  // Update the vertex after snapping to the snapStep
32623
32694
  newPoints[1] = newVertex;
32624
- // Only move points if all new positions are valid
32625
- if (valid) {
32626
- Object.entries(newPoints).forEach(([i, newPoint]) => {
32627
- coordsCopy[i] = newPoint;
32628
- });
32629
- }
32695
+ Object.entries(newPoints).forEach(([i, newPoint]) => {
32696
+ coordsCopy[i] = newPoint;
32697
+ });
32630
32698
  return coordsCopy;
32631
32699
  }
32632
32700
 
@@ -32691,10 +32759,17 @@ function boundAndSnapAngleEndPoints(destinationPoint, {
32691
32759
 
32692
32760
  // Snap the angle to the nearest multiple of snapDegrees (if provided)
32693
32761
  angle = Math.round((angle - offsetDegrees) / snap) * snap + offsetDegrees;
32694
- const distance = vec.dist(coordsCopy[index], vertex);
32762
+
32763
+ // add 0.01 to prevent rounding errors from causing the point to snap to
32764
+ // a location that is too close to the vertex.
32765
+ const minDistance = minDistanceBetweenAngleVertexAndSidePoint + 0.01;
32766
+ const distance = Math.max(vec.dist(coordsCopy[index], vertex), minDistance);
32695
32767
  const snappedValue = vec.add(vertex, polar(distance, angle));
32696
32768
  return snappedValue;
32697
32769
  }
32770
+ function angleSidePointsTooCloseToVertex(state) {
32771
+ return tooClose(state.coords[0], state.coords[1], state.range) || tooClose(state.coords[2], state.coords[1], state.range);
32772
+ }
32698
32773
  function boundAndSnapToPolygonAngle(destinationPoint, {
32699
32774
  range,
32700
32775
  coords
@@ -32917,7 +32992,12 @@ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
32917
32992
  useEffect(() => {
32918
32993
  dispatch(changeRange([[xMinRange, xMaxRange], [yMinRange, yMaxRange]]));
32919
32994
  }, [dispatch, xMinRange, xMaxRange, yMinRange, yMaxRange]);
32995
+
32996
+ // Update the graph whenever any of the following values changes.
32997
+ // This is necessary to keep the graph previews in sync with the updated
32998
+ // graph settings within the interative graph editor.
32920
32999
  const numSegments = graph.type === "segment" ? graph.numSegments : null;
33000
+ const numPoints = graph.type === "point" ? graph.numPoints : null;
32921
33001
  const numSides = graph.type === "polygon" ? graph.numSides : null;
32922
33002
  const snapTo = graph.type === "polygon" ? graph.snapTo : null;
32923
33003
  const showAngles = graph.type === "polygon" ? graph.showAngles : null;
@@ -32932,7 +33012,7 @@ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
32932
33012
  if (latestPropsRef.current !== originalPropsRef.current) {
32933
33013
  dispatch(reinitialize(latestPropsRef.current));
32934
33014
  }
32935
- }, [graph.type, numSegments, numSides, snapTo, showAngles, showSides, latestPropsRef, graph.startCoords]);
33015
+ }, [graph.type, numPoints, numSegments, numSides, snapTo, showAngles, showSides, latestPropsRef, graph.startCoords]);
32936
33016
  return /*#__PURE__*/React.createElement(MafsGraph, _extends({}, props, {
32937
33017
  state: state,
32938
33018
  dispatch: dispatch
@@ -34242,6 +34322,7 @@ class InteractiveGraph extends React.Component {
34242
34322
  // Mafs shim
34243
34323
  const mafsFlags = (_this$props$apiOption = this.props.apiOptions) == null || (_this$props$apiOption = _this$props$apiOption.flags) == null ? void 0 : _this$props$apiOption["mafs"];
34244
34324
  if (shouldUseMafs(mafsFlags, this.props.graph)) {
34325
+ var _this$props$apiOption2;
34245
34326
  const box = getInteractiveBoxFromSizeClass(this.props.containerSizeClass);
34246
34327
  const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, box[0]);
34247
34328
  const snapStep = this.props.snapStep || Util.snapStepFromGridStep(gridStep);
@@ -34251,7 +34332,8 @@ class InteractiveGraph extends React.Component {
34251
34332
  snapStep: snapStep,
34252
34333
  box: box,
34253
34334
  showTooltips: !!this.props.showTooltips,
34254
- hintMode: this.props.hintMode
34335
+ hintMode: this.props.hintMode,
34336
+ readOnly: (_this$props$apiOption2 = this.props.apiOptions) == null ? void 0 : _this$props$apiOption2.readOnly
34255
34337
  }));
34256
34338
  }
34257
34339
  return /*#__PURE__*/React.createElement(LegacyInteractiveGraph, _extends({
@@ -44170,7 +44252,7 @@ const init = function init(options) {
44170
44252
 
44171
44253
  // This file is processed by a Rollup plugin (replace) to inject the production
44172
44254
  const libName = "@khanacademy/perseus";
44173
- const libVersion = "27.2.0";
44255
+ const libVersion = "28.1.0";
44174
44256
  addLibraryVersionToPerseusDebug(libName, libVersion);
44175
44257
 
44176
44258
  const apiVersion = {
@@ -44725,7 +44807,6 @@ const context = /*#__PURE__*/React.createContext(defaultContext);
44725
44807
  const {
44726
44808
  mapObject
44727
44809
  } = objective_;
44728
- /* eslint-disable-next-line react/no-unsafe */
44729
44810
  class ServerItemRenderer extends React.Component {
44730
44811
  constructor(props) {
44731
44812
  super(props);
@@ -46091,7 +46172,16 @@ class MultiRenderer extends React.Component {
46091
46172
  // _annotateRendererArray() needs this
46092
46173
  ref: null,
46093
46174
  makeRenderer: () => /*#__PURE__*/React.createElement(HintsRenderer, _extends({}, this._getRendererProps(), {
46094
- findExternalWidgets: findExternalWidgets,
46175
+ findExternalWidgets: findExternalWidgets
46176
+ // Note(Jeremy): The MultiRenderer codebase types are
46177
+ // slightly different from the rest of the codebase.
46178
+ // Ideally `HintNode` would spread the `Hint` type into it,
46179
+ // but there is a difference in optionality of `widgets`
46180
+ // and `images` and that causes a huge cascade of type
46181
+ // errors (they are truly optional, but most of our code
46182
+ // assumes they're provided/set). Ignoring this for now.
46183
+ // @ts-expect-error - TS2769 - Type 'HintNode' is not assignable to type 'Hint'.
46184
+ ,
46095
46185
  hints: [hint]
46096
46186
  }))
46097
46187
  };
@@ -47750,5 +47840,5 @@ const EditorJsonify = {
47750
47840
  }
47751
47841
  };
47752
47842
 
47753
- export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getCorrectAnswerForWidgetId, getLineCoords, getLinearSystemCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
47843
+ export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getCorrectAnswerForWidgetId, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
47754
47844
  //# sourceMappingURL=index.js.map