@khanacademy/perseus 39.0.1 → 41.0.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 (53) hide show
  1. package/dist/components/hud.d.ts +4 -0
  2. package/dist/components/i18n-context.d.ts +4 -0
  3. package/dist/components/icon.d.ts +28 -27
  4. package/dist/components/image-loader.d.ts +9 -9
  5. package/dist/components/inline-icon.d.ts +6 -9
  6. package/dist/components/lint.d.ts +13 -0
  7. package/dist/components/math-input.d.ts +2 -2
  8. package/dist/components/multi-button-group.d.ts +26 -0
  9. package/dist/components/number-input.d.ts +19 -0
  10. package/dist/components/range-input.d.ts +3 -0
  11. package/dist/components/stub-tag-editor.d.ts +2 -2
  12. package/dist/components/svg-image.d.ts +30 -0
  13. package/dist/components/tooltip.d.ts +41 -30
  14. package/dist/es/index.css +6 -0
  15. package/dist/es/index.js +1502 -2183
  16. package/dist/es/index.js.map +1 -1
  17. package/dist/es/strings.js +16 -2
  18. package/dist/es/strings.js.map +1 -1
  19. package/dist/icon-paths.d.ts +1 -65
  20. package/dist/index.css +6 -0
  21. package/dist/index.d.ts +11 -6
  22. package/dist/index.js +1509 -2175
  23. package/dist/index.js.map +1 -1
  24. package/dist/strings.d.ts +8 -0
  25. package/dist/strings.js +22 -2
  26. package/dist/strings.js.map +1 -1
  27. package/dist/styles/global-styles.d.ts +2 -47
  28. package/dist/types.d.ts +5 -5
  29. package/dist/widgets/expression/expression.d.ts +4 -4
  30. package/dist/widgets/grapher/util.d.ts +0 -4
  31. package/dist/widgets/interactive-graphs/graphs/angle.d.ts +2 -5
  32. package/dist/widgets/interactive-graphs/graphs/circle.d.ts +2 -5
  33. package/dist/widgets/interactive-graphs/graphs/components/movable-point-view.d.ts +1 -11
  34. package/dist/widgets/interactive-graphs/graphs/components/movable-point.d.ts +2 -1
  35. package/dist/widgets/interactive-graphs/graphs/components/use-control-point.d.ts +23 -0
  36. package/dist/widgets/interactive-graphs/graphs/linear-system.d.ts +2 -5
  37. package/dist/widgets/interactive-graphs/graphs/linear.d.ts +2 -5
  38. package/dist/widgets/interactive-graphs/graphs/point.d.ts +7 -5
  39. package/dist/widgets/interactive-graphs/graphs/polygon.d.ts +2 -5
  40. package/dist/widgets/interactive-graphs/graphs/quadratic.d.ts +2 -4
  41. package/dist/widgets/interactive-graphs/graphs/ray.d.ts +2 -5
  42. package/dist/widgets/interactive-graphs/graphs/screenreader-text.d.ts +1 -0
  43. package/dist/widgets/interactive-graphs/graphs/segment.d.ts +2 -5
  44. package/dist/widgets/interactive-graphs/graphs/sinusoid.d.ts +2 -5
  45. package/dist/widgets/interactive-graphs/mafs-graph.d.ts +1 -2
  46. package/dist/widgets/interactive-graphs/reducer/interactive-graph-action.d.ts +5 -0
  47. package/dist/widgets/interactive-graphs/types.d.ts +13 -1
  48. package/dist/widgets/interactive-graphs/utils.d.ts +3 -0
  49. package/dist/widgets/label-image/label-image.d.ts +2 -2
  50. package/package.json +10 -9
  51. package/dist/components/constants.d.ts +0 -7
  52. package/dist/styles/global-constants.d.ts +0 -342
  53. package/dist/widgets/interactive-graphs/graphs/index.d.ts +0 -9
package/dist/es/index.js CHANGED
@@ -6,9 +6,9 @@ import { View, RenderStateRoot, UniqueIDProvider, useOnMountEffect, useLatestRef
6
6
  import Tooltip$1 from '@khanacademy/wonder-blocks-tooltip';
7
7
  import { HeadingMedium, LabelSmall, Body, LabelMedium } from '@khanacademy/wonder-blocks-typography';
8
8
  import { StyleSheet, css } from 'aphrodite';
9
- import classNames from 'classnames';
9
+ import classNames$1 from 'classnames';
10
10
  import * as React from 'react';
11
- import React__default, { useContext, useState, useEffect, useRef, createContext, forwardRef, useImperativeHandle } from 'react';
11
+ import React__default, { useContext, useState, useEffect, useRef, createContext, forwardRef, useLayoutEffect, useImperativeHandle } from 'react';
12
12
  import * as ReactDOM from 'react-dom';
13
13
  import ReactDOM__default from 'react-dom';
14
14
  import _, { debounce as debounce$1 } from 'underscore';
@@ -32,7 +32,7 @@ import { OptionItem, SingleSelect, MultiSelect } from '@khanacademy/wonder-block
32
32
  import caretDown from '@phosphor-icons/core/regular/caret-down.svg';
33
33
  import caretUp from '@phosphor-icons/core/regular/caret-up.svg';
34
34
  import debounce$2 from 'lodash.debounce';
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
+ import { vec, Coordinates, Ellipse, Plot as Plot$3, Line as Line$4, Point as Point$2, Polygon, Text as Text$1, useTransformContext, Mafs } from 'mafs';
36
36
  import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
37
37
  import questionIcon from '@phosphor-icons/core/regular/question.svg';
38
38
  import { useDrag } from '@use-gesture/react';
@@ -238,7 +238,15 @@ const mockStrings = {
238
238
  simulationLocaleWarning: "Sorry, this simulation isn't available in your language.",
239
239
  addPoint: "Add Point",
240
240
  removePoint: "Remove Point",
241
- graphKeyboardPrompt: "Press Shift + Enter to interact with the graph"
241
+ graphKeyboardPrompt: "Press Shift + Enter to interact with the graph",
242
+ srPointAtCoordinates: ({
243
+ x,
244
+ y
245
+ }) => `Point at ${x} comma ${y}`,
246
+ srInteractiveElements: ({
247
+ elements
248
+ }) => `Interactive elements: ${elements}`,
249
+ srNoInteractiveElements: "No interactive elements"
242
250
  };
243
251
 
244
252
  /**
@@ -246,11 +254,13 @@ const mockStrings = {
246
254
  * used inside the Perseus package.
247
255
  *
248
256
  */
249
- // @ts-expect-error - TS2322 - Type 'Context<{ strings: {}; locale: string; }>' is not assignable to type 'Context<I18nContextType>'.
250
- const PerseusI18nContext = /*#__PURE__*/React.createContext(process.env.NODE_ENV === "test" || process.env.STORYBOOK ? {
257
+ const mockPerseusI18nContext = {
251
258
  strings: mockStrings,
252
259
  locale: "en"
253
- } :
260
+ };
261
+
262
+ // @ts-expect-error - TS2322 - Type 'Context<{ strings: {}; locale: string; }>' is not assignable to type 'Context<I18nContextType>'.
263
+ const PerseusI18nContext = /*#__PURE__*/React.createContext(process.env.NODE_ENV === "test" || process.env.STORYBOOK ? mockPerseusI18nContext :
254
264
  // We want to return null here, not an empty object, so that we
255
265
  // are will throw an error when attempting to access the
256
266
  // undefined locale or strings, making it easier to debug.
@@ -479,7 +489,7 @@ class InnerMathInput extends React.Component {
479
489
  });
480
490
  }
481
491
  render() {
482
- let className = classNames({
492
+ let className = classNames$1({
483
493
  "perseus-math-input": true,
484
494
  // mathquill usually adds these itself but react removes them when
485
495
  // updating the component.
@@ -531,7 +541,7 @@ class InnerMathInput extends React.Component {
531
541
  closeButtonVisible: true,
532
542
  style: styles$E.popoverContent
533
543
  }, /*#__PURE__*/React.createElement(DesktopKeypad, _extends({
534
- onAnalyticsEvent: this.props.analytics.onAnalyticsEvent,
544
+ onAnalyticsEvent: this.props.onAnalyticsEvent,
535
545
  extraKeys: this.props.extraKeys,
536
546
  onClickKey: this.handleKeypadPress,
537
547
  cursorContext: this.state.cursorContext,
@@ -705,6 +715,9 @@ const getDependencies = () => {
705
715
  const DependenciesContext = /*#__PURE__*/React.createContext({
706
716
  analytics: {
707
717
  onAnalyticsEvent: async () => {}
718
+ },
719
+ useVideo: () => {
720
+ throw new Error("useVideo dependency not provided in Perseus dependencies");
708
721
  }
709
722
  });
710
723
 
@@ -958,7 +971,7 @@ class TextListEditor extends React.Component {
958
971
  }
959
972
  TextListEditor.propTypes = {
960
973
  options: PropTypes.array,
961
- layout: PropTypes.string,
974
+ layout: PropTypes.oneOf(["horizontal", "vertical"]),
962
975
  onChange: PropTypes.func.isRequired
963
976
  };
964
977
  TextListEditor.defaultProps = {
@@ -966,6 +979,8 @@ TextListEditor.defaultProps = {
966
979
  layout: "horizontal"
967
980
  };
968
981
 
982
+ const EMPTY_ARRAY = [];
983
+
969
984
  /**
970
985
  * Stub Tag Editor.
971
986
  *
@@ -979,7 +994,6 @@ TextListEditor.defaultProps = {
979
994
  * It also gives a nicer interface for the group metadata editor
980
995
  * in local demo mode.
981
996
  */
982
- const EMPTY_ARRAY = [];
983
997
  class StubTagEditor extends React.Component {
984
998
  render() {
985
999
  return /*#__PURE__*/React.createElement("div", null, this.props.showTitle && /*#__PURE__*/React.createElement("div", {
@@ -2408,7 +2422,7 @@ class Expression extends React.Component {
2408
2422
  }, cb);
2409
2423
  }
2410
2424
  render() {
2411
- var _this$props$keypadCon, _this$props$analytics2;
2425
+ var _this$props$keypadCon, _this$props$analytics2, _this$props$analytics3;
2412
2426
  if (this.props.apiOptions.customKeypad) {
2413
2427
  return /*#__PURE__*/React.createElement(View, {
2414
2428
  className: css(styles$D.mobileLabelInputWrapper)
@@ -2437,7 +2451,7 @@ class Expression extends React.Component {
2437
2451
  onBlur: this._handleBlur
2438
2452
  }));
2439
2453
  }
2440
- const className = classNames({
2454
+ const className = classNames$1({
2441
2455
  "perseus-widget-expression": true,
2442
2456
  "show-error-tooltip": this.state.showErrorTooltip
2443
2457
  });
@@ -2481,9 +2495,7 @@ class Expression extends React.Component {
2481
2495
  hasError: this.state.showErrorStyle,
2482
2496
  ariaLabel: this.props.ariaLabel || this.context.strings.mathInputBox,
2483
2497
  extraKeys: (_this$props$keypadCon = this.props.keypadConfiguration) == null ? void 0 : _this$props$keypadCon.extraKeys,
2484
- analytics: (_this$props$analytics2 = this.props.analytics) != null ? _this$props$analytics2 : {
2485
- onAnalyticsEvent: async function () {}
2486
- }
2498
+ onAnalyticsEvent: (_this$props$analytics2 = (_this$props$analytics3 = this.props.analytics) == null ? void 0 : _this$props$analytics3.onAnalyticsEvent) != null ? _this$props$analytics2 : async function () {}
2487
2499
  }))));
2488
2500
  }
2489
2501
  }
@@ -3886,12 +3898,12 @@ Zoom.prototype.dispose = function () {
3886
3898
  const ZoomService = new ZoomServiceClass();
3887
3899
 
3888
3900
  // @baseFontFamily: "Lato", sans-serif;
3889
- const baseFontFamily$1 = "'Lato', sans-serif";
3901
+ const baseFontFamily = "'Lato', sans-serif";
3890
3902
  // @boldFontFamily: "Lato-Bold", "Lato", sans-serif;
3891
3903
  const boldFontFamily = "'Lato-Bold', 'Lato', sans-serif";
3892
3904
  // @kaGreen: #71b307;
3893
- const kaGreen$1 = "#71B307";
3894
- const white$1 = "#FFFFFF";
3905
+ const kaGreen = "#71B307";
3906
+ const white = "#FFFFFF";
3895
3907
  const gray97 = "#F6F7F7";
3896
3908
  const gray85 = "#D6D8DA";
3897
3909
  const gray76 = "#BABEC2";
@@ -3899,23 +3911,23 @@ const gray68 = "#888D93";
3899
3911
  const gray17 = "#21242c";
3900
3912
 
3901
3913
  // @pure-sm-min: 568px;
3902
- const pureSmMin$2 = "568px";
3914
+ const pureSmMin$1 = "568px";
3903
3915
  // @pure-md-min: 768px;
3904
- const pureMdMin$2 = "768px";
3916
+ const pureMdMin$1 = "768px";
3905
3917
  // @pure-lg-min: 1024px;
3906
- const pureLgMin$2 = "1024px";
3918
+ const pureLgMin$1 = "1024px";
3907
3919
  // @pure-xl-min: 1280px;
3908
- const pureXlMin$2 = "1280px";
3920
+ const pureXlMin$1 = "1280px";
3909
3921
  // @pure-xs-max: (@pure-sm-min - 1);
3910
- const pureXsMax$2 = "567px";
3922
+ const pureXsMax$1 = "567px";
3911
3923
  // @pure-sm-max: (@pure-md-min - 1);
3912
- const pureSmMax$2 = "767px";
3924
+ const pureSmMax$1 = "767px";
3913
3925
  // @pure-md-max: (@pure-lg-min - 1);
3914
- const pureMdMax$2 = "1023px";
3926
+ const pureMdMax$1 = "1023px";
3915
3927
  // @pure-lg-max: (@pure-xl-min - 1);
3916
- const pureLgMax$2 = "1279px";
3928
+ const pureLgMax$1 = "1279px";
3917
3929
  // @tableBackgroundAccent: #f9f9f9; // for striping
3918
- const tableBackgroundAccent$1 = "#F9F9F9";
3930
+ const tableBackgroundAccent = "#F9F9F9";
3919
3931
  // @zIndexAboveScratchpad: @zIndexScratchPad + 1;
3920
3932
  const zIndexAboveScratchpad$1 = 2;
3921
3933
  // @zIndexInteractiveComponent: @zIndexAboveScratchpad + 1;
@@ -3943,26 +3955,26 @@ const checkedColor$1 = "#71B307";
3943
3955
  const articleMaxWidthInPx = 688;
3944
3956
  const articleMaxWidthTableInPx = 512;
3945
3957
 
3946
- var constants$1 = /*#__PURE__*/Object.freeze({
3958
+ var constants = /*#__PURE__*/Object.freeze({
3947
3959
  __proto__: null,
3948
- baseFontFamily: baseFontFamily$1,
3960
+ baseFontFamily: baseFontFamily,
3949
3961
  boldFontFamily: boldFontFamily,
3950
- kaGreen: kaGreen$1,
3951
- white: white$1,
3962
+ kaGreen: kaGreen,
3963
+ white: white,
3952
3964
  gray97: gray97,
3953
3965
  gray85: gray85,
3954
3966
  gray76: gray76,
3955
3967
  gray68: gray68,
3956
3968
  gray17: gray17,
3957
- pureSmMin: pureSmMin$2,
3958
- pureMdMin: pureMdMin$2,
3959
- pureLgMin: pureLgMin$2,
3960
- pureXlMin: pureXlMin$2,
3961
- pureXsMax: pureXsMax$2,
3962
- pureSmMax: pureSmMax$2,
3963
- pureMdMax: pureMdMax$2,
3964
- pureLgMax: pureLgMax$2,
3965
- tableBackgroundAccent: tableBackgroundAccent$1,
3969
+ pureSmMin: pureSmMin$1,
3970
+ pureMdMin: pureMdMin$1,
3971
+ pureLgMin: pureLgMin$1,
3972
+ pureXlMin: pureXlMin$1,
3973
+ pureXsMax: pureXsMax$1,
3974
+ pureSmMax: pureSmMax$1,
3975
+ pureMdMax: pureMdMax$1,
3976
+ pureLgMax: pureLgMax$1,
3977
+ tableBackgroundAccent: tableBackgroundAccent,
3966
3978
  zIndexAboveScratchpad: zIndexAboveScratchpad$1,
3967
3979
  zIndexInteractiveComponent: zIndexInteractiveComponent$1,
3968
3980
  phoneMargin: phoneMargin,
@@ -4080,7 +4092,7 @@ class FixedToResponsive extends React.Component {
4080
4092
  };
4081
4093
 
4082
4094
  // NOTE(jeremy): This depends on styles defined in perseus-renderer.less
4083
- const className = classNames("fixed-to-responsive", this.props.className);
4095
+ const className = classNames$1("fixed-to-responsive", this.props.className);
4084
4096
  const container = /*#__PURE__*/React.createElement("div", {
4085
4097
  className: className,
4086
4098
  style: style
@@ -14800,11 +14812,7 @@ _.extend(MovableLine$4.prototype, {
14800
14812
  * A stripped version of Icon.jsx from webapp. Takes an SVG icon and renders it
14801
14813
  * inline like Font Awesome did.
14802
14814
  *
14803
- * If you are looking for an icon that we've used before you should look in
14804
- * webapp's `icon-paths.js` which is a reference file for all the SVG paths
14805
- * that we've used. You'll need to copy the object from that file into
14806
- * whichever file you're using the icon and explicitly pass it in to the
14807
- * <InlineIcon/> React component.
14815
+ * You can refer to `icon-paths.ts` for all available SVG icons.
14808
14816
  *
14809
14817
  * We assume that the viewBox is cropped and aligned to (0, 0), but icons can
14810
14818
  * be defined differently. At some point we might want to add these attributes
@@ -14812,13 +14820,14 @@ _.extend(MovableLine$4.prototype, {
14812
14820
  *
14813
14821
  * Sample usage:
14814
14822
  *
14815
- * const editIcon = {
14823
+ * ```
14824
+ * const editIcon = {
14816
14825
  * path: "M41.209 53.753l5.39 0l0 5.39l3.136 0l6.468-6.517-8.477-8.526-6.517 6.517l0 3.136zm33.173-34.937q-.882-.882-1.862.049l-19.6 19.6q-.931.98-.049 1.862t1.862-.049l19.6-19.6q.931-.98.049-1.862zm-38.563 45.668l0-16.121l37.632-37.632 16.17 16.121-37.632 37.632l-16.17 0zm43.022-12.397l0 10.633q-.049 6.713-4.753 11.417t-11.368 4.704l-46.599 0q-6.713 0-11.417-4.753t-4.704-11.368l0-46.599q0-6.664 4.753-11.417t11.368-4.704l46.599 0q3.528 0 6.566 1.372.833.392.98 1.323t-.49 1.617l-2.744 2.744q-.784.784-1.96.441t-2.352-.343l-46.599 0q-3.675 0-6.321 2.646t-2.646 6.321l0 46.599q0 3.675 2.646 6.321t6.321 2.646l46.599 0q3.675 0 6.321-2.646t2.646-6.321l0-7.056q0-.735.49-1.225l3.577-3.577q.833-.833 1.96-.392t1.127 1.617zm7.203-51.646q2.254 0 3.773 1.568l8.526 8.526q1.568 1.568 1.568 3.822t-1.568 3.773l-5.145 5.145-16.121-16.121 5.145-5.145q1.568-1.568 3.822-1.568z",
14817
14826
  * width: 100,
14818
14827
  * height: 78.912,
14819
- * };
14820
- * <InlineIcon {...editIcon} />
14821
- *
14828
+ * };
14829
+ * <InlineIcon {...editIcon} />
14830
+ * ```
14822
14831
  */
14823
14832
  const InlineIcon = ({
14824
14833
  path,
@@ -14841,11 +14850,7 @@ const InlineIcon = ({
14841
14850
  }));
14842
14851
 
14843
14852
  /**
14844
- * Icon paths to be used with `inline-icon.jsx`.
14845
- *
14846
- * These paths are taken directly from webapp's `icon-paths.js`. Unlike the
14847
- * webapp equivalent, these can be directly required within Perseus files since
14848
- * this is all bundled together anyway.
14853
+ * Icon paths to be used with `inline-icon.tsx` and `icon.tsx`.
14849
14854
  */
14850
14855
 
14851
14856
  const iconCheck = {
@@ -14853,77 +14858,26 @@ const iconCheck = {
14853
14858
  width: 24,
14854
14859
  height: 24
14855
14860
  };
14856
- const iconCheckMedium = {
14857
- path: "M6.072 10.4l6.175-7.058a1 1 0 1 1 1.506 1.317L6.769 12.64a1 1 0 0 1-1.55-.054L2.203 8.604a1 1 0 1 1 1.594-1.208L6.072 10.4z",
14858
- width: 16,
14859
- height: 16
14860
- };
14861
14861
  const iconChevronDown = {
14862
14862
  path: "M99.669 13.048q0 3.36-2.352 5.712l-41.664 41.664q-2.408 2.408-5.88 2.408t-5.712-2.408l-41.664-41.664q-2.408-2.24-2.408-5.712t2.408-5.88l4.76-4.816q2.52-2.352 5.88-2.352t5.656 2.352l31.136 31.136 31.08-31.136q2.352-2.352 5.712-2.352t5.88 2.352l4.816 4.816q2.352 2.52 2.352 5.88z",
14863
14863
  width: 100,
14864
14864
  height: 63.034
14865
14865
  };
14866
- const iconChevronRight = {
14867
- path: "M62.808 49.728q0 3.36-2.352 5.88l-41.72 41.664q-2.352 2.408-5.768 2.408t-5.768-2.408l-4.872-4.76q-2.352-2.52-2.352-5.88t2.352-5.712l31.08-31.136-31.08-31.024q-2.352-2.52-2.352-5.88t2.352-5.712l4.872-4.76q2.296-2.408 5.768-2.408t5.768 2.408l41.72 41.664q2.352 2.296 2.352 5.656z",
14868
- width: 63.034,
14869
- height: 100
14870
- };
14871
14866
  const iconCircle = {
14872
14867
  path: "M100.035 50.046q.057 13.623-6.669 25.137t-18.24 18.183-25.08 6.669-25.137-6.726q-11.514-6.726-18.183-18.183-6.726-11.571-6.726-25.137t6.726-25.08 18.24-18.24 25.08-6.669q13.566 0 25.08 6.726 11.514 6.669 18.24 18.183t6.669 25.137z",
14873
14868
  width: 100,
14874
14869
  height: 100
14875
14870
  };
14876
- const iconCircleArrowDown = {
14877
- path: "M50.046 83.676q1.767 0 2.907-1.14l29.526-29.526q1.197-1.197 1.197-2.907t-1.197-2.964l-5.928-5.928q-1.197-1.14-2.964-1.14t-2.907 1.14l-12.312 12.312l0-32.661q0-1.71-1.254-2.964t-2.907-1.254l-8.322 0q-1.71 0-2.964 1.254t-1.254 2.964l0 32.661l-12.312-12.312q-1.197-1.254-2.907-1.254t-2.907 1.254l-5.928 5.928q-1.197 1.197-1.197 2.964t1.197 2.907l29.469 29.526q1.197 1.14 2.964 1.14zm49.989-33.63q.057 13.623-6.669 25.137t-18.24 18.183-25.08 6.669-25.137-6.726q-11.514-6.726-18.183-18.183-6.726-11.571-6.726-25.137t6.726-25.08 18.24-18.24 25.08-6.669q13.566 0 25.08 6.726 11.514 6.669 18.24 18.183t6.669 25.137z",
14878
- width: 100,
14879
- height: 100
14880
- };
14881
- const iconCircleArrowUp = {
14882
- path: "M54.207 83.391q1.653 0 2.907-1.254t1.254-2.907l0-32.718l12.312 12.312q1.254 1.254 2.964 1.254t2.907-1.254l5.928-5.928q1.197-1.197 1.14-2.964 0-1.767-1.14-2.907l-29.526-29.526q-1.197-1.14-2.907-1.14t-2.964 1.14l-29.469 29.526q-1.197 1.254-1.197 2.964t1.197 2.907l5.928 5.928q1.197 1.197 2.907 1.197t2.907-1.197l12.312-12.312l0 32.718q0 1.653 1.254 2.907t2.964 1.254l8.322 0zm45.828-33.345q.057 13.623-6.669 25.137t-18.24 18.183-25.08 6.669-25.137-6.726q-11.514-6.726-18.183-18.183-6.726-11.571-6.726-25.137t6.726-25.08 18.24-18.24 25.08-6.669q13.566 0 25.08 6.726 11.514 6.669 18.24 18.183t6.669 25.137z",
14883
- width: 100,
14884
- height: 100
14885
- };
14886
14871
  const iconCircleThin = {
14887
14872
  path: "M50.046 8.322q-8.493 0-16.188 3.306-15.561 6.669-22.173 22.23-3.363 7.695-3.363 16.188t3.306 16.188 8.949 13.281q5.586 5.586 13.281 8.892t16.188 3.306 16.188-3.306 13.281-8.892 8.892-13.281 3.306-16.188-3.306-16.188-8.892-13.281-13.281-8.949q-7.695-3.306-16.188-3.306zm0 91.713q-13.623 0-25.137-6.726t-18.183-18.183q-6.726-11.571-6.726-25.137t6.726-25.08 18.24-18.24 25.08-6.669q13.566 0 25.08 6.726 11.514 6.669 18.24 18.183t6.726 25.137-6.726 25.137-18.24 18.126q-11.514 6.726-25.08 6.726z",
14888
14873
  width: 100,
14889
14874
  height: 99.944
14890
14875
  };
14891
- const iconDesktop = {
14892
- path: "M94.208 52.119l0-43.746q0-.69-.506-1.15t-1.196-.506l-84.088 0q-.69 0-1.196.506t-.506 1.15l0 43.746q0 .69.506 1.196t1.196.506l84.088 0q.69 0 1.196-.506t.506-1.196zm6.716-43.746l0 57.224q0 3.45-2.484 5.934t-5.934 2.484l-28.566 0q0 3.128 2.53 7.774.828 1.61.828 2.622t-1.012 2.07q-1.012 1.012-2.346.966l-26.91 0q-1.38 0-2.392-1.012t-1.012-2.024q0-1.058 1.656-4.14t1.748-6.256l-28.612 0q-3.45 0-5.934-2.484t-2.484-5.934l0-57.224q0-3.45 2.484-5.934t5.934-2.438l84.088 0q3.45 0 5.98 2.438 2.438 2.484 2.438 5.934z",
14893
- width: 100,
14894
- height: 86.648
14895
- };
14896
- const iconDropdownArrow = {
14897
- path: "M9 9.8c0 .5.7 1.7 1.5 2.8 1.5 1.9 1.5 1.9 3 0C15.7 9.7 15.4 9 12 9c-1.6 0-3 .4-3 .8z",
14898
- width: 24,
14899
- height: 24
14900
- };
14901
- const iconExclamationSign = {
14902
- path: "M58.368 81.225l0-12.369q0-.912-.57-1.539t-1.425-.627l-12.54 0q-.855-.057-1.482.627t-.684 1.539l0 12.369q-.057.855.627 1.482t1.539.684l12.54 0q.855 0 1.425-.627t.57-1.539zm1.026-62.871q0-1.596-2.223-1.71l-14.307 0q-2.109 0-2.223 1.71l1.14 40.47q0 .627.627 1.14t1.539.456l12.084 0q.912-.057 1.539-.513t.684-1.083zm-9.348-18.354q13.566 0 25.08 6.726 11.514 6.669 18.24 18.183t6.726 25.137-6.726 25.137-18.24 18.183-25.08 6.669-25.137-6.726q-11.514-6.726-18.183-18.183-6.726-11.571-6.726-25.137t6.726-25.08 18.24-18.24 25.08-6.669z",
14903
- width: 100,
14904
- height: 99.944
14905
- };
14906
- // Grabbed from https://github.com/encharm/Font-Awesome-SVG-PNG
14907
- const iconGear = {
14908
- path: "M1152 896q0-106-75-181t-181-75-181 75-75 181 75 181 181 75 181-75 75-181zm512-109v222q0 12-8 23t-20 13l-185 28q-19 54-39 91 35 50 107 138 10 12 10 25t-9 23q-27 37-99 108t-94 71q-12 0-26-9l-138-108q-44 23-91 38-16 136-29 186-7 28-36 28h-222q-14 0-24.5-8.5t-11.5-21.5l-28-184q-49-16-90-37l-141 107q-10 9-25 9-14 0-25-11-126-114-165-168-7-10-7-23 0-12 8-23 15-21 51-66.5t54-70.5q-27-50-41-99l-183-27q-13-2-21-12.5t-8-23.5v-222q0-12 8-23t19-13l186-28q14-46 39-92-40-57-107-138-10-12-10-24 0-10 9-23 26-36 98.5-107.5t94.5-71.5q13 0 26 10l138 107q44-23 91-38 16-136 29-186 7-28 36-28h222q14 0 24.5 8.5t11.5 21.5l28 184q49 16 90 37l142-107q9-9 24-9 13 0 25 10 129 119 165 170 7 8 7 22 0 12-8 23-15 21-51 66.5t-54 70.5q26 50 41 98l183 28q13 2 21 12.5t8 23.5z",
14909
- width: 1792,
14910
- height: 1792
14911
- };
14912
- const iconMobilePhone = {
14913
- path: "M36.04 89.557q0-2.584-1.836-4.42t-4.42-1.836-4.352 1.836q-1.836 1.836-1.836 4.42t1.836 4.352 4.42 1.836q2.652-.068 4.42-1.836t1.768-4.352zm16.184-12.444l0-54.74q0-1.088-.748-1.768t-1.768-.68l-39.78 0q-1.088 0-1.768.748t-.68 1.7l0 54.74q0 1.02.748 1.768t1.7.68l39.78 0q1.02-.068 1.768-.748t.748-1.7zm-14.892-65.892q0-1.224-1.292-1.292l-12.444 0q-1.224.068-1.224 1.292t1.224 1.224l12.444 0q1.292 0 1.292-1.224zm22.372-1.292l0 79.628q0 3.944-2.992 6.936t-7.004 2.992l-39.78 0q-4.012 0-7.004-2.924-2.924-2.924-2.924-7.004l0-79.628q0-4.012 2.924-6.936t7.004-2.992l39.78 0q4.012-.068 7.004 2.924t2.992 7.004z",
14914
- width: 60.013,
14915
- height: 100
14916
- };
14917
14876
  const iconOk = {
14918
14877
  path: "M37.964 76.048q-2.576 0-4.368-1.792l-31.864-31.864q-1.792-1.792-1.792-4.368t1.792-4.368l8.736-8.68q1.792-1.792 4.368-1.792t4.312 1.792l18.816 18.872 42-42.056q1.792-1.792 4.368-1.792t4.312 1.792l8.736 8.736q1.792 1.792 1.792 4.368t-1.792 4.312l-55.048 55.048q-1.792 1.792-4.368 1.792z",
14919
14878
  width: 100,
14920
14879
  height: 76.637
14921
14880
  };
14922
- const iconPlus = {
14923
- path: "M99.758 43.09l0 13.578q0 2.852-1.984 4.836t-4.836 1.984l-29.45 0l0 29.45q0 2.852-1.984 4.836t-4.836 1.984l-13.578 0q-2.852 0-4.836-1.984t-1.984-4.836l0-29.45l-29.45 0q-2.852 0-4.836-1.984t-1.984-4.836l0-13.578q0-2.852 1.984-4.836t4.836-1.984l29.45 0l0-29.45q0-2.852 1.984-4.836t4.836-1.984l13.578 0q2.852 0 4.836 1.984t1.984 4.836l0 29.45l29.45 0q2.852 0 4.836 1.984t1.984 4.836z",
14924
- width: 100,
14925
- height: 100
14926
- };
14927
14881
  const iconRemove = {
14928
14882
  path: "M100.464 80.808q0 3.404-2.368 5.772l-11.47 11.544q-2.368 2.368-5.772 2.368t-5.698-2.368l-24.864-24.864-24.864 24.864q-2.368 2.368-5.772 2.368t-5.772-2.368l-11.47-11.544q-2.368-2.368-2.368-5.772t2.368-5.698l24.864-24.864-24.864-24.864q-2.368-2.368-2.368-5.772t2.368-5.772l11.47-11.47q2.368-2.368 5.772-2.368t5.772 2.368l24.864 24.864 24.864-24.864q2.294-2.368 5.698-2.368t5.772 2.368l11.47 11.47q2.368 2.368 2.368 5.772t-2.368 5.772l-24.864 24.864 24.864 24.864q2.368 2.294 2.368 5.698z",
14929
14883
  width: 100,
@@ -14939,53 +14893,17 @@ const iconTryAgain = {
14939
14893
  width: 23,
14940
14894
  height: 23
14941
14895
  };
14942
- const iconTablet = {
14943
- path: "M45.322 90.706q0-1.86-1.302-3.224-1.364-1.364-3.224-1.364t-3.224 1.364-1.302 3.224q0 1.86 1.364 3.224 1.302 1.364 3.162 1.302 1.86.062 3.224-1.302t1.302-3.224zm27.218-11.346l0-68.014q0-.93-.682-1.612t-1.55-.682l-58.962 0q-.93 0-1.612.682t-.682 1.612l0 68.014q0 .93.682 1.612t1.612.62l58.962 0q.992-.062 1.612-.682t.62-1.55zm9.114-68.014l0 77.066q0 4.65-3.348 7.998t-7.998 3.348l-58.962 0q-4.65 0-7.998-3.348t-3.348-7.998l0-77.066q0-4.65 3.348-7.998t7.998-3.348l58.962 0q4.65 0 7.998 3.348t3.348 7.998z",
14944
- width: 81.852,
14945
- height: 100
14946
- };
14947
14896
  const iconTrash = {
14948
14897
  path: "M31.293 37.506q2.052 0 2.052 2.109l0 37.506q0 1.995-2.052 2.109l-4.218 0q-.912-.057-1.482-.627t-.57-1.482l0-37.506q0-2.109 2.052-2.109l4.218 0zm18.753 2.109l0 37.506q0 .912-.57 1.482t-1.539.627l-4.161 0q-1.995 0-2.109-2.109l0-37.506q.057-.912.627-1.482t1.482-.627l4.161 0q.969.057 1.539.627t.57 1.482zm14.592-2.109q2.052 0 2.052 2.109l0 37.506q0 1.995-2.052 2.109l-4.161 0q-.969-.057-1.539-.627t-.57-1.482l0-37.506q0-2.109 2.109-2.109l4.161 0zm10.431 49.248l0-61.731l-58.368 0l0 61.731q.057 2.679.969 3.819t1.083 1.14l54.207 0q.171 0 1.14-1.083t.969-3.876zm-43.776-70.11l29.184 0l-3.135-7.581q-.456-.57-1.14-.741l-20.634 0q-.627.114-1.083.741zm-31.293 2.109q0-1.995 2.109-2.109l20.121 0l4.56-10.83q.969-2.394 3.477-4.104 2.565-1.71 5.187-1.71l20.805 0q2.622 0 5.187 1.71t3.477 4.104l4.56 10.83l20.178 0q.912.057 1.482.627t.57 1.482l0 4.161q0 1.995-2.052 2.109l-6.27 0l0 61.731q0 5.415-3.078 9.348t-7.353 3.933l-54.207 0q-4.275 0-7.353-3.819t-3.078-9.177l0-62.016l-6.213 0q-.969 0-1.539-.57t-.57-1.539l0-4.161z",
14949
14898
  width: 91.681,
14950
14899
  height: 100
14951
14900
  };
14952
- const iconUndo = {
14953
- path: "M10,6.6C10,7.2,9.8,8,9.3,9.1c0,0,0,0.1-0.1,0.1S9.2,9.3,9.2,9.4c0,0,0,0.1-0.1,0.1C9,9.6,9,9.6,8.9,9.6 c-0.1,0-0.1,0-0.1-0.1c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1s0-0.1,0-0.1c0-0.3,0-0.5,0-0.7c0-0.4,0-0.7-0.1-1C8.6,7.1,8.5,6.9,8.4,6.7S8.2,6.3,8,6.1C7.8,5.9,7.6,5.8,7.4,5.7S6.9,5.5,6.7,5.5S6.1,5.4,5.8,5.4c-0.3,0-0.6,0-1,0H3.6v1.4c0,0.1,0,0.2-0.1,0.3C3.4,7.1,3.3,7.1,3.2,7.1C3.1,7.1,3,7.1,3,7L0.1,4.1C0,4.1,0,4,0,3.9s0-0.2,0.1-0.3L3,0.8C3,0.7,3.1,0.7,3.2,0.7c0.1,0,0.2,0,0.3,0.1C3.5,0.9,3.6,0.9,3.6,1v1.4h1.2c2.6,0,4.3,0.7,4.9,2.2C9.9,5.2,10,5.8,10,6.6z",
14954
- width: 10,
14955
- height: 10
14956
- };
14957
14901
  const iconMinus = {
14958
14902
  path: `M8,13 L16,13 C16.5522847,13 17,12.5522847 17,12 C17,11.4477153 16.5522847,11 16,11 L8,11 C7.44771525,11 7,11.4477153 7,12 C7,12.5522847 7.44771525,13 8,13 Z`,
14959
14903
  width: 24,
14960
14904
  height: 24
14961
14905
  };
14962
14906
 
14963
- var iconPaths = /*#__PURE__*/Object.freeze({
14964
- __proto__: null,
14965
- iconCheck: iconCheck,
14966
- iconCheckMedium: iconCheckMedium,
14967
- iconChevronDown: iconChevronDown,
14968
- iconChevronRight: iconChevronRight,
14969
- iconCircle: iconCircle,
14970
- iconCircleArrowDown: iconCircleArrowDown,
14971
- iconCircleArrowUp: iconCircleArrowUp,
14972
- iconCircleThin: iconCircleThin,
14973
- iconDesktop: iconDesktop,
14974
- iconDropdownArrow: iconDropdownArrow,
14975
- iconExclamationSign: iconExclamationSign,
14976
- iconGear: iconGear,
14977
- iconMobilePhone: iconMobilePhone,
14978
- iconOk: iconOk,
14979
- iconPlus: iconPlus,
14980
- iconRemove: iconRemove,
14981
- iconStar: iconStar,
14982
- iconTryAgain: iconTryAgain,
14983
- iconTablet: iconTablet,
14984
- iconTrash: iconTrash,
14985
- iconUndo: iconUndo,
14986
- iconMinus: iconMinus
14987
- });
14988
-
14989
14907
  /* eslint-disable @babel/no-invalid-this */
14990
14908
  const add$1 = {
14991
14909
  constrain: function () {
@@ -16878,6 +16796,15 @@ const Status = {
16878
16796
  LOADED: "loaded",
16879
16797
  FAILED: "failed"
16880
16798
  };
16799
+ /**
16800
+ * Component to display an image (or other React components) while the desired
16801
+ * image is loading.
16802
+ *
16803
+ * Derived from
16804
+ * https://github.com/hzdg/react-imageloader/blob/master/src/index.js
16805
+ * to better suit our environment/build tools. Additionally, this one does
16806
+ * not introduce a wrapper element, which makes styling easier.
16807
+ */
16881
16808
  class ImageLoader extends React.Component {
16882
16809
  constructor(props) {
16883
16810
  super(props);
@@ -17432,7 +17359,7 @@ class SvgImage extends React.Component {
17432
17359
  // Just use a normal image if a normal image is provided
17433
17360
  if (!Util.isLabeledSVG(imageSrc)) {
17434
17361
  if (responsive) {
17435
- const wrapperClasses = classNames({
17362
+ const wrapperClasses = classNames$1({
17436
17363
  zoomable: (width || 0) > ZOOMABLE_THRESHOLD,
17437
17364
  "svg-image": true
17438
17365
  });
@@ -18222,7 +18149,7 @@ const styles$C = StyleSheet.create({
18222
18149
  },
18223
18150
  ":hover ~ div span[data-lint-inside-table]": {
18224
18151
  backgroundColor: warningColor,
18225
- color: white$1
18152
+ color: white
18226
18153
  }
18227
18154
  },
18228
18155
  // This is how we position the hover target for inline lint.
@@ -18258,7 +18185,7 @@ const styles$C = StyleSheet.create({
18258
18185
  // draw attention to the linty content.
18259
18186
  ":hover ~ span": {
18260
18187
  backgroundColor: warningColor,
18261
- color: white$1
18188
+ color: white
18262
18189
  }
18263
18190
  },
18264
18191
  radioWidgetHoverTarget: {
@@ -18300,7 +18227,7 @@ const styles$C = StyleSheet.create({
18300
18227
  // draw attention to the linty content.
18301
18228
  ":hover ~ span": {
18302
18229
  backgroundColor: warningColor,
18303
- color: white$1
18230
+ color: white
18304
18231
  }
18305
18232
  },
18306
18233
  // This is the class for the lint indicator in the margin.
@@ -18337,10 +18264,10 @@ const styles$C = StyleSheet.create({
18337
18264
  // When it is displayed, it goes on top!
18338
18265
  zIndex: 1000,
18339
18266
  // These styles control what the tooltip looks like
18340
- color: white$1,
18267
+ color: white,
18341
18268
  backgroundColor: gray17,
18342
18269
  opacity: 0.9,
18343
- fontFamily: baseFontFamily$1,
18270
+ fontFamily: baseFontFamily,
18344
18271
  fontSize: "12px",
18345
18272
  lineHeight: "15px",
18346
18273
  width: "320px",
@@ -19342,7 +19269,7 @@ var preprocessTex = (texCode => texCode
19342
19269
 
19343
19270
  const {
19344
19271
  interactiveSizes
19345
- } = constants$1;
19272
+ } = constants;
19346
19273
 
19347
19274
  // Note: these size cutoffs represent content-width cutoffs as specified in
19348
19275
  // http://zpl.io/1mVmvU (broken link - we no longer use Zeplin)
@@ -19375,13 +19302,6 @@ const getInteractiveBoxFromSizeClass = sizeClass => {
19375
19302
  return [interactiveSizes.defaultBoxSize, interactiveSizes.defaultBoxSize];
19376
19303
  };
19377
19304
 
19378
- var sizingUtils = /*#__PURE__*/Object.freeze({
19379
- __proto__: null,
19380
- containerSizeClass: containerSizeClass,
19381
- getClassFromWidth: getClassFromWidth,
19382
- getInteractiveBoxFromSizeClass: getInteractiveBoxFromSizeClass
19383
- });
19384
-
19385
19305
  class WidgetContainer extends React.Component {
19386
19306
  constructor(...args) {
19387
19307
  super(...args);
@@ -19432,7 +19352,7 @@ class WidgetContainer extends React.Component {
19432
19352
  return this.props.shouldHighlight !== nextProps.shouldHighlight || this.props.type !== nextProps.type || this.state.widgetProps !== nextState.widgetProps || this.state.sizeClass !== nextState.sizeClass;
19433
19353
  }
19434
19354
  render() {
19435
- let className = classNames({
19355
+ let className = classNames$1({
19436
19356
  "perseus-widget-container": true,
19437
19357
  "widget-highlight": this.props.shouldHighlight,
19438
19358
  "widget-nohighlight": !this.props.shouldHighlight,
@@ -20025,7 +19945,7 @@ class Renderer extends React.Component {
20025
19945
  if (this.translationIndex != null) {
20026
19946
  className = null;
20027
19947
  } else {
20028
- className = classNames({
19948
+ className = classNames$1({
20029
19949
  "perseus-paragraph-centered": !this._foundTextNodes,
20030
19950
  // There is only one node being rendered,
20031
19951
  // and it's a full-width widget.
@@ -20841,7 +20761,7 @@ class Renderer extends React.Component {
20841
20761
  const markdownContents = this.outputMarkdown(parsedMarkdown, {
20842
20762
  baseElements: apiOptions.baseElements
20843
20763
  });
20844
- const className = classNames({
20764
+ const className = classNames$1({
20845
20765
  [ClassNames.RENDERER]: true,
20846
20766
  [ClassNames.RESPONSIVE_RENDERER]: true,
20847
20767
  [ClassNames.TWO_COLUMN_RENDERER]: this._isTwoColumn
@@ -21014,7 +20934,7 @@ const Triangle = props => {
21014
20934
  width: 0,
21015
20935
  position: "absolute",
21016
20936
  left: props.left,
21017
- top: props["top"],
20937
+ top: props.top,
21018
20938
  borderLeft: borderLeft,
21019
20939
  borderRight: borderRight,
21020
20940
  borderTop: borderTop,
@@ -21095,6 +21015,47 @@ const HORIZONTAL_ALIGNMNENTS = {
21095
21015
  arrowLeft: arrowSize => -arrowSize - 2
21096
21016
  }
21097
21017
  };
21018
+ /**
21019
+ * DEPRECATED! Use Wonder Blocks tooltip instead.
21020
+ *
21021
+ * A generic tooltip library for React.js
21022
+ *
21023
+ * ```
21024
+ * import Tooltip from "./tooltip";
21025
+ * <Tooltip
21026
+ * className="class-for-tooltip-contents"
21027
+ * horizontalPosition={HoriziontalDirection.Left}
21028
+ * horizontalAlign={HoriziontalDirection.Left}
21029
+ * verticalPosition={VerticalDirection.Top}
21030
+ * arrowSize={10} // arrow size in pixels
21031
+ * borderColor="#ccc" // color of the border for the tooltip
21032
+ * show={true} // whether the tooltip should currently be visible
21033
+ * targetContainerStyle={targetContainerStyle}
21034
+ * >
21035
+ * <TargetElementOfTheTooltip />
21036
+ * <TooltipContents1 />
21037
+ * <TooltipContents2 />
21038
+ * </Tooltip>
21039
+ * ```
21040
+ *
21041
+ * To show/hide the tooltip, the parent component should call the
21042
+ * `.show()` and `.hide()` methods of the tooltip when appropriate.
21043
+ * (These are usually set up as handlers of events on the target element.)
21044
+ *
21045
+ * Notes:
21046
+ * `className` should not specify a border; that is handled by `borderColor`
21047
+ * so that the arrow and tooltip match
21048
+ *
21049
+ * ```
21050
+ * __,,--``\\
21051
+ * _,,-''`` \\ ,
21052
+ * '----------_.------'-.___|\__
21053
+ * _.--''`` `)__ )__ @\__
21054
+ * ( .. ''---/___,,E/__,E'------`
21055
+ * `-''`''
21056
+ * Here be dragons.
21057
+ * ```
21058
+ */
21098
21059
  class Tooltip extends React.Component {
21099
21060
  constructor(...args) {
21100
21061
  super(...args);
@@ -21321,10 +21282,7 @@ class InputWithExamples extends React.Component {
21321
21282
  return "- " + example;
21322
21283
  }).join("\n");
21323
21284
  const showExamples = this.props.shouldShowExamples && this.state.showExamples;
21324
- return /*#__PURE__*/React.createElement(Tooltip
21325
- // eslint-disable-next-line react/no-string-refs
21326
- , {
21327
- ref: "tooltip",
21285
+ return /*#__PURE__*/React.createElement(Tooltip, {
21328
21286
  className: "perseus-formats-tooltip preview-measure",
21329
21287
  horizontalPosition: HorizontalDirection.Left,
21330
21288
  horizontalAlign: HorizontalDirection.Left,
@@ -24577,29 +24535,29 @@ var PassageRef$1 = {
24577
24535
  * });
24578
24536
  */
24579
24537
  const {
24580
- pureXsMax: pureXsMax$1,
24581
- pureSmMin: pureSmMin$1,
24582
- pureSmMax: pureSmMax$1,
24583
- pureMdMin: pureMdMin$1,
24584
- pureMdMax: pureMdMax$1,
24585
- pureLgMin: pureLgMin$1,
24586
- pureLgMax: pureLgMax$1,
24587
- pureXlMin: pureXlMin$1
24588
- } = constants$1;
24538
+ pureXsMax,
24539
+ pureSmMin,
24540
+ pureSmMax,
24541
+ pureMdMin,
24542
+ pureMdMax,
24543
+ pureLgMin,
24544
+ pureLgMax,
24545
+ pureXlMin
24546
+ } = constants;
24589
24547
  var mediaQueries = {
24590
- xs: `@media screen and (max-width: ${pureXsMax$1})`,
24591
- sm: `@media screen and (min-width: ${pureSmMin$1}) and ` + `(max-width: ${pureSmMax$1})`,
24592
- md: `@media screen and (min-width: ${pureMdMin$1}) and ` + `(max-width: ${pureMdMax$1})`,
24593
- lg: `@media screen and (min-width: ${pureLgMin$1}) and ` + `(max-width: ${pureLgMax$1})`,
24594
- xl: `@media screen and (min-width: ${pureXlMin$1})`,
24595
- xsOrSmaller: `@media screen and (max-width: ${pureXsMax$1})`,
24596
- smOrSmaller: `@media screen and (max-width: ${pureSmMax$1})`,
24597
- mdOrSmaller: `@media screen and (max-width: ${pureMdMax$1})`,
24598
- lgOrSmaller: `@media screen and (max-width: ${pureLgMax$1})`,
24599
- smOrLarger: `@media screen and (min-width: ${pureSmMin$1})`,
24600
- mdOrLarger: `@media screen and (min-width: ${pureMdMin$1})`,
24601
- lgOrLarger: `@media screen and (min-width: ${pureLgMin$1})`,
24602
- xlOrLarger: `@media screen and (min-width: ${pureXlMin$1})`
24548
+ xs: `@media screen and (max-width: ${pureXsMax})`,
24549
+ sm: `@media screen and (min-width: ${pureSmMin}) and ` + `(max-width: ${pureSmMax})`,
24550
+ md: `@media screen and (min-width: ${pureMdMin}) and ` + `(max-width: ${pureMdMax})`,
24551
+ lg: `@media screen and (min-width: ${pureLgMin}) and ` + `(max-width: ${pureLgMax})`,
24552
+ xl: `@media screen and (min-width: ${pureXlMin})`,
24553
+ xsOrSmaller: `@media screen and (max-width: ${pureXsMax})`,
24554
+ smOrSmaller: `@media screen and (max-width: ${pureSmMax})`,
24555
+ mdOrSmaller: `@media screen and (max-width: ${pureMdMax})`,
24556
+ lgOrSmaller: `@media screen and (max-width: ${pureLgMax})`,
24557
+ smOrLarger: `@media screen and (min-width: ${pureSmMin})`,
24558
+ mdOrLarger: `@media screen and (min-width: ${pureMdMin})`,
24559
+ lgOrLarger: `@media screen and (min-width: ${pureLgMin})`,
24560
+ xlOrLarger: `@media screen and (min-width: ${pureXlMin})`
24603
24561
  };
24604
24562
 
24605
24563
  const {
@@ -24609,7 +24567,7 @@ const {
24609
24567
  checkedColor,
24610
24568
  circleSize,
24611
24569
  radioMarginWidth
24612
- } = constants$1;
24570
+ } = constants;
24613
24571
  var sharedStyles = StyleSheet.create({
24614
24572
  perseusInteractive: {
24615
24573
  zIndex: zIndexInteractiveComponent,
@@ -24841,6 +24799,37 @@ const BASE_ICON_SIZE = 10;
24841
24799
  // If you pass in an object you need to specify an SVG path to
24842
24800
  // render along with the width and height of the path (to ensure
24843
24801
  // that it gets scaled correctly).
24802
+ /**
24803
+ * An SVG Icon
24804
+ *
24805
+ * This component is designed to take in SVG paths and render icons based upon
24806
+ * them. If you are looking for an icon that we've used before you should look
24807
+ * in `icon-paths.js` which is a reference file for all the SVG paths that
24808
+ * we've used. You'll need to copy the object from that file into whichever
24809
+ * file you're using the icon and explicitly pass it in to the <Icon/> React
24810
+ * component.
24811
+ *
24812
+ * Sample usage:
24813
+ *
24814
+ * ```
24815
+ * const dropdownIcon = `M5,6L0,0L10,0`;
24816
+ * <Icon icon={dropdownIcon} />
24817
+ * ```
24818
+ *
24819
+ * Or:
24820
+ *
24821
+ * ```
24822
+ * const dropdownIcon = {
24823
+ * path: `M5,6L0,0L10,0`,
24824
+ * height: 10,
24825
+ * width: 10,
24826
+ * };
24827
+ * <Icon icon={dropdownIcon} size={20} />
24828
+ * ```
24829
+ *
24830
+ * If you want to add an entirely new icon please read the note inside
24831
+ * the `icon-paths.ts` file.
24832
+ */
24844
24833
  class Icon extends React.Component {
24845
24834
  render() {
24846
24835
  const {
@@ -24962,7 +24951,7 @@ function getA11yText(letter, checked, correct, crossedOut, showCorrectness, stri
24962
24951
  */
24963
24952
  const FocusRing = function FocusRing({
24964
24953
  visible = true,
24965
- color = kaGreen$1,
24954
+ color = kaGreen,
24966
24955
  multipleSelect = false,
24967
24956
  children
24968
24957
  }) {
@@ -25063,7 +25052,7 @@ function ChoiceInner(props) {
25063
25052
  } = usePerseusI18n();
25064
25053
  const letter = getChoiceLetter(pos, strings);
25065
25054
  if (!showCorrectness) {
25066
- return letter;
25055
+ return /*#__PURE__*/React.createElement("span", null, letter);
25067
25056
  }
25068
25057
  if (correct) {
25069
25058
  return /*#__PURE__*/React.createElement(InlineIcon, _extends({}, iconCheck, {
@@ -25090,7 +25079,7 @@ function getDynamicStyles(checked, showCorrectness, pressed, multipleSelect, cor
25090
25079
  backgroundColor = "transparent";
25091
25080
  } else if (checked) {
25092
25081
  const bg = showCorrectness && correct ? color.green : color.blue;
25093
- color$1 = white$1;
25082
+ color$1 = white;
25094
25083
  backgroundColor = bg;
25095
25084
  borderColor = bg;
25096
25085
  } else {
@@ -25133,7 +25122,6 @@ const ChoiceIcon = function ChoiceIcon(props) {
25133
25122
  visible: focused || hovered,
25134
25123
  multipleSelect: multipleSelect
25135
25124
  }, /*#__PURE__*/React.createElement("div", {
25136
- // @ts-expect-error - TS2322 - Type '{ backgroundColor: string | null | undefined; borderColor: string; color: string; borderRadius: number; }' is not assignable to type 'Properties<string | number, string & {}>'.
25137
25125
  style: dynamicStyles,
25138
25126
  "data-testid": "choice-icon__library-choice-icon",
25139
25127
  className: css(styles$s.circle, showCorrectness && correct && styles$s.circleCorrect, showCorrectness && !correct && styles$s.circleIncorrect, showCorrectness && !correct && (checked || previouslyAnswered) && styles$s.circleIncorrectAnswered)
@@ -25168,7 +25156,7 @@ const styles$s = StyleSheet.create({
25168
25156
  borderStyle: "solid",
25169
25157
  borderWidth: 2,
25170
25158
  // The default icons have letters in them. Style those letters.
25171
- fontFamily: baseFontFamily$1,
25159
+ fontFamily: baseFontFamily,
25172
25160
  // NOTE(emily): We explicitly set the font weight instead of using the
25173
25161
  // "bold font family" so that characters which fall back to the default
25174
25162
  // font get bolded too.
@@ -25328,8 +25316,8 @@ const Choice = function Choice(props) {
25328
25316
  crossedOut: updatedCrossedOut
25329
25317
  });
25330
25318
  }
25331
- const descriptionClassName = classNames("description", css(styles$q.description));
25332
- const rationaleClassName = classNames("perseus-radio-rationale-content", css(styles$q.rationale));
25319
+ const descriptionClassName = classNames$1("description", css(styles$q.description));
25320
+ const rationaleClassName = classNames$1("perseus-radio-rationale-content", css(styles$q.rationale));
25333
25321
 
25334
25322
  // We want to show the choices as dimmed out when the choices are disabled.
25335
25323
  // However, we don't want to do this when we're in review mode in the
@@ -25679,8 +25667,8 @@ const BaseRadio = function BaseRadio({
25679
25667
  const isMobile = apiOptions.isMobile;
25680
25668
  const firstChoiceHighlighted = choices[0].highlighted;
25681
25669
  const lastChoiceHighlighted = choices[choices.length - 1].highlighted;
25682
- const className = classNames("perseus-widget-radio", !editMode && "perseus-rendered-radio", css(styles$p.radio, styles$p.responsiveRadioContainer, firstChoiceHighlighted && isMobile && styles$p.radioContainerFirstHighlighted, lastChoiceHighlighted && isMobile && styles$p.radioContainerLastHighlighted));
25683
- const instructionsClassName = classNames("instructions", css(styles$p.instructions, isMobile && styles$p.instructionsMobile));
25670
+ const className = classNames$1("perseus-widget-radio", !editMode && "perseus-rendered-radio", css(styles$p.radio, styles$p.responsiveRadioContainer, firstChoiceHighlighted && isMobile && styles$p.radioContainerFirstHighlighted, lastChoiceHighlighted && isMobile && styles$p.radioContainerLastHighlighted));
25671
+ const instructionsClassName = classNames$1("instructions", css(styles$p.instructions, isMobile && styles$p.instructionsMobile));
25684
25672
  const instructions = getInstructionsText(multipleSelect, countChoices, numCorrect, strings);
25685
25673
  const responsiveClassName = css(styles$p.responsiveFieldset);
25686
25674
  const fieldset = /*#__PURE__*/React.createElement("fieldset", {
@@ -25746,7 +25734,7 @@ const BaseRadio = function BaseRadio({
25746
25734
  if (reviewMode && reviewModeRubric) {
25747
25735
  correctnessClass = reviewModeRubric.choices[i].correct ? ClassNames.CORRECT : ClassNames.INCORRECT;
25748
25736
  }
25749
- const className = classNames(aphroditeClassName(choice.checked),
25737
+ const className = classNames$1(aphroditeClassName(choice.checked),
25750
25738
  // TODO(aria): Make test case for these API
25751
25739
  // classNames
25752
25740
  ClassNames.RADIO.OPTION, choice.checked && ClassNames.RADIO.SELECTED, correctnessClass);
@@ -26539,7 +26527,7 @@ class Categorizer extends React.Component {
26539
26527
 
26540
26528
  // TODO(benkomalo): kill CSS-based styling and move everything to
26541
26529
  // aphrodite.
26542
- const extraClassNames = classNames({
26530
+ const extraClassNames = classNames$1({
26543
26531
  "categorizer-container": true,
26544
26532
  "static-mode": this.props.static
26545
26533
  });
@@ -27433,7 +27421,7 @@ class GradedGroup extends React.Component {
27433
27421
  gradeStatus = this.context.strings.incorrect;
27434
27422
  }
27435
27423
  const mobileClass = this.props.inGradedGroupSet ? css(styles$j.gradedGroupInSet) : css(styles$j.gradedGroup);
27436
- const classes = classNames({
27424
+ const classes = classNames$1({
27437
27425
  [mobileClass]: apiOptions.isMobile,
27438
27426
  "perseus-graded-group": true,
27439
27427
  "answer-correct": apiOptions.isMobile ? false : this.state.status === GRADING_STATUSES.correct,
@@ -27548,7 +27536,7 @@ const styles$j = StyleSheet.create({
27548
27536
  gradedGroup: {
27549
27537
  borderTop: `1px solid ${gray76}`,
27550
27538
  borderBottom: `1px solid ${gray76}`,
27551
- backgroundColor: tableBackgroundAccent$1,
27539
+ backgroundColor: tableBackgroundAccent,
27552
27540
  marginLeft: negativePhoneMargin,
27553
27541
  marginRight: negativePhoneMargin,
27554
27542
  paddingBottom: phoneMargin,
@@ -27612,7 +27600,7 @@ class Indicators extends React.Component {
27612
27600
  return /*#__PURE__*/React.createElement("ul", {
27613
27601
  // reduntantly add class name for use in .less files--
27614
27602
  // the styles object key gets hashed
27615
- className: classNames(css(styles$i.indicatorContainer), "indicatorContainer")
27603
+ className: classNames$1(css(styles$i.indicatorContainer), "indicatorContainer")
27616
27604
  }, this.props.gradedGroups.map(({
27617
27605
  title
27618
27606
  }, i) => /*#__PURE__*/React.createElement("li", {
@@ -27827,7 +27815,7 @@ const styles$i = StyleSheet.create({
27827
27815
  container: {
27828
27816
  borderTop: `1px solid ${gray76}`,
27829
27817
  borderBottom: `1px solid ${gray76}`,
27830
- backgroundColor: tableBackgroundAccent$1,
27818
+ backgroundColor: tableBackgroundAccent,
27831
27819
  marginLeft: negativePhoneMargin,
27832
27820
  marginRight: negativePhoneMargin,
27833
27821
  paddingBottom: phoneMargin,
@@ -28435,21 +28423,6 @@ const typeToButton = type => {
28435
28423
  };
28436
28424
  };
28437
28425
 
28438
- var util = /*#__PURE__*/Object.freeze({
28439
- __proto__: null,
28440
- DEFAULT_BACKGROUND_IMAGE: DEFAULT_BACKGROUND_IMAGE,
28441
- allTypes: allTypes,
28442
- functionForType: functionForType,
28443
- getEquationString: getEquationString,
28444
- pointsFromNormalized: pointsFromNormalized,
28445
- maybePointsFromNormalized: maybePointsFromNormalized,
28446
- defaultPlotProps: defaultPlotProps,
28447
- chooseType: chooseType,
28448
- getGridAndSnapSteps: getGridAndSnapSteps,
28449
- DEFAULT_GRAPHER_PROPS: DEFAULT_GRAPHER_PROPS,
28450
- typeToButton: typeToButton
28451
- });
28452
-
28453
28426
  function getCoefficientsByType(data) {
28454
28427
  if (data.type === "exponential" || data.type === "logarithm") {
28455
28428
  const grader = functionForType(data.type);
@@ -29006,7 +28979,7 @@ class Group extends React.Component {
29006
28979
  // TODO(mdr): Widgets inside this Renderer are not discoverable through
29007
28980
  // the parent Renderer's `findWidgets` function.
29008
28981
  return /*#__PURE__*/React.createElement("div", {
29009
- className: classNames({
28982
+ className: classNames$1({
29010
28983
  "perseus-group": true,
29011
28984
  "perseus-group-valid-answer": isValid,
29012
28985
  "perseus-group-invalid-answer": isInvalid
@@ -29274,7 +29247,7 @@ class ImageWidget extends React.Component {
29274
29247
  }
29275
29248
  _title = `**${_title}** `;
29276
29249
  }
29277
- const className = classNames({
29250
+ const className = classNames$1({
29278
29251
  "perseus-image-caption": true,
29279
29252
  "has-title": !!_title
29280
29253
  });
@@ -30982,6 +30955,10 @@ const HiddenIcon = () => /*#__PURE__*/React.createElement("svg", {
30982
30955
  }, /*#__PURE__*/React.createElement("path", {
30983
30956
  d: "M0 0h24v24H0z"
30984
30957
  }))));
30958
+ /**
30959
+ * A "heads-up display" (HUD) indicator that includes a short message (usually
30960
+ * used for linting errors). The indicator can be disabled.
30961
+ */
30985
30962
  const HUD = ({
30986
30963
  message,
30987
30964
  enabled,
@@ -31013,7 +30990,7 @@ const styles$g = StyleSheet.create({
31013
30990
  fontFamily: boldFontFamily,
31014
30991
  fontSize: "15px",
31015
30992
  lineHeight: "18px",
31016
- color: white$1,
30993
+ color: white,
31017
30994
  userSelect: "none",
31018
30995
  borderWidth: 0 // <button> gives us a border by default
31019
30996
  },
@@ -31202,18 +31179,24 @@ const {
31202
31179
  const toNumericString = KhanMath.toNumericString;
31203
31180
  const getNumericFormat = KhanMath.getNumericFormat;
31204
31181
 
31205
- /* An input box that accepts only numeric strings
31182
+ /**
31183
+ * An input box that accepts only numeric strings
31206
31184
  *
31207
- * Calls onChange(value, format) for valid numbers.
31208
- * Reverts to the current value onBlur or on [ENTER],
31185
+ * Calls `onChange(value, format)` for valid numbers.
31186
+ *
31187
+ * Reverts to the current value `onBlur` or on [ENTER],
31209
31188
  * but maintains the format (i.e. 3/2, 1 1/2, 150%)
31210
- * Accepts empty input and sends it to onChange as null
31211
- * if no numeric placeholder is set.
31212
- * If given a checkValidity function, will turn
31213
- * the background/outline red when invalid
31214
- * If useArrowKeys is set to true, up/down arrows will
31215
- * increment/decrement integers
31216
- * Optionally takes a size ("mini", "small", "normal")
31189
+ *
31190
+ * Accepts empty input and sends it to `onChange` as `null` if no numeric
31191
+ * placeholder is set.
31192
+ *
31193
+ * If given a `checkValidity` function, will turn the background/outline red
31194
+ * when invalid.
31195
+ *
31196
+ * If `useArrowKeys` is set to `true`, up/down arrows will increment/decrement
31197
+ * integers.
31198
+ *
31199
+ * Optionally takes a `size` (`"mini"`, `"small"`,` `"normal"`)
31217
31200
  */
31218
31201
  class NumberInput extends React.Component {
31219
31202
  constructor(...args) {
@@ -31335,7 +31318,7 @@ class NumberInput extends React.Component {
31335
31318
  }
31336
31319
  }
31337
31320
  render() {
31338
- let classes = classNames({
31321
+ let classes = classNames$1({
31339
31322
  "number-input": true,
31340
31323
  "invalid-input": !this._checkValidity(this.props.value),
31341
31324
  mini: this.props.size === "mini",
@@ -31372,7 +31355,7 @@ NumberInput.propTypes = {
31372
31355
  onChange: PropTypes.func.isRequired,
31373
31356
  onFormatChange: PropTypes.func,
31374
31357
  checkValidity: PropTypes.func,
31375
- size: PropTypes.string,
31358
+ size: PropTypes.oneOf(["mini", "small", "normal"]),
31376
31359
  label: PropTypes.oneOf(["put your labels outside your inputs!"])
31377
31360
  };
31378
31361
  NumberInput.defaultProps = {
@@ -31386,8 +31369,8 @@ NumberInput.defaultProps = {
31386
31369
 
31387
31370
  const truth = () => true;
31388
31371
 
31389
- /* A minor abstraction on top of NumberInput for ranges
31390
- *
31372
+ /**
31373
+ * A minor abstraction on top of `NumberInput` for ranges
31391
31374
  */
31392
31375
  class RangeInput extends React.Component {
31393
31376
  constructor(...args) {
@@ -32094,7 +32077,12 @@ const actions = {
32094
32077
  },
32095
32078
  polygon: {
32096
32079
  movePoint,
32097
- moveAll
32080
+ moveAll,
32081
+ addPoint,
32082
+ removePoint,
32083
+ focusPoint,
32084
+ blurPoint,
32085
+ clickPoint
32098
32086
  },
32099
32087
  quadratic: {
32100
32088
  movePoint
@@ -32233,11 +32221,333 @@ function reinitialize(params) {
32233
32221
  };
32234
32222
  }
32235
32223
 
32224
+ /**
32225
+ * Wrapper with Mafs transform styles for SVG elements.
32226
+ * Scales and translates the SVG element to match the Mafs view.
32227
+ * Use if you do not need/want to use the hook in `use-transform.ts`.
32228
+ */
32229
+ const MafsCssTransformWrapper = ({
32230
+ children
32231
+ }) => /*#__PURE__*/React.createElement("g", {
32232
+ style: {
32233
+ transform: `var(--mafs-view-transform) var(--mafs-user-transform)`
32234
+ }
32235
+ }, children);
32236
+
32237
+ const _excluded$1 = ["children"];
32238
+ const TextLabel = _ref => {
32239
+ let {
32240
+ children
32241
+ } = _ref,
32242
+ rest = _objectWithoutPropertiesLoose(_ref, _excluded$1);
32243
+ return /*#__PURE__*/React__default.createElement(Text$1, _extends({
32244
+ size: 16,
32245
+ svgTextProps: {
32246
+ filter: "url(#background)",
32247
+ fontWeight: "bold"
32248
+ }
32249
+ }, rest), children);
32250
+ };
32251
+ const SvgDefs = () => /*#__PURE__*/React__default.createElement("defs", null, /*#__PURE__*/React__default.createElement("filter", {
32252
+ id: "background",
32253
+ x: "-5%",
32254
+ width: "110%",
32255
+ y: "0%",
32256
+ height: "100%"
32257
+ }, /*#__PURE__*/React__default.createElement("feFlood", {
32258
+ floodColor: "#FFF",
32259
+ floodOpacity: "0.64"
32260
+ }), /*#__PURE__*/React__default.createElement("feComposite", {
32261
+ operator: "over",
32262
+ in: "SourceGraphic"
32263
+ })));
32264
+
32265
+ const PolygonAngle = ({
32266
+ centerPoint,
32267
+ endPoints,
32268
+ range,
32269
+ polygonLines,
32270
+ showAngles,
32271
+ snapTo
32272
+ }) => {
32273
+ const [centerX, centerY] = centerPoint;
32274
+ const areClockwise = clockwise$1([centerPoint, ...endPoints]);
32275
+ const [[startX, startY], [endX, endY]] = areClockwise ? endPoints : endPoints.reverse();
32276
+ const radius = 0.3;
32277
+ const a = vec.dist(centerPoint, endPoints[0]);
32278
+ const b = vec.dist(centerPoint, endPoints[1]);
32279
+ const c = vec.dist(endPoints[0], endPoints[1]);
32280
+
32281
+ // Law of cosines
32282
+ const angle = Math.acos((a ** 2 + b ** 2 - c ** 2) / (2 * a * b));
32283
+ const y1 = centerY + (startY - centerY) / a * radius;
32284
+ const x2 = centerX + (endX - centerX) / b * radius;
32285
+ const x1 = centerX + (startX - centerX) / a * radius;
32286
+ const y2 = centerY + (endY - centerY) / b * radius;
32287
+
32288
+ // Fourth point that would create a rhomboid
32289
+ // Used to calculate the inside of the angle and to create square
32290
+ const [x3, y3] = vec.add(centerPoint, vec.add(vec.sub([x1, y1], centerPoint), vec.sub([x2, y2], centerPoint)));
32291
+ if (!showAngles) {
32292
+ return isRightPolygonAngle(angle) ? /*#__PURE__*/React.createElement(RightAngleSquare, {
32293
+ start: [x1, y1],
32294
+ vertex: [x2, y2],
32295
+ end: [x3, y3]
32296
+ }) : null;
32297
+ }
32298
+
32299
+ // Midpoint betwen ends of arc
32300
+ const isOutside = shouldDrawArcOutside([x3, y3], centerPoint, range, polygonLines);
32301
+ const largeArcFlag = isOutside ? 1 : 0;
32302
+ const sweepFlag = isOutside ? 1 : 0;
32303
+ const arc = `M ${x1} ${y1} A ${radius} ${radius} 0 ${largeArcFlag} ${sweepFlag} ${x2} ${y2}`;
32304
+ let angleInDegrees = angle * (180 / Math.PI);
32305
+ // If we have triggered "largArcFlag", the angle should be greater than 180
32306
+ if (isOutside) {
32307
+ angleInDegrees = 360 - angleInDegrees;
32308
+ }
32309
+
32310
+ // Only want to show whole numbers when in angles mode
32311
+ const angleLabelNumber = parseFloat(angleInDegrees.toFixed(snapTo === "angles" ? 0 : 1));
32312
+ const angleLabel = Number.isInteger(angleLabelNumber) ? angleLabelNumber : "≈ " + angleLabelNumber;
32313
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("filter", {
32314
+ id: "background",
32315
+ x: "-5%",
32316
+ width: "110%",
32317
+ y: "0%",
32318
+ height: "100%"
32319
+ }, /*#__PURE__*/React.createElement("feFlood", {
32320
+ floodColor: "#FFF",
32321
+ floodOpacity: "0.5"
32322
+ }), /*#__PURE__*/React.createElement("feComposite", {
32323
+ operator: "over",
32324
+ in: "SourceGraphic"
32325
+ }))), !isOutside && isRightAngle(angle) ? /*#__PURE__*/React.createElement(RightAngleSquare, {
32326
+ start: [x1, y1],
32327
+ vertex: [x2, y2],
32328
+ end: [x3, y3]
32329
+ }) : /*#__PURE__*/React.createElement(Arc, {
32330
+ arc: arc
32331
+ }), /*#__PURE__*/React.createElement(TextLabel, {
32332
+ x: x3,
32333
+ y: y3,
32334
+ attach: y3 - centerY > 0 ? "s" : "n",
32335
+ attachDistance: Math.abs(y3 - centerY) < 0.2 || vec.dist([x3, y3], centerPoint) < 0.3 ? 20 : 10
32336
+ }, angleLabel, "\xB0"));
32337
+ };
32338
+ const Angle = ({
32339
+ vertex,
32340
+ coords,
32341
+ showAngles,
32342
+ allowReflexAngles,
32343
+ range
32344
+ }) => {
32345
+ // Check if the points are clockwise or not, depending on whether we allow reflex angles
32346
+ const areClockwise = clockwise$1([...coords, vertex]);
32347
+ const shouldReverseCoords = areClockwise && !allowReflexAngles;
32348
+
32349
+ // Reverse the coordinates accordingly to ensure the angle is calculated correctly
32350
+ const clockwiseCoords = shouldReverseCoords ? coords : coords.reverse();
32351
+
32352
+ // Calculate the angles between the two points
32353
+ const startAngle = getAngleFromVertex(clockwiseCoords[0], vertex);
32354
+ const endAngle = getAngleFromVertex(clockwiseCoords[1], vertex);
32355
+ const angle = (startAngle + 360 - endAngle) % 360;
32356
+
32357
+ // Check if the angle is reflexive
32358
+ const isReflexive = angle > 180;
32359
+
32360
+ // Break out the necessary points for the arc calculation
32361
+ const [centerX, centerY] = vertex;
32362
+ const [point1, point2] = clockwiseCoords;
32363
+ const [startX, startY] = point1;
32364
+ const [endX, endY] = point2;
32365
+ const a = vec.dist(vertex, point1);
32366
+ const b = vec.dist(vertex, point2);
32367
+
32368
+ // Set the radius of the arc
32369
+ const radius = 2;
32370
+
32371
+ // Calculate the end points of the arc
32372
+ const y1 = centerY + (startY - centerY) / a * radius;
32373
+ const x2 = centerX + (endX - centerX) / b * radius;
32374
+ const x1 = centerX + (startX - centerX) / a * radius;
32375
+ const y2 = centerY + (endY - centerY) / b * radius;
32376
+
32377
+ // Used to calculate the inside of the angle and to create square
32378
+ const [x3, y3] = vec.add(vertex, vec.add(vec.sub([x1, y1], vertex), vec.sub([x2, y2], vertex)));
32379
+
32380
+ // Determine whether the arc should be drawn outside the angle
32381
+ const isOutside = shouldDrawArcOutside([x3, y3], vertex, range, [[vertex, point1], [vertex, point2]]);
32382
+
32383
+ // Determine the flags for the arc
32384
+ // If the angle is outside OR reflexive, we want to draw a large arc
32385
+ // If the angle is outside AND reflexive, we want to sweep the arc
32386
+ const largeArcFlag = isOutside || isReflexive ? 1 : 0;
32387
+ const sweepFlag = isOutside && isReflexive ? 1 : 0;
32388
+
32389
+ // Create the SVG path for the arc
32390
+ const arc = `M ${x1} ${y1} A ${radius} ${radius} 0 ${largeArcFlag} ${sweepFlag} ${x2} ${y2}`;
32391
+
32392
+ // We only ever want to show whole angles
32393
+ const angleLabel = parseFloat(angle.toFixed(0)); // Only want to show whole angles
32394
+
32395
+ // Calculate the text position based on the angle and whether we allow reflex angles
32396
+ // Let's try the angle bisector method to find the midpoint of the arc
32397
+ const [textX, textY] = calculateBisectorPoint(point1, point2, vertex, isReflexive, allowReflexAngles, radius);
32398
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("filter", {
32399
+ id: "background",
32400
+ x: "-5%",
32401
+ width: "110%",
32402
+ y: "0%",
32403
+ height: "100%"
32404
+ }, /*#__PURE__*/React.createElement("feFlood", {
32405
+ floodColor: "#FFF",
32406
+ floodOpacity: "0.5"
32407
+ }), /*#__PURE__*/React.createElement("feComposite", {
32408
+ operator: "over",
32409
+ in: "SourceGraphic"
32410
+ }))), isRightAngle(angle) ? /*#__PURE__*/React.createElement(RightAngleSquare, {
32411
+ start: [x1, y1],
32412
+ vertex: [x2, y2],
32413
+ end: [x3, y3],
32414
+ className: "arc-right-angle"
32415
+ }) : /*#__PURE__*/React.createElement(Arc, {
32416
+ arc: arc,
32417
+ className: "angle-arc"
32418
+ }), showAngles && /*#__PURE__*/React.createElement(TextLabel, {
32419
+ x: textX,
32420
+ y: textY,
32421
+ color: color.blue
32422
+ }, angleLabel, "\xB0"));
32423
+ };
32424
+
32425
+ /**
32426
+ * This is broken out into its own component so that it can be used for an early return
32427
+ * (see line 55 or https://github.com/Khan/perseus/blob/84bbd882b11d16871e1b813a0b901f3b903d5479/packages/perseus/src/widgets/interactive-graphs/graphs/components/angle.tsx#L53-L57)
32428
+ */
32429
+ const RightAngleSquare = ({
32430
+ start: [x1, y1],
32431
+ vertex: [x2, y2],
32432
+ end: [x3, y3],
32433
+ className
32434
+ }) => /*#__PURE__*/React.createElement(MafsCssTransformWrapper, null, /*#__PURE__*/React.createElement("path", {
32435
+ d: `M ${x1} ${y1} L ${x3} ${y3} M ${x3} ${y3} L ${x2} ${y2}`,
32436
+ strokeWidth: 0.02,
32437
+ fill: "none",
32438
+ className: className
32439
+ }));
32440
+
32441
+ // We're conditionally adding the class name here so that we can style the arc differently
32442
+ // based on whether it's an angle or a polygon angle
32443
+ const Arc = ({
32444
+ arc,
32445
+ className
32446
+ }) => {
32447
+ return /*#__PURE__*/React.createElement(MafsCssTransformWrapper, null, /*#__PURE__*/React.createElement("path", {
32448
+ d: arc,
32449
+ strokeWidth: 0.02,
32450
+ fill: "none",
32451
+ className: className
32452
+ }));
32453
+ };
32454
+ const isRightPolygonAngle = angle => Math.abs(angle - Math.PI / 2) < 0.01;
32455
+ const isRightAngle = angle => Math.round(angle) === 90;
32456
+
32457
+ /**
32458
+ * Determines if an angle is an inside (false) or outside (true) angle.
32459
+ * They way, we know to flip the `largeArc` and `sweepArc` flags.
32460
+ * Uses the priciple that a ray from a point inside a polygon will intersect
32461
+ * with an odd number of lines, while a ray from a point outside the polygon
32462
+ * will intersect with an even number of lines.
32463
+ * https://stackoverflow.com/questions/217578/how-can-i-determine-whether-a-2d-point-is-within-a-polygon
32464
+ */
32465
+ const shouldDrawArcOutside = (midpoint, vertex, range, polygonLines) => {
32466
+ // Create a ray from the midpoint (inside angle) to the edge of the range
32467
+ const rangeIntersectionPoint = getIntersectionOfRayWithBox(midpoint, vertex, range);
32468
+ let lineIntersectionCount = 0;
32469
+ polygonLines.forEach(line => segmentsIntersect([vertex, rangeIntersectionPoint], line) && lineIntersectionCount++);
32470
+
32471
+ // If the number of intersections is even, the angle is inside the polygon
32472
+ return !isEven(lineIntersectionCount);
32473
+ };
32474
+ const isEven = n => n % 2 === 0;
32475
+
32476
+ // This function calculates the bisector point of an angle formed by two points
32477
+ // and a vertex. It is used to position the angle label so that it is always
32478
+ // outside the arc at the exact midpoint of the angle.
32479
+ // Reflex angles make use of polar coordinates to determine the correct direction
32480
+ function calculateBisectorPoint(point1, point2, vertex, isReflex, allowReflex, arcRadius) {
32481
+ const [originX, originY] = vertex;
32482
+ const [x1, y1] = point1;
32483
+ const [x2, y2] = point2;
32484
+
32485
+ // Convert the Cartesian Coordinates to vectors
32486
+ const vectorA = [x1 - originX, y1 - originY];
32487
+ const vectorB = [x2 - originX, y2 - originY];
32488
+
32489
+ // Calculate the average angle of the vectors
32490
+ // so that we can point the bisector in the correct direction using polar coordinates
32491
+ const angleA = Math.atan2(vectorA[1], vectorA[0]);
32492
+ const angleB = Math.atan2(vectorB[1], vectorB[0]);
32493
+
32494
+ // Calculate the average of these two angles
32495
+ let averageAngle = (angleA + angleB) / 2;
32496
+ const angleRadians = Math.abs(angleA - angleB);
32497
+
32498
+ // Adjust the average angle to point in the correct direction
32499
+ // and to account for reflex angles and a bug with 180 degrees
32500
+ // that occurs due to our angleOffsetDegrees option in the graph
32501
+ if (allowReflex) {
32502
+ // If the resulting angles are less than 180 degrees,
32503
+ // or if the user has flipped the points on the interactive element
32504
+ // ensure that we have the correct direction for the reflex angle
32505
+ if (angleRadians <= Math.PI && isReflex || angleB > angleA) {
32506
+ averageAngle += Math.PI; // Add 180 degrees
32507
+ }
32508
+ } else {
32509
+ // If the resulting angles are greater than 180 degrees, adjust
32510
+ // to get the correct angle, as we already know that the angle is not reflexive
32511
+ // This fixes a bug that occurs at 180 degrees due to our angleOffsetDegrees option in the graph
32512
+ if (angleRadians > Math.PI) {
32513
+ averageAngle -= Math.PI; // Subract 180 degrees
32514
+ }
32515
+ }
32516
+
32517
+ // Convert the average angle back to Cartesian coordinates
32518
+ const sum = [Math.cos(averageAngle), Math.sin(averageAngle)];
32519
+
32520
+ // Calculate the magnitude of the sum to normalize it
32521
+ const sumMagnitude = Math.sqrt(sum[0] ** 2 + sum[1] ** 2);
32522
+ const bisectorDirection = [sum[0] / sumMagnitude, sum[1] / sumMagnitude];
32523
+
32524
+ // Calculate the initial distance of the bisector direction from the origin
32525
+ const initialDistance = Math.sqrt(bisectorDirection[0] ** 2 + bisectorDirection[1] ** 2);
32526
+
32527
+ // Determine the minimum radius to ensure that the text is always outside the arc
32528
+ const requiredDistance = arcRadius * 1.75;
32529
+ let radius = requiredDistance / initialDistance;
32530
+
32531
+ // Apply radius only if the initial distance is less than required
32532
+ if (initialDistance >= requiredDistance) {
32533
+ radius = 1; // Use the unit vector as is
32534
+ }
32535
+
32536
+ // Scale the bisector direction by the radius
32537
+ const bisectorPoint = [bisectorDirection[0] * radius, bisectorDirection[1] * radius];
32538
+
32539
+ // Add the vertex to the bisector point to get the final position
32540
+ // to ensure that the angle label moves with the interactive element
32541
+ const scaledBisector = vec.add(bisectorPoint, vertex);
32542
+ return scaledBisector;
32543
+ }
32544
+
32236
32545
  /**
32237
32546
  * 44 is touch best practice and AAA compliant for WCAG
32238
32547
  * https://www.w3.org/WAI/WCAG21/Understanding/target-size.html
32239
32548
  */
32240
32549
  const TARGET_SIZE = 44;
32550
+ const REMOVE_BUTTON_ID = "perseus_mafs_remove_button";
32241
32551
 
32242
32552
  // same as pointsFromNormalized in interactive-graph.tsx
32243
32553
  const normalizePoints = (range, step, coordsList, noSnap) => coordsList.map(coords =>
@@ -32268,6 +32578,9 @@ function bound$1({
32268
32578
  const boundingBox = inset(snapStep, range);
32269
32579
  return clampToBox(boundingBox, point);
32270
32580
  }
32581
+ function isUnlimitedGraphState(state) {
32582
+ return state.type === "point" && state.numPoints === "unlimited" || state.type === "polygon" && state.numSides === "unlimited";
32583
+ }
32271
32584
 
32272
32585
  var isProduction = process.env.NODE_ENV === 'production';
32273
32586
  var prefix = 'Invariant failed';
@@ -32434,6 +32747,14 @@ function range(min, max, step = 1) {
32434
32747
  return result;
32435
32748
  }
32436
32749
 
32750
+ function srFormatNumber(a, locale) {
32751
+ // adding zero here converts negative zero to positive zero.
32752
+ return (0 + a).toLocaleString(locale, {
32753
+ maximumFractionDigits: 3,
32754
+ useGrouping: false // no thousands separators
32755
+ });
32756
+ }
32757
+
32437
32758
  // The hitbox size of 48px by 48px is preserved from the legacy interactive
32438
32759
  // graph.
32439
32760
  const hitboxSizePx = 48;
@@ -32441,33 +32762,26 @@ const hitboxSizePx = 48;
32441
32762
  // MovablePointView is a purely presentational component (i.e. it is a pure
32442
32763
  // function with no state or effects) that renders the SVG for a movable point
32443
32764
  // on an interactive graph.
32444
- //
32445
- // It has two modes for managing tabbing / focus: "controlled" (where the caller
32446
- // manages the display of the focus ring, and the point itself cannot be
32447
- // focused) and "uncontrolled" (where the point is focusable, and the browser
32448
- // manages the focus state and styling). For context on why we did this, see
32449
- // the description of https://github.com/Khan/perseus/pull/1240
32450
32765
  const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
32451
32766
  var _Object$entries$find$, _Object$entries$find;
32452
32767
  const {
32453
32768
  range,
32454
32769
  markings,
32455
- showTooltips,
32456
- disableKeyboardInteraction
32770
+ showTooltips
32457
32771
  } = useGraphConfig();
32458
32772
  const {
32459
32773
  point,
32460
32774
  color: color$1 = color.blue,
32461
32775
  dragging,
32462
- focusBehavior,
32463
32776
  cursor,
32777
+ showFocusRing,
32464
32778
  onClick = () => {}
32465
32779
  } = props;
32466
32780
 
32467
32781
  // WB Tooltip requires a color name for the background color.
32468
32782
  // Since the color in props is a hex value, a reverse lookup is needed.
32469
32783
  const wbColorName = (_Object$entries$find$ = (_Object$entries$find = Object.entries(color).find(([_, value]) => value === color$1)) == null ? void 0 : _Object$entries$find[0]) != null ? _Object$entries$find$ : "blue";
32470
- const pointClasses = `movable-point ${dragging ? "movable-point--dragging" : ""} ${focusClass(focusBehavior)}`;
32784
+ const pointClasses = classNames("movable-point", dragging && "movable-point--dragging", showFocusRing && "movable-point--focus");
32471
32785
  const [[x, y]] = useTransformVectorsToPixels(point);
32472
32786
  const [[xMin, xMax], [yMin, yMax]] = range;
32473
32787
  const [[verticalStartX]] = useTransformVectorsToPixels([xMin, 0]);
@@ -32496,15 +32810,6 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
32496
32810
  cursor
32497
32811
  },
32498
32812
  "data-testid": "movable-point",
32499
- tabIndex: disableKeyboardInteraction ? -1 : tabIndex(focusBehavior),
32500
- onFocus: event => {
32501
- const callback = getOnFocusChangeCallback(focusBehavior);
32502
- callback && callback(event, true);
32503
- },
32504
- onBlur: event => {
32505
- const callback = getOnFocusChangeCallback(focusBehavior);
32506
- callback && callback(event, false);
32507
- },
32508
32813
  onClick: onClick
32509
32814
  }, /*#__PURE__*/React.createElement("circle", {
32510
32815
  className: "movable-point-hitbox",
@@ -32541,25 +32846,113 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
32541
32846
  }
32542
32847
  }, svgForPoint) : svgForPoint);
32543
32848
  });
32544
- function focusClass(config) {
32545
- if (config.type === "controlled" && config.showFocusRing) {
32546
- return "movable-point--focus";
32547
- }
32548
- return "";
32849
+
32850
+ // TODO(benchristel): Move this to a more central location if it's reused.
32851
+ // Or install the library.
32852
+ function classNames(...names) {
32853
+ return names.filter(Boolean).join(" ");
32549
32854
  }
32550
- function tabIndex(config) {
32551
- if (config.type === "uncontrolled") {
32552
- return config.tabIndex;
32553
- }
32554
- return undefined;
32855
+
32856
+ function useControlPoint(params) {
32857
+ const {
32858
+ snapStep,
32859
+ disableKeyboardInteraction
32860
+ } = useGraphConfig();
32861
+ const {
32862
+ point,
32863
+ color,
32864
+ cursor,
32865
+ constrain = p => snap(snapStep, p),
32866
+ onMove = noop$1,
32867
+ onClick = noop$1,
32868
+ onFocus = noop$1,
32869
+ onBlur = noop$1,
32870
+ forwardedRef = noop$1
32871
+ } = params;
32872
+ const {
32873
+ strings,
32874
+ locale
32875
+ } = usePerseusI18n();
32876
+ const [focused, setFocused] = useState(false);
32877
+ const focusableHandleRef = useRef(null);
32878
+ useDraggable({
32879
+ gestureTarget: focusableHandleRef,
32880
+ point,
32881
+ onMove,
32882
+ constrainKeyboardMovement: constrain
32883
+ });
32884
+ const visiblePointRef = useRef(null);
32885
+ const {
32886
+ dragging
32887
+ } = useDraggable({
32888
+ gestureTarget: visiblePointRef,
32889
+ point,
32890
+ onMove,
32891
+ constrainKeyboardMovement: constrain
32892
+ });
32893
+ useLayoutEffect(() => {
32894
+ setForwardedRef(forwardedRef, focusableHandleRef.current);
32895
+ }, [forwardedRef]);
32896
+ const focusableHandle = /*#__PURE__*/React.createElement("g", {
32897
+ "data-testid": "movable-point__focusable-handle",
32898
+ className: "movable-point__focusable-handle",
32899
+ tabIndex: disableKeyboardInteraction ? -1 : 0,
32900
+ ref: focusableHandleRef,
32901
+ role: "button",
32902
+ "aria-label": strings.srPointAtCoordinates({
32903
+ x: srFormatNumber(point[X], locale),
32904
+ y: srFormatNumber(point[Y], locale)
32905
+ })
32906
+ // aria-live="assertive" causes the new location of the point to be
32907
+ // announced immediately on move.
32908
+ ,
32909
+ "aria-live": "assertive",
32910
+ onFocus: event => {
32911
+ onFocus(event);
32912
+ setFocused(true);
32913
+ },
32914
+ onBlur: event => {
32915
+ onBlur(event);
32916
+ setFocused(false);
32917
+ }
32918
+ });
32919
+ const visiblePoint = /*#__PURE__*/React.createElement(MovablePointView, {
32920
+ cursor: cursor,
32921
+ onClick: () => {
32922
+ var _focusableHandleRef$c;
32923
+ onClick();
32924
+ (_focusableHandleRef$c = focusableHandleRef.current) == null || _focusableHandleRef$c.focus();
32925
+ },
32926
+ point: point,
32927
+ dragging: dragging,
32928
+ color: color,
32929
+ ref: visiblePointRef,
32930
+ showFocusRing: focused
32931
+ });
32932
+ return {
32933
+ focusableHandle,
32934
+ visiblePoint,
32935
+ focusableHandleRef,
32936
+ visiblePointRef
32937
+ };
32555
32938
  }
32556
- function getOnFocusChangeCallback(config) {
32557
- if (config.type === "uncontrolled") {
32558
- return config.onFocusChange;
32939
+
32940
+ // TODO(benchristel): move this to a more central place if we want to reuse it.
32941
+ function setForwardedRef(ref, value) {
32942
+ if (typeof ref === "function") {
32943
+ ref(value);
32944
+ } else if (ref !== null) {
32945
+ ref.current = value;
32559
32946
  }
32560
- return () => {};
32561
32947
  }
32562
32948
 
32949
+ // This `noop` const is an optimization. Above, we use the forwardedRef
32950
+ // variable, which defaults to `noop`, in a useEffect deps array. Using `noop`
32951
+ // as the default value instead of `() => {}` ensures that the forwardedRef
32952
+ // variable will have the same value on repeated renders, preventing the
32953
+ // useEffect callback from re-running needlessly.
32954
+ const noop$1 = () => {};
32955
+
32563
32956
  const MovableLine = props => {
32564
32957
  const {
32565
32958
  onMoveLine = () => {},
@@ -32583,11 +32976,19 @@ const MovableLine = props => {
32583
32976
  const {
32584
32977
  visiblePoint: visiblePoint1,
32585
32978
  focusableHandle: focusableHandle1
32586
- } = useControlPoint(start, color, p => onMovePoint(0, p));
32979
+ } = useControlPoint({
32980
+ point: start,
32981
+ color,
32982
+ onMove: p => onMovePoint(0, p)
32983
+ });
32587
32984
  const {
32588
32985
  visiblePoint: visiblePoint2,
32589
32986
  focusableHandle: focusableHandle2
32590
- } = useControlPoint(end, color, p => onMovePoint(1, p));
32987
+ } = useControlPoint({
32988
+ point: end,
32989
+ color,
32990
+ onMove: p => onMovePoint(1, p)
32991
+ });
32591
32992
  const line = /*#__PURE__*/React.createElement(Line$1, {
32592
32993
  start: start,
32593
32994
  end: end,
@@ -32597,51 +32998,6 @@ const MovableLine = props => {
32597
32998
  });
32598
32999
  return /*#__PURE__*/React.createElement(React.Fragment, null, focusableHandle1, line, focusableHandle2, visiblePoint1, visiblePoint2);
32599
33000
  };
32600
- function useControlPoint(point, color, onMovePoint) {
32601
- const {
32602
- snapStep,
32603
- disableKeyboardInteraction
32604
- } = useGraphConfig();
32605
- const [focused, setFocused] = useState(false);
32606
- const keyboardHandleRef = useRef(null);
32607
- useDraggable({
32608
- gestureTarget: keyboardHandleRef,
32609
- point,
32610
- onMove: onMovePoint,
32611
- constrainKeyboardMovement: p => snap(snapStep, p)
32612
- });
32613
- const visiblePointRef = useRef(null);
32614
- const {
32615
- dragging
32616
- } = useDraggable({
32617
- gestureTarget: visiblePointRef,
32618
- point,
32619
- onMove: onMovePoint,
32620
- constrainKeyboardMovement: p => snap(snapStep, p)
32621
- });
32622
- const focusableHandle = /*#__PURE__*/React.createElement("g", {
32623
- "data-testid": "movable-point__focusable-handle",
32624
- className: "movable-point__focusable-handle",
32625
- tabIndex: disableKeyboardInteraction ? -1 : 0,
32626
- ref: keyboardHandleRef,
32627
- onFocus: () => setFocused(true),
32628
- onBlur: () => setFocused(false)
32629
- });
32630
- const visiblePoint = /*#__PURE__*/React.createElement(MovablePointView, {
32631
- point: point,
32632
- dragging: dragging,
32633
- color: color,
32634
- ref: visiblePointRef,
32635
- focusBehavior: {
32636
- type: "controlled",
32637
- showFocusRing: focused
32638
- }
32639
- });
32640
- return {
32641
- focusableHandle,
32642
- visiblePoint
32643
- };
32644
- }
32645
33001
  const defaultStroke = "var(--movable-line-stroke-color)";
32646
33002
  const Line$1 = props => {
32647
33003
  const {
@@ -32731,25 +33087,338 @@ function trimRange(range, graphDimensionsInPixels) {
32731
33087
  return inset([graphUnitsToTrimX, graphUnitsToTrimY], range);
32732
33088
  }
32733
33089
 
32734
- const SegmentGraph = props => {
33090
+ const MovablePoint$1 = /*#__PURE__*/React.forwardRef((props, pointRef) => {
32735
33091
  const {
32736
- dispatch
33092
+ visiblePoint,
33093
+ focusableHandle
33094
+ } = useControlPoint(_extends({}, props, {
33095
+ forwardedRef: pointRef
33096
+ }));
33097
+ return /*#__PURE__*/React.createElement(React.Fragment, null, focusableHandle, visiblePoint);
33098
+ });
33099
+
33100
+ function renderAngleGraph(state, dispatch) {
33101
+ return {
33102
+ graph: /*#__PURE__*/React.createElement(AngleGraph, {
33103
+ graphState: state,
33104
+ dispatch: dispatch
33105
+ }),
33106
+ interactiveElementsDescription: null
33107
+ };
33108
+ }
33109
+ function AngleGraph(props) {
33110
+ const {
33111
+ dispatch,
33112
+ graphState
32737
33113
  } = props;
32738
33114
  const {
32739
- coords: segments
32740
- } = props.graphState;
32741
- return /*#__PURE__*/React.createElement(React.Fragment, null, segments == null ? void 0 : segments.map((segment, i) => /*#__PURE__*/React.createElement(MovableLine, {
32742
- key: i,
32743
- points: segment,
32744
- onMoveLine: delta => {
32745
- dispatch(actions.segment.moveLine(i, delta));
32746
- },
32747
- onMovePoint: (endpointIndex, destination) => {
32748
- dispatch(actions.segment.movePointInFigure(i, endpointIndex, destination));
33115
+ graphDimensionsInPixels
33116
+ } = useGraphConfig();
33117
+ const {
33118
+ coords,
33119
+ showAngles,
33120
+ range,
33121
+ allowReflexAngles,
33122
+ snapDegrees
33123
+ } = graphState;
33124
+
33125
+ // Break the coords into the two end points and the center point
33126
+ const endPoints = [coords[0], coords[2]];
33127
+ const centerPoint = coords[1];
33128
+
33129
+ // Convert the vectors to pixels for rendering the svg lines
33130
+ const angleLines = [[centerPoint, endPoints[0]], [centerPoint, endPoints[1]]];
33131
+ const linePixelCoords = [useTransformVectorsToPixels(centerPoint, endPoints[0]), useTransformVectorsToPixels(centerPoint, endPoints[1])];
33132
+
33133
+ // Create the SVG lines for the angle
33134
+ const svgLines = linePixelCoords.map(([startPtPx, endPtPx], i) => {
33135
+ const trimmedRange = trimRange(range, graphDimensionsInPixels);
33136
+ const endExtend = getIntersectionOfRayWithBox(angleLines[i][0], angleLines[i][1], trimmedRange);
33137
+ return /*#__PURE__*/React.createElement("g", {
33138
+ key: `line-${i}`
33139
+ }, /*#__PURE__*/React.createElement(SVGLine, {
33140
+ start: startPtPx,
33141
+ end: endPtPx,
33142
+ style: {
33143
+ stroke: "var(--movable-line-stroke-color)",
33144
+ strokeWidth: "var(--movable-line-stroke-weight)"
33145
+ }
33146
+ }), /*#__PURE__*/React.createElement(Vector, {
33147
+ tail: angleLines[i][1],
33148
+ tip: endExtend,
33149
+ color: "var(--movable-line-stroke-color)"
33150
+ }));
33151
+ });
33152
+
33153
+ // Create the angle indicator parameters
33154
+ const angleParams = {
33155
+ vertex: centerPoint,
33156
+ coords: endPoints,
33157
+ allowReflexAngles: allowReflexAngles || false,
33158
+ // Whether to allow reflex angles or not
33159
+ snapDegrees: snapDegrees || 1,
33160
+ // The multiple of degrees to snap to
33161
+ range: range,
33162
+ showAngles: showAngles || false // Whether to show the angle or not
33163
+ };
33164
+
33165
+ // Render the lines, angle, and then movable points
33166
+ return /*#__PURE__*/React.createElement(React.Fragment, null, svgLines, /*#__PURE__*/React.createElement(Angle, angleParams), /*#__PURE__*/React.createElement(MovablePoint$1, {
33167
+ point: coords[1],
33168
+ constrain: p => p,
33169
+ onMove: destination => dispatch(actions.angle.movePoint(1, destination))
33170
+ }), /*#__PURE__*/React.createElement(MovablePoint$1, {
33171
+ point: coords[0],
33172
+ constrain: getAngleSideConstraint(coords[0], coords[1], snapDegrees || 1),
33173
+ onMove: destination => dispatch(actions.angle.movePoint(0, destination))
33174
+ }), /*#__PURE__*/React.createElement(MovablePoint$1, {
33175
+ point: coords[2],
33176
+ constrain: getAngleSideConstraint(coords[2], coords[1], snapDegrees || 1),
33177
+ onMove: destination => dispatch(actions.angle.movePoint(2, destination))
33178
+ }));
33179
+ }
33180
+ const positiveX = [1, 0];
33181
+ const negativeX = [-1, 0];
33182
+ const positiveY = [0, 1];
33183
+ const negativeY = [0, -1];
33184
+ function getAngleSideConstraint(sidePoint, vertex, snapDegrees) {
33185
+ var _findIntersectionOfRa, _findIntersectionOfRa2, _findIntersectionOfRa3, _findIntersectionOfRa4;
33186
+ const currentAngle = calculateAngleInDegrees(vec.sub(sidePoint, vertex));
33187
+
33188
+ // Find the rays that start at the current point and point up, down, left
33189
+ // and right.
33190
+ const leftRay = [sidePoint, vec.add(sidePoint, negativeX)];
33191
+ const rightRay = [sidePoint, vec.add(sidePoint, positiveX)];
33192
+ const upRay = [sidePoint, vec.add(sidePoint, positiveY)];
33193
+ const downRay = [sidePoint, vec.add(sidePoint, negativeY)];
33194
+
33195
+ // find the angles that lie one snap step clockwise and counter-clockwise
33196
+ // from the current angle. These are the angles to which the side can be
33197
+ // moved.
33198
+ const oneStepCounterClockwise = currentAngle + snapDegrees;
33199
+ const oneStepClockwise = currentAngle - snapDegrees;
33200
+
33201
+ // find the rays that start from the vertex and point in the direction of
33202
+ // the angles we just computed.
33203
+ const counterClockwiseRay = [vertex, vec.add(vertex, polar(1, oneStepCounterClockwise))];
33204
+ const clockwiseRay = [vertex, vec.add(vertex, polar(1, oneStepClockwise))];
33205
+
33206
+ // find the intersections of those rays with the horizontal and vertical
33207
+ // rays extending from the sidePoint. These intersections are the points to
33208
+ // which sidePoint can move that will result in a rotation of `snapDegrees`.
33209
+ const left = (_findIntersectionOfRa = findIntersectionOfRays(leftRay, counterClockwiseRay)) != null ? _findIntersectionOfRa : findIntersectionOfRays(leftRay, clockwiseRay);
33210
+ const right = (_findIntersectionOfRa2 = findIntersectionOfRays(rightRay, counterClockwiseRay)) != null ? _findIntersectionOfRa2 : findIntersectionOfRays(rightRay, clockwiseRay);
33211
+ const up = (_findIntersectionOfRa3 = findIntersectionOfRays(upRay, counterClockwiseRay)) != null ? _findIntersectionOfRa3 : findIntersectionOfRays(upRay, clockwiseRay);
33212
+ const down = (_findIntersectionOfRa4 = findIntersectionOfRays(downRay, counterClockwiseRay)) != null ? _findIntersectionOfRa4 : findIntersectionOfRays(downRay, clockwiseRay);
33213
+ return {
33214
+ up: up != null ? up : sidePoint,
33215
+ down: down != null ? down : sidePoint,
33216
+ left: left != null ? left : sidePoint,
33217
+ right: right != null ? right : sidePoint
33218
+ };
33219
+ }
33220
+
33221
+ function getGradableGraph(state, initialGraph) {
33222
+ if (!state.hasBeenInteractedWith) {
33223
+ return _extends({}, initialGraph);
33224
+ }
33225
+ if (initialGraph.type === "linear-system" && state.type === "linear-system") {
33226
+ return _extends({}, initialGraph, {
33227
+ coords: state.coords
33228
+ });
33229
+ }
33230
+ if (state.type === "segment" && initialGraph.type === "segment") {
33231
+ return _extends({}, initialGraph, {
33232
+ coords: state.coords
33233
+ });
33234
+ }
33235
+ if (state.type === "linear" && initialGraph.type === "linear") {
33236
+ return _extends({}, initialGraph, {
33237
+ coords: state.coords
33238
+ });
33239
+ }
33240
+ if (state.type === "ray" && initialGraph.type === "ray") {
33241
+ return _extends({}, initialGraph, {
33242
+ coords: state.coords
33243
+ });
33244
+ }
33245
+ if (state.type === "polygon" && initialGraph.type === "polygon") {
33246
+ return _extends({}, initialGraph, {
33247
+ coords: state.coords
33248
+ });
33249
+ }
33250
+ if (state.type === "point" && initialGraph.type === "point") {
33251
+ return _extends({}, initialGraph, {
33252
+ coords: state.coords
33253
+ });
33254
+ }
33255
+ if (state.type === "circle" && initialGraph.type === "circle") {
33256
+ return _extends({}, initialGraph, {
33257
+ center: state.center,
33258
+ radius: getRadius(state)
33259
+ });
33260
+ }
33261
+ if (state.type === "quadratic" && initialGraph.type === "quadratic") {
33262
+ return _extends({}, initialGraph, {
33263
+ coords: state.coords
33264
+ });
33265
+ }
33266
+ if (state.type === "sinusoid" && initialGraph.type === "sinusoid") {
33267
+ return _extends({}, initialGraph, {
33268
+ coords: state.coords
33269
+ });
33270
+ }
33271
+ if (state.type === "angle" && initialGraph.type === "angle") {
33272
+ // We're going to reverse the coords for scoring if the angle is clockwise and
33273
+ // we don't allow reflex angles. This is because the angle is largely defined
33274
+ // by the order of the points in the coords array, and we want to maintain
33275
+ // the same angle scoring with the legacy graph (which had a bug).
33276
+ // (LEMS-2190): When we remove the legacy graph, move this logic to the scoring function.
33277
+ const areClockwise = clockwise$1([state.coords[0], state.coords[2], state.coords[1]]);
33278
+ const shouldReverseCoords = areClockwise && !state.allowReflexAngles;
33279
+ const coords = shouldReverseCoords ? state.coords.slice().reverse() : state.coords;
33280
+ return _extends({}, initialGraph, {
33281
+ coords,
33282
+ allowReflexAngles: state.allowReflexAngles
33283
+ });
33284
+ }
33285
+ if (state.type === "none" && initialGraph.type === "none") {
33286
+ return {
33287
+ type: "none"
33288
+ };
33289
+ }
33290
+ throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
33291
+ }
33292
+
33293
+ /**
33294
+ * determine radius of a circle graph
33295
+ *
33296
+ * @param graph - the graph object containing the circle
33297
+ * @returns the radius of the circle
33298
+ */
33299
+ function getRadius(graph) {
33300
+ const [centerX, centerY] = graph.center;
33301
+ const [edgeX, edgeY] = graph.radiusPoint;
33302
+ return Math.sqrt(Math.pow(edgeX - centerX, 2) + Math.pow(edgeY - centerY, 2));
33303
+ }
33304
+
33305
+ function renderCircleGraph(state, dispatch) {
33306
+ return {
33307
+ graph: /*#__PURE__*/React.createElement(CircleGraph, {
33308
+ graphState: state,
33309
+ dispatch: dispatch
33310
+ }),
33311
+ interactiveElementsDescription: null
33312
+ };
33313
+ }
33314
+ function CircleGraph(props) {
33315
+ const {
33316
+ dispatch,
33317
+ graphState
33318
+ } = props;
33319
+ const {
33320
+ center,
33321
+ radiusPoint
33322
+ } = graphState;
33323
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(MovableCircle, {
33324
+ center: center,
33325
+ radius: getRadius(graphState),
33326
+ onMove: c => dispatch(actions.circle.moveCenter(c))
33327
+ }), /*#__PURE__*/React.createElement(MovablePoint$1, {
33328
+ point: radiusPoint,
33329
+ cursor: "ew-resize",
33330
+ onMove: newRadiusPoint => {
33331
+ dispatch(actions.circle.moveRadiusPoint(newRadiusPoint));
32749
33332
  }
32750
- })));
32751
- };
33333
+ }));
33334
+ }
33335
+ function MovableCircle(props) {
33336
+ const {
33337
+ center,
33338
+ radius,
33339
+ onMove
33340
+ } = props;
33341
+ const {
33342
+ snapStep,
33343
+ disableKeyboardInteraction
33344
+ } = useGraphConfig();
33345
+ const draggableRef = useRef(null);
33346
+ const {
33347
+ dragging
33348
+ } = useDraggable({
33349
+ gestureTarget: draggableRef,
33350
+ point: center,
33351
+ onMove,
33352
+ constrainKeyboardMovement: p => snap(snapStep, p)
33353
+ });
33354
+ const [centerPx] = useTransformVectorsToPixels(center);
33355
+ const [radiiPx] = useTransformDimensionsToPixels([radius, radius]);
33356
+ return /*#__PURE__*/React.createElement("g", {
33357
+ ref: draggableRef,
33358
+ tabIndex: disableKeyboardInteraction ? -1 : 0,
33359
+ className: `movable-circle ${dragging ? "movable-circle--dragging" : ""}`
33360
+ }, /*#__PURE__*/React.createElement("ellipse", {
33361
+ className: "focus-ring",
33362
+ cx: centerPx[X],
33363
+ cy: centerPx[Y],
33364
+ rx: radiiPx[X] + 3,
33365
+ ry: radiiPx[Y] + 3
33366
+ }), /*#__PURE__*/React.createElement("ellipse", {
33367
+ className: "circle",
33368
+ cx: centerPx[X],
33369
+ cy: centerPx[Y],
33370
+ rx: radiiPx[X],
33371
+ ry: radiiPx[Y]
33372
+ }), /*#__PURE__*/React.createElement(DragHandle, {
33373
+ center: center
33374
+ }));
33375
+ }
33376
+ const dragHandleDimensions = [24, 14];
33377
+ const dragHandleDotPositions = crossProduct([-4.4, 0, 4.4], [-2.1, 2.1]);
33378
+ function DragHandle(props) {
33379
+ const {
33380
+ center
33381
+ } = props;
33382
+ const cornerRadius = Math.min(...dragHandleDimensions) / 2;
33383
+ const [centerPx] = useTransformVectorsToPixels(center);
33384
+ const topLeft = vec.sub(centerPx, vec.scale(dragHandleDimensions, 0.5));
33385
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("rect", {
33386
+ className: "movable-circle-handle",
33387
+ x: topLeft[X],
33388
+ y: topLeft[Y],
33389
+ width: dragHandleDimensions[X],
33390
+ height: dragHandleDimensions[Y],
33391
+ rx: cornerRadius,
33392
+ ry: cornerRadius
33393
+ }), dragHandleDotPositions.map(offsetPx => {
33394
+ const [xPx, yPx] = vec.add(offsetPx, centerPx);
33395
+ return /*#__PURE__*/React.createElement("circle", {
33396
+ key: `circle-${xPx}-${yPx}`,
33397
+ className: "movable-circle-handle-dot",
33398
+ cx: xPx,
33399
+ cy: yPx
33400
+ });
33401
+ }));
33402
+ }
33403
+ function crossProduct(as, bs) {
33404
+ const result = [];
33405
+ for (const a of as) {
33406
+ for (const b of bs) {
33407
+ result.push([a, b]);
33408
+ }
33409
+ }
33410
+ return result;
33411
+ }
32752
33412
 
33413
+ function renderLinearGraph(state, dispatch) {
33414
+ return {
33415
+ graph: /*#__PURE__*/React.createElement(LinearGraph, {
33416
+ graphState: state,
33417
+ dispatch: dispatch
33418
+ }),
33419
+ interactiveElementsDescription: null
33420
+ };
33421
+ }
32753
33422
  const LinearGraph = (props, key) => {
32754
33423
  const {
32755
33424
  dispatch
@@ -32776,6 +33445,15 @@ const LinearGraph = (props, key) => {
32776
33445
  });
32777
33446
  };
32778
33447
 
33448
+ function renderLinearSystemGraph(state, dispatch) {
33449
+ return {
33450
+ graph: /*#__PURE__*/React.createElement(LinearSystemGraph, {
33451
+ graphState: state,
33452
+ dispatch: dispatch
33453
+ }),
33454
+ interactiveElementsDescription: null
33455
+ };
33456
+ }
32779
33457
  const LinearSystemGraph = props => {
32780
33458
  const {
32781
33459
  dispatch
@@ -32798,405 +33476,122 @@ const LinearSystemGraph = props => {
32798
33476
  })), ";");
32799
33477
  };
32800
33478
 
32801
- const RayGraph = props => {
33479
+ function renderPointGraph(state, dispatch) {
33480
+ return {
33481
+ graph: /*#__PURE__*/React.createElement(PointGraph, {
33482
+ graphState: state,
33483
+ dispatch: dispatch
33484
+ }),
33485
+ interactiveElementsDescription: /*#__PURE__*/React.createElement(PointGraphDescription, {
33486
+ state: state
33487
+ })
33488
+ };
33489
+ }
33490
+ function PointGraph(props) {
33491
+ const numPoints = props.graphState.numPoints;
33492
+ if (numPoints === "unlimited") {
33493
+ return UnlimitedPointGraph(props);
33494
+ }
33495
+ return LimitedPointGraph(props);
33496
+ }
33497
+ function LimitedPointGraph(props) {
32802
33498
  const {
32803
33499
  dispatch
32804
33500
  } = props;
33501
+ return /*#__PURE__*/React.createElement(React.Fragment, null, props.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
33502
+ key: i,
33503
+ point: point,
33504
+ onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination))
33505
+ })));
33506
+ }
33507
+ function UnlimitedPointGraph(props) {
32805
33508
  const {
32806
- coords: line
32807
- } = props.graphState;
32808
- const handleMoveLine = delta => dispatch(actions.ray.moveRay(delta));
32809
- const handleMovePoint = (pointIndex, newPoint) => dispatch(actions.ray.movePoint(pointIndex, newPoint));
32810
-
32811
- // Ray graphs only have one line
32812
- return /*#__PURE__*/React.createElement(MovableLine, {
32813
- points: line,
32814
- onMoveLine: handleMoveLine,
32815
- onMovePoint: handleMovePoint,
32816
- extend: {
32817
- start: false,
32818
- end: true
32819
- }
32820
- });
32821
- };
32822
-
32823
- /**
32824
- * Wrapper with Mafs transform styles for SVG elements.
32825
- * Scales and translates the SVG element to match the Mafs view.
32826
- * Use if you do not need/want to use the hook in `use-transform.ts`.
32827
- */
32828
- const MafsCssTransformWrapper = ({
32829
- children
32830
- }) => /*#__PURE__*/React.createElement("g", {
32831
- style: {
32832
- transform: `var(--mafs-view-transform) var(--mafs-user-transform)`
32833
- }
32834
- }, children);
32835
-
32836
- const _excluded$1 = ["children"];
32837
- const TextLabel = _ref => {
32838
- let {
32839
- children
32840
- } = _ref,
32841
- rest = _objectWithoutPropertiesLoose(_ref, _excluded$1);
32842
- return /*#__PURE__*/React__default.createElement(Text$1, _extends({
32843
- size: 16,
32844
- svgTextProps: {
32845
- filter: "url(#background)",
32846
- fontWeight: "bold"
33509
+ dispatch
33510
+ } = props;
33511
+ const graphConfig = useGraphConfig();
33512
+ const {
33513
+ range: [[minX, maxX], [minY, maxY]]
33514
+ } = graphConfig;
33515
+ const width = maxX - minX;
33516
+ const height = maxY - minY;
33517
+ const [[widthPx, heightPx]] = useTransformDimensionsToPixels([width, height]);
33518
+ const [[left, top]] = useTransformVectorsToPixels([minX, maxY]);
33519
+ const itemsRef = React.useRef([]);
33520
+ React.useEffect(() => {
33521
+ const focusedIndex = props.graphState.focusedPointIndex;
33522
+ if (focusedIndex != null) {
33523
+ var _itemsRef$current$foc;
33524
+ (_itemsRef$current$foc = itemsRef.current[focusedIndex]) == null || _itemsRef$current$foc.focus();
32847
33525
  }
32848
- }, rest), children);
32849
- };
32850
- const SvgDefs = () => /*#__PURE__*/React__default.createElement("defs", null, /*#__PURE__*/React__default.createElement("filter", {
32851
- id: "background",
32852
- x: "-5%",
32853
- width: "110%",
32854
- y: "0%",
32855
- height: "100%"
32856
- }, /*#__PURE__*/React__default.createElement("feFlood", {
32857
- floodColor: "#FFF",
32858
- floodOpacity: "0.64"
32859
- }), /*#__PURE__*/React__default.createElement("feComposite", {
32860
- operator: "over",
32861
- in: "SourceGraphic"
32862
- })));
32863
-
32864
- const PolygonAngle = ({
32865
- centerPoint,
32866
- endPoints,
32867
- range,
32868
- polygonLines,
32869
- showAngles,
32870
- snapTo
32871
- }) => {
32872
- const [centerX, centerY] = centerPoint;
32873
- const areClockwise = clockwise$1([centerPoint, ...endPoints]);
32874
- const [[startX, startY], [endX, endY]] = areClockwise ? endPoints : endPoints.reverse();
32875
- const radius = 0.3;
32876
- const a = vec.dist(centerPoint, endPoints[0]);
32877
- const b = vec.dist(centerPoint, endPoints[1]);
32878
- const c = vec.dist(endPoints[0], endPoints[1]);
32879
-
32880
- // Law of cosines
32881
- const angle = Math.acos((a ** 2 + b ** 2 - c ** 2) / (2 * a * b));
32882
- const y1 = centerY + (startY - centerY) / a * radius;
32883
- const x2 = centerX + (endX - centerX) / b * radius;
32884
- const x1 = centerX + (startX - centerX) / a * radius;
32885
- const y2 = centerY + (endY - centerY) / b * radius;
32886
-
32887
- // Fourth point that would create a rhomboid
32888
- // Used to calculate the inside of the angle and to create square
32889
- const [x3, y3] = vec.add(centerPoint, vec.add(vec.sub([x1, y1], centerPoint), vec.sub([x2, y2], centerPoint)));
32890
- if (!showAngles) {
32891
- return isRightPolygonAngle(angle) ? /*#__PURE__*/React.createElement(RightAngleSquare, {
32892
- start: [x1, y1],
32893
- vertex: [x2, y2],
32894
- end: [x3, y3]
32895
- }) : null;
32896
- }
32897
-
32898
- // Midpoint betwen ends of arc
32899
- const isOutside = shouldDrawArcOutside([x3, y3], centerPoint, range, polygonLines);
32900
- const largeArcFlag = isOutside ? 1 : 0;
32901
- const sweepFlag = isOutside ? 1 : 0;
32902
- const arc = `M ${x1} ${y1} A ${radius} ${radius} 0 ${largeArcFlag} ${sweepFlag} ${x2} ${y2}`;
32903
- let angleInDegrees = angle * (180 / Math.PI);
32904
- // If we have triggered "largArcFlag", the angle should be greater than 180
32905
- if (isOutside) {
32906
- angleInDegrees = 360 - angleInDegrees;
32907
- }
32908
-
32909
- // Only want to show whole numbers when in angles mode
32910
- const angleLabelNumber = parseFloat(angleInDegrees.toFixed(snapTo === "angles" ? 0 : 1));
32911
- const angleLabel = Number.isInteger(angleLabelNumber) ? angleLabelNumber : "≈ " + angleLabelNumber;
32912
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("filter", {
32913
- id: "background",
32914
- x: "-5%",
32915
- width: "110%",
32916
- y: "0%",
32917
- height: "100%"
32918
- }, /*#__PURE__*/React.createElement("feFlood", {
32919
- floodColor: "#FFF",
32920
- floodOpacity: "0.5"
32921
- }), /*#__PURE__*/React.createElement("feComposite", {
32922
- operator: "over",
32923
- in: "SourceGraphic"
32924
- }))), !isOutside && isRightAngle(angle) ? /*#__PURE__*/React.createElement(RightAngleSquare, {
32925
- start: [x1, y1],
32926
- vertex: [x2, y2],
32927
- end: [x3, y3]
32928
- }) : /*#__PURE__*/React.createElement(Arc, {
32929
- arc: arc
32930
- }), /*#__PURE__*/React.createElement(TextLabel, {
32931
- x: x3,
32932
- y: y3,
32933
- attach: y3 - centerY > 0 ? "s" : "n",
32934
- attachDistance: Math.abs(y3 - centerY) < 0.2 || vec.dist([x3, y3], centerPoint) < 0.3 ? 20 : 10
32935
- }, angleLabel, "\xB0"));
32936
- };
32937
- const Angle = ({
32938
- vertex,
32939
- coords,
32940
- showAngles,
32941
- allowReflexAngles,
32942
- range
32943
- }) => {
32944
- // Check if the points are clockwise or not, depending on whether we allow reflex angles
32945
- const areClockwise = clockwise$1([...coords, vertex]);
32946
- const shouldReverseCoords = areClockwise && !allowReflexAngles;
32947
-
32948
- // Reverse the coordinates accordingly to ensure the angle is calculated correctly
32949
- const clockwiseCoords = shouldReverseCoords ? coords : coords.reverse();
32950
-
32951
- // Calculate the angles between the two points
32952
- const startAngle = getAngleFromVertex(clockwiseCoords[0], vertex);
32953
- const endAngle = getAngleFromVertex(clockwiseCoords[1], vertex);
32954
- const angle = (startAngle + 360 - endAngle) % 360;
32955
-
32956
- // Check if the angle is reflexive
32957
- const isReflexive = angle > 180;
32958
-
32959
- // Break out the necessary points for the arc calculation
32960
- const [centerX, centerY] = vertex;
32961
- const [point1, point2] = clockwiseCoords;
32962
- const [startX, startY] = point1;
32963
- const [endX, endY] = point2;
32964
- const a = vec.dist(vertex, point1);
32965
- const b = vec.dist(vertex, point2);
32966
-
32967
- // Set the radius of the arc
32968
- const radius = 2;
32969
-
32970
- // Calculate the end points of the arc
32971
- const y1 = centerY + (startY - centerY) / a * radius;
32972
- const x2 = centerX + (endX - centerX) / b * radius;
32973
- const x1 = centerX + (startX - centerX) / a * radius;
32974
- const y2 = centerY + (endY - centerY) / b * radius;
32975
-
32976
- // Used to calculate the inside of the angle and to create square
32977
- const [x3, y3] = vec.add(vertex, vec.add(vec.sub([x1, y1], vertex), vec.sub([x2, y2], vertex)));
32978
-
32979
- // Determine whether the arc should be drawn outside the angle
32980
- const isOutside = shouldDrawArcOutside([x3, y3], vertex, range, [[vertex, point1], [vertex, point2]]);
32981
-
32982
- // Determine the flags for the arc
32983
- // If the angle is outside OR reflexive, we want to draw a large arc
32984
- // If the angle is outside AND reflexive, we want to sweep the arc
32985
- const largeArcFlag = isOutside || isReflexive ? 1 : 0;
32986
- const sweepFlag = isOutside && isReflexive ? 1 : 0;
32987
-
32988
- // Create the SVG path for the arc
32989
- const arc = `M ${x1} ${y1} A ${radius} ${radius} 0 ${largeArcFlag} ${sweepFlag} ${x2} ${y2}`;
32990
-
32991
- // We only ever want to show whole angles
32992
- const angleLabel = parseFloat(angle.toFixed(0)); // Only want to show whole angles
32993
-
32994
- // Calculate the text position based on the angle and whether we allow reflex angles
32995
- // Let's try the angle bisector method to find the midpoint of the arc
32996
- const [textX, textY] = calculateBisectorPoint(point1, point2, vertex, isReflexive, allowReflexAngles, radius);
32997
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("filter", {
32998
- id: "background",
32999
- x: "-5%",
33000
- width: "110%",
33001
- y: "0%",
33002
- height: "100%"
33003
- }, /*#__PURE__*/React.createElement("feFlood", {
33004
- floodColor: "#FFF",
33005
- floodOpacity: "0.5"
33006
- }), /*#__PURE__*/React.createElement("feComposite", {
33007
- operator: "over",
33008
- in: "SourceGraphic"
33009
- }))), isRightAngle(angle) ? /*#__PURE__*/React.createElement(RightAngleSquare, {
33010
- start: [x1, y1],
33011
- vertex: [x2, y2],
33012
- end: [x3, y3],
33013
- className: "arc-right-angle"
33014
- }) : /*#__PURE__*/React.createElement(Arc, {
33015
- arc: arc,
33016
- className: "angle-arc"
33017
- }), showAngles && /*#__PURE__*/React.createElement(TextLabel, {
33018
- x: textX,
33019
- y: textY,
33020
- color: color.blue
33021
- }, angleLabel, "\xB0"));
33022
- };
33023
-
33024
- /**
33025
- * This is broken out into its own component so that it can be used for an early return
33026
- * (see line 55 or https://github.com/Khan/perseus/blob/84bbd882b11d16871e1b813a0b901f3b903d5479/packages/perseus/src/widgets/interactive-graphs/graphs/components/angle.tsx#L53-L57)
33027
- */
33028
- const RightAngleSquare = ({
33029
- start: [x1, y1],
33030
- vertex: [x2, y2],
33031
- end: [x3, y3],
33032
- className
33033
- }) => /*#__PURE__*/React.createElement(MafsCssTransformWrapper, null, /*#__PURE__*/React.createElement("path", {
33034
- d: `M ${x1} ${y1} L ${x3} ${y3} M ${x3} ${y3} L ${x2} ${y2}`,
33035
- strokeWidth: 0.02,
33036
- fill: "none",
33037
- className: className
33038
- }));
33039
-
33040
- // We're conditionally adding the class name here so that we can style the arc differently
33041
- // based on whether it's an angle or a polygon angle
33042
- const Arc = ({
33043
- arc,
33044
- className
33045
- }) => {
33046
- return /*#__PURE__*/React.createElement(MafsCssTransformWrapper, null, /*#__PURE__*/React.createElement("path", {
33047
- d: arc,
33048
- strokeWidth: 0.02,
33049
- fill: "none",
33050
- className: className
33051
- }));
33052
- };
33053
- const isRightPolygonAngle = angle => Math.abs(angle - Math.PI / 2) < 0.01;
33054
- const isRightAngle = angle => Math.round(angle) === 90;
33055
-
33056
- /**
33057
- * Determines if an angle is an inside (false) or outside (true) angle.
33058
- * They way, we know to flip the `largeArc` and `sweepArc` flags.
33059
- * Uses the priciple that a ray from a point inside a polygon will intersect
33060
- * with an odd number of lines, while a ray from a point outside the polygon
33061
- * will intersect with an even number of lines.
33062
- * https://stackoverflow.com/questions/217578/how-can-i-determine-whether-a-2d-point-is-within-a-polygon
33063
- */
33064
- const shouldDrawArcOutside = (midpoint, vertex, range, polygonLines) => {
33065
- // Create a ray from the midpoint (inside angle) to the edge of the range
33066
- const rangeIntersectionPoint = getIntersectionOfRayWithBox(midpoint, vertex, range);
33067
- let lineIntersectionCount = 0;
33068
- polygonLines.forEach(line => segmentsIntersect([vertex, rangeIntersectionPoint], line) && lineIntersectionCount++);
33069
-
33070
- // If the number of intersections is even, the angle is inside the polygon
33071
- return !isEven(lineIntersectionCount);
33072
- };
33073
- const isEven = n => n % 2 === 0;
33074
-
33075
- // This function calculates the bisector point of an angle formed by two points
33076
- // and a vertex. It is used to position the angle label so that it is always
33077
- // outside the arc at the exact midpoint of the angle.
33078
- // Reflex angles make use of polar coordinates to determine the correct direction
33079
- function calculateBisectorPoint(point1, point2, vertex, isReflex, allowReflex, arcRadius) {
33080
- const [originX, originY] = vertex;
33081
- const [x1, y1] = point1;
33082
- const [x2, y2] = point2;
33083
-
33084
- // Convert the Cartesian Coordinates to vectors
33085
- const vectorA = [x1 - originX, y1 - originY];
33086
- const vectorB = [x2 - originX, y2 - originY];
33087
-
33088
- // Calculate the average angle of the vectors
33089
- // so that we can point the bisector in the correct direction using polar coordinates
33090
- const angleA = Math.atan2(vectorA[1], vectorA[0]);
33091
- const angleB = Math.atan2(vectorB[1], vectorB[0]);
33092
-
33093
- // Calculate the average of these two angles
33094
- let averageAngle = (angleA + angleB) / 2;
33095
- const angleRadians = Math.abs(angleA - angleB);
33096
-
33097
- // Adjust the average angle to point in the correct direction
33098
- // and to account for reflex angles and a bug with 180 degrees
33099
- // that occurs due to our angleOffsetDegrees option in the graph
33100
- if (allowReflex) {
33101
- // If the resulting angles are less than 180 degrees,
33102
- // or if the user has flipped the points on the interactive element
33103
- // ensure that we have the correct direction for the reflex angle
33104
- if (angleRadians <= Math.PI && isReflex || angleB > angleA) {
33105
- averageAngle += Math.PI; // Add 180 degrees
33106
- }
33107
- } else {
33108
- // If the resulting angles are greater than 180 degrees, adjust
33109
- // to get the correct angle, as we already know that the angle is not reflexive
33110
- // This fixes a bug that occurs at 180 degrees due to our angleOffsetDegrees option in the graph
33111
- if (angleRadians > Math.PI) {
33112
- averageAngle -= Math.PI; // Subract 180 degrees
33526
+ }, [props.graphState.focusedPointIndex, itemsRef]);
33527
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("rect", {
33528
+ style: {
33529
+ fill: "rgba(0,0,0,0)",
33530
+ cursor: "crosshair"
33531
+ },
33532
+ width: widthPx,
33533
+ height: heightPx,
33534
+ x: left,
33535
+ y: top,
33536
+ onClick: event => {
33537
+ const elementRect = event.currentTarget.getBoundingClientRect();
33538
+ const x = event.clientX - elementRect.x;
33539
+ const y = event.clientY - elementRect.y;
33540
+ const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
33541
+ dispatch(actions.pointGraph.addPoint(graphCoordinates[0]));
33113
33542
  }
33114
- }
33115
-
33116
- // Convert the average angle back to Cartesian coordinates
33117
- const sum = [Math.cos(averageAngle), Math.sin(averageAngle)];
33118
-
33119
- // Calculate the magnitude of the sum to normalize it
33120
- const sumMagnitude = Math.sqrt(sum[0] ** 2 + sum[1] ** 2);
33121
- const bisectorDirection = [sum[0] / sumMagnitude, sum[1] / sumMagnitude];
33122
-
33123
- // Calculate the initial distance of the bisector direction from the origin
33124
- const initialDistance = Math.sqrt(bisectorDirection[0] ** 2 + bisectorDirection[1] ** 2);
33125
-
33126
- // Determine the minimum radius to ensure that the text is always outside the arc
33127
- const requiredDistance = arcRadius * 1.75;
33128
- let radius = requiredDistance / initialDistance;
33129
-
33130
- // Apply radius only if the initial distance is less than required
33131
- if (initialDistance >= requiredDistance) {
33132
- radius = 1; // Use the unit vector as is
33133
- }
33134
-
33135
- // Scale the bisector direction by the radius
33136
- const bisectorPoint = [bisectorDirection[0] * radius, bisectorDirection[1] * radius];
33137
-
33138
- // Add the vertex to the bisector point to get the final position
33139
- // to ensure that the angle label moves with the interactive element
33140
- const scaledBisector = vec.add(bisectorPoint, vertex);
33141
- return scaledBisector;
33142
- }
33143
-
33144
- const MovablePoint$1 = /*#__PURE__*/React.forwardRef((props, pointRef) => {
33145
- const {
33146
- snapStep
33147
- } = useGraphConfig();
33148
- const elementRef = useRef(null);
33149
- const {
33150
- point,
33151
- onMove = () => {},
33152
- onFocusChange = () => {},
33153
- onClick = () => {},
33154
- cursor,
33155
- color: color$1 = color.blue,
33156
- constrain = p => snap(snapStep, p)
33157
- } = props;
33158
- const {
33159
- dragging
33160
- } = useDraggable({
33161
- gestureTarget: elementRef,
33162
- point,
33163
- onMove,
33164
- constrainKeyboardMovement: constrain
33165
- });
33166
- return /*#__PURE__*/React.createElement(MovablePointView, {
33543
+ }), props.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
33544
+ key: i,
33545
+ point: point,
33546
+ onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
33167
33547
  ref: ref => {
33168
- // Note: This handles the case where pointRef is either a
33169
- // function or a MutableRefObject. Mostly this is to satisfy
33170
- // the type system, the function branch seems to be the only
33171
- // one that occurs
33172
- if (typeof pointRef === "function") {
33173
- pointRef(ref);
33174
- } else if (pointRef !== null) {
33175
- pointRef.current = ref;
33176
- }
33177
-
33178
- // This ref is for the useDraggable and is not passed up the
33179
- // chain
33180
- elementRef.current = ref;
33548
+ itemsRef.current[i] = ref;
33181
33549
  },
33182
- point: point,
33183
- color: color$1,
33184
- dragging: dragging,
33185
- focusBehavior: {
33186
- type: "uncontrolled",
33187
- tabIndex: 0,
33188
- onFocusChange
33550
+ onFocus: () => {
33551
+ dispatch(actions.pointGraph.focusPoint(i));
33189
33552
  },
33190
33553
  onClick: () => {
33191
- var _elementRef$current;
33192
- onClick && onClick();
33193
- (_elementRef$current = elementRef.current) == null || _elementRef$current.focus();
33194
- },
33195
- cursor: cursor
33554
+ dispatch(actions.pointGraph.clickPoint(i));
33555
+ }
33556
+ })));
33557
+ }
33558
+ function PointGraphDescription({
33559
+ state
33560
+ }) {
33561
+ // PointGraphDescription needs to `usePerseusI18n`, so it has to be a
33562
+ // component rather than a function that simply returns a string.
33563
+ const i18n = usePerseusI18n();
33564
+ return describePointGraph(state, i18n);
33565
+ }
33566
+
33567
+ // Exported for testing
33568
+ function describePointGraph(state, i18n) {
33569
+ const {
33570
+ strings,
33571
+ locale
33572
+ } = i18n;
33573
+ if (state.coords.length === 0) {
33574
+ return strings.srNoInteractiveElements;
33575
+ }
33576
+ const pointDescriptions = state.coords.map(([x, y]) => strings.srPointAtCoordinates({
33577
+ x: srFormatNumber(x, locale),
33578
+ y: srFormatNumber(y, locale)
33579
+ }));
33580
+ return strings.srInteractiveElements({
33581
+ elements: pointDescriptions.join(", ")
33196
33582
  });
33197
- });
33583
+ }
33198
33584
 
33199
- const PolygonGraph = props => {
33585
+ function renderPolygonGraph(state, dispatch) {
33586
+ return {
33587
+ graph: /*#__PURE__*/React.createElement(PolygonGraph, {
33588
+ graphState: state,
33589
+ dispatch: dispatch
33590
+ }),
33591
+ interactiveElementsDescription: null
33592
+ };
33593
+ }
33594
+ const LimitedPolygonGraph = props => {
33200
33595
  const [hovered, setHovered] = React.useState(false);
33201
33596
  // This is more so required for the re-rendering that occurs when state
33202
33597
  // updates; specifically with regard to line weighting and polygon focus.
@@ -33307,6 +33702,153 @@ const PolygonGraph = props => {
33307
33702
  }
33308
33703
  })));
33309
33704
  };
33705
+
33706
+ // TODO(catjohnson): reduce redundancy between LimitedPolygonGraph and UnlimitedPolygonGraph
33707
+ // both components are vary similar, however more implementation is needed to be added before
33708
+ // it is clear what can and can't be shared between components.
33709
+ const UnlimitedPolygonGraph = props => {
33710
+ const [hovered, setHovered] = React.useState(false);
33711
+ // This is more so required for the re-rendering that occurs when state
33712
+ // updates; specifically with regard to line weighting and polygon focus.
33713
+ const [focusVisible, setFocusVisible] = React.useState(false);
33714
+ const {
33715
+ dispatch
33716
+ } = props;
33717
+ const {
33718
+ coords,
33719
+ showAngles,
33720
+ showSides,
33721
+ range,
33722
+ snapStep,
33723
+ snapTo = "grid"
33724
+ } = props.graphState;
33725
+ const graphConfig = useGraphConfig();
33726
+
33727
+ // TODO(catjohnson): Explore abstracting this code as it is similar to point.tsx
33728
+ // and hopefully we can cut down ont the unlimited graph redundancy.
33729
+ const {
33730
+ range: [x, y],
33731
+ disableKeyboardInteraction,
33732
+ graphDimensionsInPixels
33733
+ } = graphConfig;
33734
+ const widthPx = graphDimensionsInPixels[0];
33735
+ const heightPx = graphDimensionsInPixels[1];
33736
+ const [[left, top]] = useTransformVectorsToPixels([x[0], y[1]]);
33737
+ const pointRef = React.useRef([]);
33738
+
33739
+ // TODO(benchristel): can the default set of points be removed here? I don't
33740
+ // think coords can be null.
33741
+ const points = coords != null ? coords : [[0, 0]];
33742
+ const polygonRef = React.useRef(null);
33743
+ const dragReferencePoint = points[0];
33744
+ const constrain = ["angles", "sides"].includes(snapTo) ? p => p : p => snap(snapStep, p);
33745
+ const {
33746
+ dragging
33747
+ } = useDraggable({
33748
+ gestureTarget: polygonRef,
33749
+ point: dragReferencePoint,
33750
+ onMove: newPoint => {
33751
+ const delta = vec.sub(newPoint, dragReferencePoint);
33752
+ dispatch(actions.polygon.moveAll(delta));
33753
+ },
33754
+ constrainKeyboardMovement: constrain
33755
+ });
33756
+ const lines = getLines(points);
33757
+ React.useEffect(() => {
33758
+ const focusedIndex = props.graphState.focusedPointIndex;
33759
+ if (focusedIndex != null) {
33760
+ var _pointRef$current$foc;
33761
+ (_pointRef$current$foc = pointRef.current[focusedIndex]) == null || _pointRef$current$foc.focus();
33762
+ }
33763
+ }, [props.graphState.focusedPointIndex, pointRef]);
33764
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("rect", {
33765
+ style: {
33766
+ fill: "rgba(0,0,0,0)",
33767
+ cursor: "crosshair"
33768
+ },
33769
+ width: widthPx,
33770
+ height: heightPx,
33771
+ x: left,
33772
+ y: top,
33773
+ onClick: event => {
33774
+ const elementRect = event.currentTarget.getBoundingClientRect();
33775
+ const x = event.clientX - elementRect.x;
33776
+ const y = event.clientY - elementRect.y;
33777
+ const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
33778
+ dispatch(actions.polygon.addPoint(graphCoordinates[0]));
33779
+ }
33780
+ }), /*#__PURE__*/React.createElement(Polygon, {
33781
+ points: [...points],
33782
+ color: "var(--movable-line-stroke-color)",
33783
+ svgPolygonProps: {
33784
+ strokeWidth: focusVisible ? "var(--movable-line-stroke-weight-active)" : "var(--movable-line-stroke-weight)",
33785
+ style: {
33786
+ fill: "transparent"
33787
+ }
33788
+ }
33789
+ }), props.graphState.coords.map((point, i) => {
33790
+ const pt1 = points.at(i - 1);
33791
+ const pt2 = points[(i + 1) % points.length];
33792
+ if (!pt1 || !pt2) {
33793
+ return null;
33794
+ }
33795
+ return /*#__PURE__*/React.createElement(PolygonAngle, {
33796
+ key: "angle-" + i,
33797
+ centerPoint: point,
33798
+ endPoints: [pt1, pt2],
33799
+ range: range,
33800
+ polygonLines: lines,
33801
+ showAngles: !!showAngles,
33802
+ snapTo: snapTo
33803
+ });
33804
+ }), showSides && lines.map(([start, end], i) => {
33805
+ const [x, y] = vec.midpoint(start, end);
33806
+ const length = parseFloat(vec.dist(start, end).toFixed(snapTo === "sides" ? 0 : 1));
33807
+ return /*#__PURE__*/React.createElement(TextLabel, {
33808
+ key: "side-" + i,
33809
+ x: x,
33810
+ y: y
33811
+ }, !Number.isInteger(length) && "≈ ", length);
33812
+ }), /*#__PURE__*/React.createElement(Polygon, {
33813
+ points: [...points],
33814
+ color: "transparent",
33815
+ svgPolygonProps: {
33816
+ ref: polygonRef,
33817
+ tabIndex: disableKeyboardInteraction ? -1 : 0,
33818
+ strokeWidth: TARGET_SIZE,
33819
+ style: {
33820
+ cursor: dragging ? "grabbing" : "grab",
33821
+ fill: hovered ? "var(--mafs-blue)" : "transparent"
33822
+ },
33823
+ onMouseEnter: () => setHovered(true),
33824
+ onMouseLeave: () => setHovered(false),
33825
+ // Required to remove line weighting when user clicks away
33826
+ // from the focused polygon
33827
+ onKeyDownCapture: () => {
33828
+ setFocusVisible(hasFocusVisible(polygonRef.current));
33829
+ },
33830
+ // Required for lines to darken on focus
33831
+ onFocus: () => setFocusVisible(hasFocusVisible(polygonRef.current)),
33832
+ // Required for line weighting to update on blur. Without this,
33833
+ // the user has to hover over the shape for it to update
33834
+ onBlur: () => setFocusVisible(hasFocusVisible(polygonRef.current)),
33835
+ className: "movable-polygon"
33836
+ }
33837
+ }), props.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
33838
+ key: i,
33839
+ point: point,
33840
+ onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
33841
+ ref: ref => {
33842
+ pointRef.current[i] = ref;
33843
+ },
33844
+ onFocus: () => {
33845
+ dispatch(actions.pointGraph.focusPoint(i));
33846
+ },
33847
+ onClick: () => {
33848
+ dispatch(actions.pointGraph.clickPoint(i));
33849
+ }
33850
+ })));
33851
+ };
33310
33852
  function getLines(points) {
33311
33853
  return points.map((point, i) => {
33312
33854
  const next = points[(i + 1) % points.length];
@@ -33327,190 +33869,20 @@ const hasFocusVisible = element => {
33327
33869
  return matches(":focus");
33328
33870
  }
33329
33871
  };
33872
+ const PolygonGraph = props => {
33873
+ const numSides = props.graphState.numSides;
33874
+ return numSides === "unlimited" ? UnlimitedPolygonGraph(props) : LimitedPolygonGraph(props);
33875
+ };
33330
33876
 
33331
- function getGradableGraph(state, initialGraph) {
33332
- if (!state.hasBeenInteractedWith) {
33333
- return _extends({}, initialGraph);
33334
- }
33335
- if (initialGraph.type === "linear-system" && state.type === "linear-system") {
33336
- return _extends({}, initialGraph, {
33337
- coords: state.coords
33338
- });
33339
- }
33340
- if (state.type === "segment" && initialGraph.type === "segment") {
33341
- return _extends({}, initialGraph, {
33342
- coords: state.coords
33343
- });
33344
- }
33345
- if (state.type === "linear" && initialGraph.type === "linear") {
33346
- return _extends({}, initialGraph, {
33347
- coords: state.coords
33348
- });
33349
- }
33350
- if (state.type === "ray" && initialGraph.type === "ray") {
33351
- return _extends({}, initialGraph, {
33352
- coords: state.coords
33353
- });
33354
- }
33355
- if (state.type === "polygon" && initialGraph.type === "polygon") {
33356
- return _extends({}, initialGraph, {
33357
- coords: state.coords
33358
- });
33359
- }
33360
- if (state.type === "point" && initialGraph.type === "point") {
33361
- return _extends({}, initialGraph, {
33362
- coords: state.coords
33363
- });
33364
- }
33365
- if (state.type === "circle" && initialGraph.type === "circle") {
33366
- return _extends({}, initialGraph, {
33367
- center: state.center,
33368
- radius: getRadius(state)
33369
- });
33370
- }
33371
- if (state.type === "quadratic" && initialGraph.type === "quadratic") {
33372
- return _extends({}, initialGraph, {
33373
- coords: state.coords
33374
- });
33375
- }
33376
- if (state.type === "sinusoid" && initialGraph.type === "sinusoid") {
33377
- return _extends({}, initialGraph, {
33378
- coords: state.coords
33379
- });
33380
- }
33381
- if (state.type === "angle" && initialGraph.type === "angle") {
33382
- // We're going to reverse the coords for scoring if the angle is clockwise and
33383
- // we don't allow reflex angles. This is because the angle is largely defined
33384
- // by the order of the points in the coords array, and we want to maintain
33385
- // the same angle scoring with the legacy graph (which had a bug).
33386
- // (LEMS-2190): When we remove the legacy graph, move this logic to the scoring function.
33387
- const areClockwise = clockwise$1([state.coords[0], state.coords[2], state.coords[1]]);
33388
- const shouldReverseCoords = areClockwise && !state.allowReflexAngles;
33389
- const coords = shouldReverseCoords ? state.coords.slice().reverse() : state.coords;
33390
- return _extends({}, initialGraph, {
33391
- coords,
33392
- allowReflexAngles: state.allowReflexAngles
33393
- });
33394
- }
33395
- if (state.type === "none" && initialGraph.type === "none") {
33396
- return {
33397
- type: "none"
33398
- };
33399
- }
33400
- throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
33401
- }
33402
-
33403
- /**
33404
- * determine radius of a circle graph
33405
- *
33406
- * @param graph - the graph object containing the circle
33407
- * @returns the radius of the circle
33408
- */
33409
- function getRadius(graph) {
33410
- const [centerX, centerY] = graph.center;
33411
- const [edgeX, edgeY] = graph.radiusPoint;
33412
- return Math.sqrt(Math.pow(edgeX - centerX, 2) + Math.pow(edgeY - centerY, 2));
33413
- }
33414
-
33415
- function CircleGraph(props) {
33416
- const {
33417
- dispatch,
33418
- graphState
33419
- } = props;
33420
- const {
33421
- center,
33422
- radiusPoint
33423
- } = graphState;
33424
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(MovableCircle, {
33425
- center: center,
33426
- radius: getRadius(graphState),
33427
- onMove: c => dispatch(actions.circle.moveCenter(c))
33428
- }), /*#__PURE__*/React.createElement(MovablePoint$1, {
33429
- point: radiusPoint,
33430
- cursor: "ew-resize",
33431
- onMove: newRadiusPoint => {
33432
- dispatch(actions.circle.moveRadiusPoint(newRadiusPoint));
33433
- }
33434
- }));
33435
- }
33436
- function MovableCircle(props) {
33437
- const {
33438
- center,
33439
- radius,
33440
- onMove
33441
- } = props;
33442
- const {
33443
- snapStep,
33444
- disableKeyboardInteraction
33445
- } = useGraphConfig();
33446
- const draggableRef = useRef(null);
33447
- const {
33448
- dragging
33449
- } = useDraggable({
33450
- gestureTarget: draggableRef,
33451
- point: center,
33452
- onMove,
33453
- constrainKeyboardMovement: p => snap(snapStep, p)
33454
- });
33455
- const [centerPx] = useTransformVectorsToPixels(center);
33456
- const [radiiPx] = useTransformDimensionsToPixels([radius, radius]);
33457
- return /*#__PURE__*/React.createElement("g", {
33458
- ref: draggableRef,
33459
- tabIndex: disableKeyboardInteraction ? -1 : 0,
33460
- className: `movable-circle ${dragging ? "movable-circle--dragging" : ""}`
33461
- }, /*#__PURE__*/React.createElement("ellipse", {
33462
- className: "focus-ring",
33463
- cx: centerPx[X],
33464
- cy: centerPx[Y],
33465
- rx: radiiPx[X] + 3,
33466
- ry: radiiPx[Y] + 3
33467
- }), /*#__PURE__*/React.createElement("ellipse", {
33468
- className: "circle",
33469
- cx: centerPx[X],
33470
- cy: centerPx[Y],
33471
- rx: radiiPx[X],
33472
- ry: radiiPx[Y]
33473
- }), /*#__PURE__*/React.createElement(DragHandle, {
33474
- center: center
33475
- }));
33476
- }
33477
- const dragHandleDimensions = [24, 14];
33478
- const dragHandleDotPositions = crossProduct([-4.4, 0, 4.4], [-2.1, 2.1]);
33479
- function DragHandle(props) {
33480
- const {
33481
- center
33482
- } = props;
33483
- const cornerRadius = Math.min(...dragHandleDimensions) / 2;
33484
- const [centerPx] = useTransformVectorsToPixels(center);
33485
- const topLeft = vec.sub(centerPx, vec.scale(dragHandleDimensions, 0.5));
33486
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("rect", {
33487
- className: "movable-circle-handle",
33488
- x: topLeft[X],
33489
- y: topLeft[Y],
33490
- width: dragHandleDimensions[X],
33491
- height: dragHandleDimensions[Y],
33492
- rx: cornerRadius,
33493
- ry: cornerRadius
33494
- }), dragHandleDotPositions.map(offsetPx => {
33495
- const [xPx, yPx] = vec.add(offsetPx, centerPx);
33496
- return /*#__PURE__*/React.createElement("circle", {
33497
- key: `circle-${xPx}-${yPx}`,
33498
- className: "movable-circle-handle-dot",
33499
- cx: xPx,
33500
- cy: yPx
33501
- });
33502
- }));
33503
- }
33504
- function crossProduct(as, bs) {
33505
- const result = [];
33506
- for (const a of as) {
33507
- for (const b of bs) {
33508
- result.push([a, b]);
33509
- }
33510
- }
33511
- return result;
33877
+ function renderQuadraticGraph(state, dispatch) {
33878
+ return {
33879
+ graph: /*#__PURE__*/React.createElement(QuadraticGraph, {
33880
+ graphState: state,
33881
+ dispatch: dispatch
33882
+ }),
33883
+ interactiveElementsDescription: null
33884
+ };
33512
33885
  }
33513
-
33514
33886
  function QuadraticGraph(props) {
33515
33887
  const {
33516
33888
  dispatch,
@@ -33565,6 +33937,74 @@ const getQuadraticCoefficients$1 = coords => {
33565
33937
  return [a, b, c];
33566
33938
  };
33567
33939
 
33940
+ function renderRayGraph(state, dispatch) {
33941
+ return {
33942
+ graph: /*#__PURE__*/React.createElement(RayGraph, {
33943
+ graphState: state,
33944
+ dispatch: dispatch
33945
+ }),
33946
+ interactiveElementsDescription: null
33947
+ };
33948
+ }
33949
+ const RayGraph = props => {
33950
+ const {
33951
+ dispatch
33952
+ } = props;
33953
+ const {
33954
+ coords: line
33955
+ } = props.graphState;
33956
+ const handleMoveLine = delta => dispatch(actions.ray.moveRay(delta));
33957
+ const handleMovePoint = (pointIndex, newPoint) => dispatch(actions.ray.movePoint(pointIndex, newPoint));
33958
+
33959
+ // Ray graphs only have one line
33960
+ return /*#__PURE__*/React.createElement(MovableLine, {
33961
+ points: line,
33962
+ onMoveLine: handleMoveLine,
33963
+ onMovePoint: handleMovePoint,
33964
+ extend: {
33965
+ start: false,
33966
+ end: true
33967
+ }
33968
+ });
33969
+ };
33970
+
33971
+ function renderSegmentGraph(state, dispatch) {
33972
+ return {
33973
+ graph: /*#__PURE__*/React.createElement(SegmentGraph, {
33974
+ graphState: state,
33975
+ dispatch: dispatch
33976
+ }),
33977
+ interactiveElementsDescription: null
33978
+ };
33979
+ }
33980
+ const SegmentGraph = props => {
33981
+ const {
33982
+ dispatch
33983
+ } = props;
33984
+ const {
33985
+ coords: segments
33986
+ } = props.graphState;
33987
+ return /*#__PURE__*/React.createElement(React.Fragment, null, segments == null ? void 0 : segments.map((segment, i) => /*#__PURE__*/React.createElement(MovableLine, {
33988
+ key: i,
33989
+ points: segment,
33990
+ onMoveLine: delta => {
33991
+ dispatch(actions.segment.moveLine(i, delta));
33992
+ },
33993
+ onMovePoint: (endpointIndex, destination) => {
33994
+ dispatch(actions.segment.movePointInFigure(i, endpointIndex, destination));
33995
+ }
33996
+ })));
33997
+ };
33998
+
33999
+ function renderSinusoidGraph(state, dispatch) {
34000
+ return {
34001
+ graph: /*#__PURE__*/React.createElement(SinusoidGraph, {
34002
+ graphState: state,
34003
+ dispatch: dispatch
34004
+ }),
34005
+ interactiveElementsDescription: null
34006
+ };
34007
+ }
33568
34008
  function SinusoidGraph(props) {
33569
34009
  const {
33570
34010
  dispatch,
@@ -33640,203 +34080,6 @@ const getSinusoidCoefficients$1 = coords => {
33640
34080
  };
33641
34081
  };
33642
34082
 
33643
- function AngleGraph(props) {
33644
- const {
33645
- dispatch,
33646
- graphState
33647
- } = props;
33648
- const {
33649
- graphDimensionsInPixels
33650
- } = useGraphConfig();
33651
- const {
33652
- coords,
33653
- showAngles,
33654
- range,
33655
- allowReflexAngles,
33656
- snapDegrees
33657
- } = graphState;
33658
-
33659
- // Break the coords into the two end points and the center point
33660
- const endPoints = [coords[0], coords[2]];
33661
- const centerPoint = coords[1];
33662
-
33663
- // Convert the vectors to pixels for rendering the svg lines
33664
- const angleLines = [[centerPoint, endPoints[0]], [centerPoint, endPoints[1]]];
33665
- const linePixelCoords = [useTransformVectorsToPixels(centerPoint, endPoints[0]), useTransformVectorsToPixels(centerPoint, endPoints[1])];
33666
-
33667
- // Create the SVG lines for the angle
33668
- const svgLines = linePixelCoords.map(([startPtPx, endPtPx], i) => {
33669
- const trimmedRange = trimRange(range, graphDimensionsInPixels);
33670
- const endExtend = getIntersectionOfRayWithBox(angleLines[i][0], angleLines[i][1], trimmedRange);
33671
- return /*#__PURE__*/React.createElement("g", {
33672
- key: `line-${i}`
33673
- }, /*#__PURE__*/React.createElement(SVGLine, {
33674
- start: startPtPx,
33675
- end: endPtPx,
33676
- style: {
33677
- stroke: "var(--movable-line-stroke-color)",
33678
- strokeWidth: "var(--movable-line-stroke-weight)"
33679
- }
33680
- }), /*#__PURE__*/React.createElement(Vector, {
33681
- tail: angleLines[i][1],
33682
- tip: endExtend,
33683
- color: "var(--movable-line-stroke-color)"
33684
- }));
33685
- });
33686
-
33687
- // Create the angle indicator parameters
33688
- const angleParams = {
33689
- vertex: centerPoint,
33690
- coords: endPoints,
33691
- allowReflexAngles: allowReflexAngles || false,
33692
- // Whether to allow reflex angles or not
33693
- snapDegrees: snapDegrees || 1,
33694
- // The multiple of degrees to snap to
33695
- range: range,
33696
- showAngles: showAngles || false // Whether to show the angle or not
33697
- };
33698
-
33699
- // Render the lines, angle, and then movable points
33700
- return /*#__PURE__*/React.createElement(React.Fragment, null, svgLines, /*#__PURE__*/React.createElement(Angle, angleParams), /*#__PURE__*/React.createElement(MovablePoint$1, {
33701
- point: coords[1],
33702
- constrain: p => p,
33703
- onMove: destination => dispatch(actions.angle.movePoint(1, destination))
33704
- }), /*#__PURE__*/React.createElement(MovablePoint$1, {
33705
- point: coords[0],
33706
- constrain: getAngleSideConstraint(coords[0], coords[1], snapDegrees || 1),
33707
- onMove: destination => dispatch(actions.angle.movePoint(0, destination))
33708
- }), /*#__PURE__*/React.createElement(MovablePoint$1, {
33709
- point: coords[2],
33710
- constrain: getAngleSideConstraint(coords[2], coords[1], snapDegrees || 1),
33711
- onMove: destination => dispatch(actions.angle.movePoint(2, destination))
33712
- }));
33713
- }
33714
- const positiveX = [1, 0];
33715
- const negativeX = [-1, 0];
33716
- const positiveY = [0, 1];
33717
- const negativeY = [0, -1];
33718
- function getAngleSideConstraint(sidePoint, vertex, snapDegrees) {
33719
- var _findIntersectionOfRa, _findIntersectionOfRa2, _findIntersectionOfRa3, _findIntersectionOfRa4;
33720
- const currentAngle = calculateAngleInDegrees(vec.sub(sidePoint, vertex));
33721
-
33722
- // Find the rays that start at the current point and point up, down, left
33723
- // and right.
33724
- const leftRay = [sidePoint, vec.add(sidePoint, negativeX)];
33725
- const rightRay = [sidePoint, vec.add(sidePoint, positiveX)];
33726
- const upRay = [sidePoint, vec.add(sidePoint, positiveY)];
33727
- const downRay = [sidePoint, vec.add(sidePoint, negativeY)];
33728
-
33729
- // find the angles that lie one snap step clockwise and counter-clockwise
33730
- // from the current angle. These are the angles to which the side can be
33731
- // moved.
33732
- const oneStepCounterClockwise = currentAngle + snapDegrees;
33733
- const oneStepClockwise = currentAngle - snapDegrees;
33734
-
33735
- // find the rays that start from the vertex and point in the direction of
33736
- // the angles we just computed.
33737
- const counterClockwiseRay = [vertex, vec.add(vertex, polar(1, oneStepCounterClockwise))];
33738
- const clockwiseRay = [vertex, vec.add(vertex, polar(1, oneStepClockwise))];
33739
-
33740
- // find the intersections of those rays with the horizontal and vertical
33741
- // rays extending from the sidePoint. These intersections are the points to
33742
- // which sidePoint can move that will result in a rotation of `snapDegrees`.
33743
- const left = (_findIntersectionOfRa = findIntersectionOfRays(leftRay, counterClockwiseRay)) != null ? _findIntersectionOfRa : findIntersectionOfRays(leftRay, clockwiseRay);
33744
- const right = (_findIntersectionOfRa2 = findIntersectionOfRays(rightRay, counterClockwiseRay)) != null ? _findIntersectionOfRa2 : findIntersectionOfRays(rightRay, clockwiseRay);
33745
- const up = (_findIntersectionOfRa3 = findIntersectionOfRays(upRay, counterClockwiseRay)) != null ? _findIntersectionOfRa3 : findIntersectionOfRays(upRay, clockwiseRay);
33746
- const down = (_findIntersectionOfRa4 = findIntersectionOfRays(downRay, counterClockwiseRay)) != null ? _findIntersectionOfRa4 : findIntersectionOfRays(downRay, clockwiseRay);
33747
- return {
33748
- up: up != null ? up : sidePoint,
33749
- down: down != null ? down : sidePoint,
33750
- left: left != null ? left : sidePoint,
33751
- right: right != null ? right : sidePoint
33752
- };
33753
- }
33754
-
33755
- function LimitedPointGraph(props) {
33756
- const {
33757
- dispatch
33758
- } = props;
33759
- return /*#__PURE__*/React.createElement(React.Fragment, null, props.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
33760
- key: i,
33761
- point: point,
33762
- onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination))
33763
- })));
33764
- }
33765
- function UnlimitedPointGraph(props) {
33766
- const {
33767
- dispatch
33768
- } = props;
33769
- const graphState = useGraphConfig();
33770
- const {
33771
- range: [[minX, maxX], [minY, maxY]]
33772
- } = graphState;
33773
- const width = maxX - minX;
33774
- const height = maxY - minY;
33775
- const [[widthPx, heightPx]] = useTransformDimensionsToPixels([width, height]);
33776
- const [[left, top]] = useTransformVectorsToPixels([minX, maxY]);
33777
- const itemsRef = React.useRef([]);
33778
- React.useEffect(() => {
33779
- const focusedIndex = props.graphState.focusedPointIndex;
33780
- if (focusedIndex != null) {
33781
- var _itemsRef$current$foc;
33782
- (_itemsRef$current$foc = itemsRef.current[focusedIndex]) == null || _itemsRef$current$foc.focus();
33783
- }
33784
- }, [props.graphState.focusedPointIndex, itemsRef]);
33785
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("rect", {
33786
- style: {
33787
- fill: "rgba(0,0,0,0)",
33788
- cursor: "crosshair"
33789
- },
33790
- width: widthPx,
33791
- height: heightPx,
33792
- x: left,
33793
- y: top,
33794
- onClick: event => {
33795
- const elementRect = event.currentTarget.getBoundingClientRect();
33796
- const x = event.clientX - elementRect.x;
33797
- const y = event.clientY - elementRect.y;
33798
- const graphCoordinates = pixelsToVectors([[x, y]], graphState);
33799
- dispatch(actions.pointGraph.addPoint(graphCoordinates[0]));
33800
- }
33801
- }), props.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
33802
- key: i,
33803
- point: point,
33804
- onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
33805
- ref: ref => {
33806
- itemsRef.current[i] = ref;
33807
- },
33808
- onFocusChange: (event, isFocused) => {
33809
- if (isFocused) {
33810
- dispatch(actions.pointGraph.focusPoint(i));
33811
- } else {
33812
- var _event$relatedTarget, _event$relatedTarget2;
33813
- if (((_event$relatedTarget = event.relatedTarget) == null ? void 0 : _event$relatedTarget.id) === REMOVE_BUTTON_ID) {
33814
- return;
33815
- // This is an optimization: If the next target
33816
- // is a point then don't blur because it casues
33817
- // the remove button to get taken off the page
33818
- // and then put back on The new point will
33819
- // receive focus and set the correct state in
33820
- // the reducer
33821
- } else if ((_event$relatedTarget2 = event.relatedTarget) != null && _event$relatedTarget2.classList.contains("movable-point")) {
33822
- return;
33823
- }
33824
- dispatch(actions.pointGraph.blurPoint());
33825
- }
33826
- },
33827
- onClick: () => {
33828
- dispatch(actions.pointGraph.clickPoint(i));
33829
- }
33830
- })));
33831
- }
33832
- function PointGraph(props) {
33833
- const numPoints = props.graphState.numPoints;
33834
- if (numPoints === "unlimited") {
33835
- return UnlimitedPointGraph(props);
33836
- }
33837
- return LimitedPointGraph(props);
33838
- }
33839
-
33840
34083
  const protractorImage = "https://ka-perseus-graphie.s3.amazonaws.com/e9d032f2ab8b95979f674fbfa67056442ba1ff6a.png";
33841
34084
 
33842
34085
  // The vector from the center of the protractor to the top left corner of the
@@ -33984,7 +34227,6 @@ function useDraggablePx(args) {
33984
34227
  * - Protractor
33985
34228
  * - Interactive Graph Elements
33986
34229
  */
33987
- const REMOVE_BUTTON_ID = "perseus_mafs_remove_button";
33988
34230
  const MafsGraph = props => {
33989
34231
  var _props$flags;
33990
34232
  const {
@@ -34002,6 +34244,7 @@ const MafsGraph = props => {
34002
34244
  const tickStep = props.step;
34003
34245
  const uniqueId = React.useId();
34004
34246
  const descriptionId = `interactive-graph-description-${uniqueId}`;
34247
+ const interactiveElementsDescriptionId = `interactive-graph-interactive-elements-description-${uniqueId}`;
34005
34248
  const graphRef = React.useRef(null);
34006
34249
  const {
34007
34250
  analytics
@@ -34025,6 +34268,7 @@ const MafsGraph = props => {
34025
34268
  const {
34026
34269
  strings
34027
34270
  } = usePerseusI18n();
34271
+ const interactionPrompt = isUnlimitedGraphState(state) && state.showKeyboardInteractionInvitation;
34028
34272
  useOnMountEffect(() => {
34029
34273
  analytics.onAnalyticsEvent({
34030
34274
  type: "perseus:interactive-graph-widget:rendered",
@@ -34035,6 +34279,13 @@ const MafsGraph = props => {
34035
34279
  }
34036
34280
  });
34037
34281
  });
34282
+ const {
34283
+ graph,
34284
+ interactiveElementsDescription
34285
+ } = renderGraphElements({
34286
+ state,
34287
+ dispatch
34288
+ });
34038
34289
  return /*#__PURE__*/React.createElement(GraphConfigContext.Provider, {
34039
34290
  value: {
34040
34291
  range: state.range,
@@ -34066,7 +34317,7 @@ const MafsGraph = props => {
34066
34317
  handleKeyboardEvent(event, state, dispatch);
34067
34318
  },
34068
34319
  "aria-label": fullGraphAriaLabel,
34069
- "aria-describedby": fullGraphAriaDescription ? descriptionId : undefined,
34320
+ "aria-describedby": describedByIds(fullGraphAriaDescription && descriptionId, interactiveElementsDescription && interactiveElementsDescriptionId),
34070
34321
  ref: graphRef,
34071
34322
  tabIndex: 0,
34072
34323
  onFocus: event => {
@@ -34078,12 +34329,12 @@ const MafsGraph = props => {
34078
34329
  }, fullGraphAriaDescription && /*#__PURE__*/React.createElement(View, {
34079
34330
  id: descriptionId,
34080
34331
  tabIndex: -1,
34081
- style: {
34082
- width: 0,
34083
- height: 0,
34084
- overflow: "hidden"
34085
- }
34086
- }, fullGraphAriaDescription), /*#__PURE__*/React.createElement(LegacyGrid, {
34332
+ className: "mafs-sr-only"
34333
+ }, fullGraphAriaDescription), interactiveElementsDescription && /*#__PURE__*/React.createElement(View, {
34334
+ id: interactiveElementsDescriptionId,
34335
+ tabIndex: -1,
34336
+ className: "mafs-sr-only"
34337
+ }, interactiveElementsDescription), /*#__PURE__*/React.createElement(LegacyGrid, {
34087
34338
  box: props.box,
34088
34339
  backgroundImage: props.backgroundImage
34089
34340
  }), /*#__PURE__*/React.createElement(View, {
@@ -34134,10 +34385,7 @@ const MafsGraph = props => {
34134
34385
  zoom: false,
34135
34386
  width: width,
34136
34387
  height: height
34137
- }, /*#__PURE__*/React.createElement("svg", nestedSVGAttributes, props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), renderGraph({
34138
- state,
34139
- dispatch
34140
- }))))), state.type === "point" && state.showKeyboardInteractionInvitation && /*#__PURE__*/React.createElement(View, {
34388
+ }, /*#__PURE__*/React.createElement("svg", nestedSVGAttributes, props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), graph)))), interactionPrompt && /*#__PURE__*/React.createElement(View, {
34141
34389
  style: {
34142
34390
  textAlign: "center",
34143
34391
  backgroundColor: "white",
@@ -34154,7 +34402,7 @@ const MafsGraph = props => {
34154
34402
  state,
34155
34403
  dispatch,
34156
34404
  width,
34157
- strings
34405
+ perseusStrings: strings
34158
34406
  })));
34159
34407
  };
34160
34408
  const renderPointGraphControls = props => {
@@ -34164,7 +34412,7 @@ const renderPointGraphControls = props => {
34164
34412
  focusedPointIndex
34165
34413
  } = props.state;
34166
34414
  const {
34167
- strings
34415
+ perseusStrings
34168
34416
  } = props;
34169
34417
  const shouldShowRemoveButton = showRemovePointButton && focusedPointIndex !== null;
34170
34418
  return /*#__PURE__*/React.createElement(View, {
@@ -34182,7 +34430,7 @@ const renderPointGraphControls = props => {
34182
34430
  onClick: () => {
34183
34431
  props.dispatch(actions.pointGraph.addPoint([0, 0]));
34184
34432
  }
34185
- }, strings.addPoint), interactionMode === "mouse" && /*#__PURE__*/React.createElement(Button, {
34433
+ }, perseusStrings.addPoint), interactionMode === "mouse" && /*#__PURE__*/React.createElement(Button, {
34186
34434
  id: REMOVE_BUTTON_ID,
34187
34435
  kind: "secondary",
34188
34436
  color: "destructive"
@@ -34195,17 +34443,66 @@ const renderPointGraphControls = props => {
34195
34443
  marginLeft: "20px",
34196
34444
  visibility: shouldShowRemoveButton ? "visible" : "hidden"
34197
34445
  },
34198
- onClick: event => {
34446
+ onClick: _event => {
34199
34447
  props.dispatch(actions.pointGraph.removePoint(props.state.focusedPointIndex));
34200
34448
  }
34201
- }, strings.removePoint));
34449
+ }, perseusStrings.removePoint));
34450
+ };
34451
+
34452
+ /**
34453
+ * TODO(catjohnson): The polygon controls will be vastly different from the point controls.
34454
+ * Will be keeping this function as is for the time being, even if it's a copy of the point graph
34455
+ * controls.
34456
+ */
34457
+ const renderPolygonGraphControls = props => {
34458
+ const {
34459
+ interactionMode,
34460
+ showRemovePointButton,
34461
+ focusedPointIndex
34462
+ } = props.state;
34463
+ const {
34464
+ perseusStrings
34465
+ } = props;
34466
+ const shouldShowRemoveButton = showRemovePointButton && focusedPointIndex !== null;
34467
+ return /*#__PURE__*/React.createElement(View, {
34468
+ style: {
34469
+ flexDirection: "row",
34470
+ width: props.width
34471
+ }
34472
+ }, interactionMode === "keyboard" && /*#__PURE__*/React.createElement(Button, {
34473
+ kind: "secondary",
34474
+ style: {
34475
+ width: "100%",
34476
+ marginLeft: "20px"
34477
+ },
34478
+ tabIndex: 0,
34479
+ onClick: () => {
34480
+ props.dispatch(actions.polygon.addPoint([0, 0]));
34481
+ }
34482
+ }, perseusStrings.addPoint), interactionMode === "mouse" && /*#__PURE__*/React.createElement(Button, {
34483
+ id: REMOVE_BUTTON_ID,
34484
+ kind: "secondary",
34485
+ color: "destructive"
34486
+ // This button is meant to be interacted with by the mouse only
34487
+ // Never allow learners to tab to this button
34488
+ ,
34489
+ tabIndex: -1,
34490
+ style: {
34491
+ width: "100%",
34492
+ marginLeft: "20px",
34493
+ visibility: shouldShowRemoveButton ? "visible" : "hidden"
34494
+ },
34495
+ onClick: _event => {
34496
+ props.dispatch(actions.polygon.removePoint(props.state.focusedPointIndex));
34497
+ }
34498
+ }, perseusStrings.removePoint));
34202
34499
  };
34203
34500
  const renderGraphControls = props => {
34204
34501
  const {
34205
34502
  state,
34206
34503
  dispatch,
34207
34504
  width,
34208
- strings
34505
+ perseusStrings
34209
34506
  } = props;
34210
34507
  const {
34211
34508
  type
@@ -34217,7 +34514,17 @@ const renderGraphControls = props => {
34217
34514
  state,
34218
34515
  dispatch,
34219
34516
  width,
34220
- strings
34517
+ perseusStrings
34518
+ });
34519
+ }
34520
+ return null;
34521
+ case "polygon":
34522
+ if (state.numSides === "unlimited") {
34523
+ return renderPolygonGraphControls({
34524
+ state,
34525
+ dispatch,
34526
+ width,
34527
+ perseusStrings
34221
34528
  });
34222
34529
  }
34223
34530
  return null;
@@ -34226,21 +34533,29 @@ const renderGraphControls = props => {
34226
34533
  }
34227
34534
  };
34228
34535
  function handleFocusEvent(event, state, dispatch) {
34229
- if (state.type === "point" && state.numPoints === "unlimited") {
34536
+ if (isUnlimitedGraphState(state)) {
34230
34537
  if (event.target.classList.contains("mafs-graph") && state.interactionMode === "mouse") {
34231
34538
  dispatch(actions.global.changeKeyboardInvitationVisibility(true));
34232
34539
  }
34233
34540
  }
34234
34541
  }
34235
- function handleBlurEvent(event, state, dispatch) {
34236
- if (state.type === "point" && state.numPoints === "unlimited") {
34542
+ function handleBlurEvent(_event, state, dispatch) {
34543
+ if (isUnlimitedGraphState(state)) {
34237
34544
  dispatch(actions.global.changeKeyboardInvitationVisibility(false));
34238
34545
  }
34239
34546
  }
34240
34547
  function handleKeyboardEvent(event, state, dispatch) {
34241
- if (state.type === "point" && state.numPoints === "unlimited") {
34548
+ if (isUnlimitedGraphState(state)) {
34242
34549
  if (event.key === "Backspace") {
34243
- dispatch(actions.global.deleteIntent());
34550
+ var _document$activeEleme;
34551
+ // TODO(benchristel): Checking classList here is a hack to prevent
34552
+ // points from being deleted if the user presses the backspace key
34553
+ // while the whole graph is focused. Instead of doing this, we
34554
+ // should move the keyboard event handler to the movable point
34555
+ // handle element.
34556
+ if ((_document$activeEleme = document.activeElement) != null && _document$activeEleme.classList.contains("movable-point__focusable-handle")) {
34557
+ dispatch(actions.global.deleteIntent());
34558
+ }
34244
34559
 
34245
34560
  // After removing a point blur
34246
34561
  // It would be nice if this could focus on the graph but doing so
@@ -34303,7 +34618,7 @@ const calculateNestedSVGCoords = (range, width, height) => {
34303
34618
  viewboxY
34304
34619
  };
34305
34620
  };
34306
- const renderGraph = props => {
34621
+ const renderGraphElements = props => {
34307
34622
  const {
34308
34623
  state,
34309
34624
  dispatch
@@ -34313,62 +34628,41 @@ const renderGraph = props => {
34313
34628
  } = state;
34314
34629
  switch (type) {
34315
34630
  case "angle":
34316
- return /*#__PURE__*/React.createElement(AngleGraph, {
34317
- graphState: state,
34318
- dispatch: dispatch
34319
- });
34631
+ return renderAngleGraph(state, dispatch);
34320
34632
  case "segment":
34321
- return /*#__PURE__*/React.createElement(SegmentGraph, {
34322
- graphState: state,
34323
- dispatch: dispatch
34324
- });
34633
+ return renderSegmentGraph(state, dispatch);
34325
34634
  case "linear-system":
34326
- return /*#__PURE__*/React.createElement(LinearSystemGraph, {
34327
- graphState: state,
34328
- dispatch: dispatch
34329
- });
34635
+ return renderLinearSystemGraph(state, dispatch);
34330
34636
  case "linear":
34331
- return /*#__PURE__*/React.createElement(LinearGraph, {
34332
- graphState: state,
34333
- dispatch: dispatch
34334
- });
34637
+ return renderLinearGraph(state, dispatch);
34335
34638
  case "ray":
34336
- return /*#__PURE__*/React.createElement(RayGraph, {
34337
- graphState: state,
34338
- dispatch: dispatch
34339
- });
34639
+ return renderRayGraph(state, dispatch);
34340
34640
  case "polygon":
34341
- return /*#__PURE__*/React.createElement(PolygonGraph, {
34342
- graphState: state,
34343
- dispatch: dispatch
34344
- });
34641
+ return renderPolygonGraph(state, dispatch);
34345
34642
  case "point":
34346
- return /*#__PURE__*/React.createElement(PointGraph, {
34347
- graphState: state,
34348
- dispatch: dispatch
34349
- });
34643
+ return renderPointGraph(state, dispatch);
34350
34644
  case "circle":
34351
- return /*#__PURE__*/React.createElement(CircleGraph, {
34352
- graphState: state,
34353
- dispatch: dispatch
34354
- });
34645
+ return renderCircleGraph(state, dispatch);
34355
34646
  case "quadratic":
34356
- return /*#__PURE__*/React.createElement(QuadraticGraph, {
34357
- graphState: state,
34358
- dispatch: dispatch
34359
- });
34647
+ return renderQuadraticGraph(state, dispatch);
34360
34648
  case "sinusoid":
34361
- return /*#__PURE__*/React.createElement(SinusoidGraph, {
34362
- graphState: state,
34363
- dispatch: dispatch
34364
- });
34649
+ return renderSinusoidGraph(state, dispatch);
34365
34650
  case "none":
34366
- return null;
34651
+ return {
34652
+ graph: null,
34653
+ interactiveElementsDescription: null
34654
+ };
34367
34655
  default:
34368
34656
  throw new UnreachableCaseError(type);
34369
34657
  }
34370
34658
  };
34371
34659
 
34660
+ // Returns a space-separated string like "foo bar" given several optional
34661
+ // string IDs. If all args are falsy, returns undefined.
34662
+ function describedByIds(...args) {
34663
+ return args.filter(Boolean).join(" ") || undefined;
34664
+ }
34665
+
34372
34666
  // Converts the state of a StatefulMafsGraph back to the format used to
34373
34667
  // represent graph state in the widget JSON.
34374
34668
  //
@@ -34483,10 +34777,15 @@ function initializeGraphState(params) {
34483
34777
  case "polygon":
34484
34778
  return _extends({}, shared, {
34485
34779
  type: "polygon",
34780
+ numSides: graph.numSides || 0,
34486
34781
  showAngles: Boolean(graph.showAngles),
34487
34782
  showSides: Boolean(graph.showSides),
34488
34783
  coords: getPolygonCoords(graph, range, step),
34489
- snapTo: (_graph$snapTo = graph.snapTo) != null ? _graph$snapTo : "grid"
34784
+ snapTo: (_graph$snapTo = graph.snapTo) != null ? _graph$snapTo : "grid",
34785
+ focusedPointIndex: null,
34786
+ showRemovePointButton: false,
34787
+ interactionMode: "mouse",
34788
+ showKeyboardInteractionInvitation: false
34490
34789
  });
34491
34790
  case "point":
34492
34791
  return _extends({}, shared, {
@@ -34776,10 +35075,9 @@ function interactiveGraphReducer(state, action) {
34776
35075
  }
34777
35076
  function doDeleteIntent(state, action) {
34778
35077
  // For unlimited point graphs
34779
- if (state.type === "point" && state.numPoints === "unlimited") {
34780
- // if there's a focused point
35078
+ if (isUnlimitedGraphState(state)) {
35079
+ // Remove the last point that was focused, if any
34781
35080
  if (state.focusedPointIndex !== null) {
34782
- // Remove the focused focus
34783
35081
  return doRemovePoint(state, actions.pointGraph.removePoint(state.focusedPointIndex));
34784
35082
  }
34785
35083
  }
@@ -34787,6 +35085,7 @@ function doDeleteIntent(state, action) {
34787
35085
  }
34788
35086
  function doFocusPoint(state, action) {
34789
35087
  switch (state.type) {
35088
+ case "polygon":
34790
35089
  case "point":
34791
35090
  return _extends({}, state, {
34792
35091
  focusedPointIndex: action.index
@@ -34797,6 +35096,7 @@ function doFocusPoint(state, action) {
34797
35096
  }
34798
35097
  function doBlurPoint(state, action) {
34799
35098
  switch (state.type) {
35099
+ case "polygon":
34800
35100
  case "point":
34801
35101
  const nextState = _extends({}, state, {
34802
35102
  showRemovePointButton: false
@@ -34810,10 +35110,7 @@ function doBlurPoint(state, action) {
34810
35110
  }
34811
35111
  }
34812
35112
  function doClickPoint(state, action) {
34813
- if (state.type !== "point") {
34814
- return state;
34815
- }
34816
- if (state.numPoints === "unlimited") {
35113
+ if (isUnlimitedGraphState(state)) {
34817
35114
  return _extends({}, state, {
34818
35115
  focusedPointIndex: action.index,
34819
35116
  showRemovePointButton: true
@@ -34822,10 +35119,7 @@ function doClickPoint(state, action) {
34822
35119
  return state;
34823
35120
  }
34824
35121
  function doChangeInteractionMode(state, action) {
34825
- if (state.type !== "point") {
34826
- return state;
34827
- }
34828
- if (state.numPoints === "unlimited") {
35122
+ if (isUnlimitedGraphState(state)) {
34829
35123
  const nextKeyboardInvitation = action.mode === "keyboard" ? false : state.showKeyboardInteractionInvitation;
34830
35124
  return _extends({}, state, {
34831
35125
  interactionMode: action.mode,
@@ -34835,10 +35129,7 @@ function doChangeInteractionMode(state, action) {
34835
35129
  return state;
34836
35130
  }
34837
35131
  function doChangeKeyboardInvitationVisibility(state, action) {
34838
- if (state.type !== "point") {
34839
- return state;
34840
- }
34841
- if (state.numPoints === "unlimited") {
35132
+ if (isUnlimitedGraphState(state)) {
34842
35133
  return _extends({}, state, {
34843
35134
  showKeyboardInteractionInvitation: action.shouldShow,
34844
35135
  hasBeenInteractedWith: true
@@ -35169,7 +35460,7 @@ function doChangeRange(state, action) {
35169
35460
  });
35170
35461
  }
35171
35462
  function doAddPoint(state, action) {
35172
- if (state.type !== "point") {
35463
+ if (!isUnlimitedGraphState(state)) {
35173
35464
  return state;
35174
35465
  }
35175
35466
  const {
@@ -35193,7 +35484,7 @@ function doAddPoint(state, action) {
35193
35484
  });
35194
35485
  }
35195
35486
  function doRemovePoint(state, action) {
35196
- if (state.type !== "point") {
35487
+ if (!isUnlimitedGraphState(state)) {
35197
35488
  return state;
35198
35489
  }
35199
35490
  let nextFocusedPointIndex;
@@ -37499,9 +37790,6 @@ function shouldUseMafs(mafsFlags, graph) {
37499
37790
  return Boolean(mafsFlags["point"]);
37500
37791
  case "polygon":
37501
37792
  if (graph.numSides === UNLIMITED) {
37502
- // TODO(benchristel): add a feature flag for the "unlimited"
37503
- // case once we've implemented polygon graphs with unlimited
37504
- // sides
37505
37793
  return Boolean(mafsFlags["unlimited-polygon"]);
37506
37794
  }
37507
37795
  return Boolean(mafsFlags["polygon"]);
@@ -37519,974 +37807,15 @@ var InteractiveGraph$1 = {
37519
37807
  validator: interactiveGraphValidator
37520
37808
  };
37521
37809
 
37522
- const black = "#000000";
37523
- const grayDarker = "#333333";
37524
- const grayDark = "#555555";
37525
- const gray = "#999999";
37526
- const grayLight = "#AAAAAA";
37527
- const grayLighter = "#DDDDDD";
37528
- const white = "#FFFFFF";
37529
- const blue = "#005A88";
37530
- const blueDark = "#2C3747";
37531
- const green = "#76A005";
37532
- const red = "#CF5044";
37533
- const yellow = "#FFC40D";
37534
- const orange = "#BF4F04";
37535
- const pink = "#C3325F";
37536
- const purple = "#7A43B6";
37537
- const bodyBackground = "#FFFFFF";
37538
- const textColor = "#444444";
37539
- const linkColor = "#005987";
37540
- const linkColorHover = "#678D00";
37541
- const sansFontFamily = "'Helvetica Neue',Helvetica,Arial,sans-serif";
37542
- const serifFontFamily = "Georgia,'Times New Roman',Times,serif";
37543
- const monoFontFamily = "Monaco,Menlo,Consolas,'Courier New',monospace";
37544
- const baseFontSize = "14px";
37545
- const baseFontFamily = "inherit";
37546
- const baseLineHeight = "20px";
37547
- const altFontFamily = "Georgia,'Times New Roman',Times,serif";
37548
- const headingsFontFamily = "inherit";
37549
- const headingsFontWeight = "bold";
37550
- const headingsColor = "inherit";
37551
- const fontSizeLarge = "18px";
37552
- const fontSizeSmall = "12px";
37553
- const fontSizeMini = "10.5px";
37554
- const paddingLarge = "11px 19px";
37555
- const paddingSmall = "2px 10px";
37556
- const paddingMini = "0 6px";
37557
- const baseBorderRadius = "4px";
37558
- const borderRadiusLarge = "6px";
37559
- const borderRadiusSmall = "3px";
37560
- const tableBackground = "#000000";
37561
- const tableBackgroundAccent = "#F9F9F9";
37562
- const tableBackgroundHover = "#F5F5F5";
37563
- const tableBorder = "#DDDDDD";
37564
- const btnBackground = "#FFFFFF";
37565
- const btnBackgroundHighlight = "rgb(230, 230, 230)";
37566
- const btnBorder = "#CCCCCC";
37567
- const btnPrimaryBackground = "#0088CC";
37568
- const btnPrimaryBackgroundHighlight = "rgb(0, 68, 204)";
37569
- const btnInfoBackground = "#5BC0DE";
37570
- const btnInfoBackgroundHighlight = "#2F96B4";
37571
- const btnSuccessBackground = "#62C462";
37572
- const btnSuccessBackgroundHighlight = "#51A351";
37573
- const btnWarningBackground = "rgb(251, 180, 81)";
37574
- const btnWarningBackgroundHighlight = "#F89406";
37575
- const btnDangerBackground = "#EE5F5B";
37576
- const btnDangerBackgroundHighlight = "#BD362F";
37577
- const btnInverseBackground = "#444444";
37578
- const btnInverseBackgroundHighlight = "#222222";
37579
- const inputBackground = "#FFFFFF";
37580
- const inputBorder = "#CCCCCC";
37581
- const inputBorderRadius = "4px";
37582
- const inputDisabledBackground = "#EEEEEE";
37583
- const formActionsBackground = "#F5F5F5";
37584
- const inputHeight = "30px";
37585
- const dropdownBackground = "#FFFFFF";
37586
- const dropdownBorder = "rgba(0, 0, 0, 0.2)";
37587
- const dropdownDividerTop = "#E5E5E5";
37588
- const dropdownDividerBottom = "#FFFFFF";
37589
- const dropdownLinkColor = "#333333";
37590
- const dropdownLinkColorHover = "#FFFFFF";
37591
- const dropdownLinkColorActive = "#FFFFFF";
37592
- const dropdownLinkBackgroundActive = "#0088CC";
37593
- const dropdownLinkBackgroundHover = "#0088CC";
37594
- const zindexDropdown = 1000;
37595
- const zindexPopover = 1010;
37596
- const zindexTooltip = 1030;
37597
- const zindexFixedNavbar = 1030;
37598
- const zindexHeader = 1060;
37599
- const zindexModalBackdrop = 1070;
37600
- const zindexModal = 1080;
37601
- const zindexAboveModal = 1090;
37602
- const iconSpritePath = "../img/glyphicons-halflings.png";
37603
- const iconWhiteSpritePath = "../img/glyphicons-halflings-white.png";
37604
- const placeholderText = "#999999";
37605
- const hrBorder = "#EEEEEE";
37606
- const horizontalComponentOffset = "180px";
37607
- const wellBackground = "#F5F5F5";
37608
- const navbarCollapseWidth = "979px";
37609
- const navbarCollapseDesktopWidth = "980px";
37610
- const navbarHeight = "40px";
37611
- const navbarBackgroundHighlight = "#FFFFFF";
37612
- const navbarBackground = "rgb(242, 242, 242)";
37613
- const navbarBorder = "rgb(212, 212, 212)";
37614
- const navbarText = "#777777";
37615
- const navbarLinkColor = "#777777";
37616
- const navbarLinkColorHover = "#333333";
37617
- const navbarLinkColorActive = "#555555";
37618
- const navbarLinkBackgroundHover = "#000000";
37619
- const navbarLinkBackgroundActive = "rgb(230, 230, 230)";
37620
- const navbarBrandColor = "#777777";
37621
- const navbarInverseBackground = "#111111";
37622
- const navbarInverseBackgroundHighlight = "#222222";
37623
- const navbarInverseBorder = "#252525";
37624
- const navbarInverseText = "#999999";
37625
- const navbarInverseLinkColor = "#999999";
37626
- const navbarInverseLinkColorHover = "#FFFFFF";
37627
- const navbarInverseLinkColorActive = "#FFFFFF";
37628
- const navbarInverseLinkBackgroundHover = "#000000";
37629
- const navbarInverseLinkBackgroundActive = "#111111";
37630
- const navbarInverseSearchBackground = "rgb(82, 82, 82)";
37631
- const navbarInverseSearchBackgroundFocus = "#FFFFFF";
37632
- const navbarInverseSearchBorder = "#111111";
37633
- const navbarInverseSearchPlaceholderColor = "#CCCCCC";
37634
- const navbarInverseBrandColor = "#999999";
37635
- const paginationBackground = "#FFFFFF";
37636
- const paginationBorder = "#DDDDDD";
37637
- const paginationActiveBackground = "#F5F5F5";
37638
- const heroUnitBackground = "#EEEEEE";
37639
- const heroUnitHeadingColor = "inherit";
37640
- const heroUnitLeadColor = "inherit";
37641
- const warningText = "#C09853";
37642
- const warningBackground = "#FCF8E3";
37643
- const warningBorder = "rgb(251, 239, 213)";
37644
- const errorText = "#B94A48";
37645
- const errorBackground = "#F2DEDE";
37646
- const errorBorder = "rgb(238, 211, 215)";
37647
- const successText = "#468847";
37648
- const successBackground = "#DFF0D8";
37649
- const successBorder = "rgb(212, 232, 197)";
37650
- const infoText = "#4A7C17";
37651
- const infoBackground = "#EDF2DF";
37652
- const infoBorder = "rgb(228, 232, 197)";
37653
- const tooltipColor = "#FFFFFF";
37654
- const tooltipBackground = "#000000";
37655
- const tooltipArrowWidth = "5px";
37656
- const tooltipArrowColor = "#000000";
37657
- const popoverBackground = "#FFFFFF";
37658
- const popoverArrowWidth = "10px";
37659
- const popoverArrowColor = "#FFFFFF";
37660
- const popoverTitleBackground = "rgb(247, 247, 247)";
37661
- const popoverArrowOuterWidth = "11px";
37662
- const popoverArrowOuterColor = "rgba(0, 0, 0, 0.25)";
37663
- const gridColumns = "12";
37664
- const gridColumnWidth = "60px";
37665
- const gridGutterWidth = "20px";
37666
- const gridRowWidth = "940px";
37667
- const gridColumnWidth1200 = "70px";
37668
- const gridGutterWidth1200 = "30px";
37669
- const gridRowWidth1200 = "1170px";
37670
- const gridColumnWidth768 = "42px";
37671
- const gridGutterWidth768 = "20px";
37672
- const gridRowWidth768 = "724px";
37673
- const fluidGridColumnWidth = "6.382978723404255%";
37674
- const fluidGridGutterWidth = "2.127659574468085%";
37675
- const fluidGridColumnWidth1200 = "5.982905982905983%";
37676
- const fluidGridGutterWidth1200 = "2.564102564102564%";
37677
- const fluidGridColumnWidth768 = "5.801104972375691%";
37678
- const fluidGridGutterWidth768 = "2.7624309392265194%";
37679
- const kaGreen = "#71B307";
37680
- const kaGreenLight = "#BED47A";
37681
- const kaBlue = "#314453";
37682
- const kaBlueLight = "#4D6779";
37683
- const grayExtraDark = "#111111";
37684
- const grayExtraLight = "#EEEEEE";
37685
- const graySuperLight = "#F7F7F7";
37686
- const whiteDark = "#FDFDFD";
37687
- const dark = "#21242C";
37688
- const blueDarkUnsaturated = "#3B414E";
37689
- const blueDarkSaturated = "#1F3043";
37690
- const blueLight = "#A9C0D1";
37691
- const greenLight = "#C6D1AD";
37692
- const greenDark = "#356700";
37693
- const yellowGreen = "#9DB63B";
37694
- const okGreen = "#A7CF5B";
37695
- const alertRed = "#C42420";
37696
- const coral = "#EE6666";
37697
- const grayBlue = "#9AB3B9";
37698
- const lightPageBackground = "#FDFDFD";
37699
- const yclaBlue = "#49BAD5";
37700
- const yclaTeal = "#56D0B3";
37701
- const yclaTealDark = "#50C1A7";
37702
- const yclaGreenDark = "#3D9A82";
37703
- const yclaGreen = "#81C262";
37704
- const learnstormBlue = "#4898FC";
37705
- const homepageBlue = "#46A8BF";
37706
- const backgroundBlue = "#00294A";
37707
- const teacherSignupBlue = "#335d7d";
37708
- const homepageGreen = "#2B8E7B";
37709
- const streakColor = "#F79734";
37710
- const facebookColor = "#1A60A2";
37711
- const blueGoogleColor = "#2CAFE2";
37712
- const redGoogleColor = "#BF4434";
37713
- const homepageColor = "#2D585E";
37714
- const lightTextColor = "#898989";
37715
- const fontSizeTiny = "11px";
37716
- const fontSizeNormal = "14px";
37717
- const fontSizeMedium = "16px";
37718
- const fontSizeExtraLarge = "24px";
37719
- const fontSizeHuge = "36px";
37720
- const fontSizeBakana = "48px";
37721
- const fontSizeBananas = "60px";
37722
- const fixedWidthFontFamily = "'Helvetica Neue',Helvetica,Arial,sans-serif";
37723
- const contentPadding = "20px";
37724
- const primaryButtonBackground = "#89B908";
37725
- const primaryButtonBorder = "#76A005";
37726
- const buttonTextColor = "rgb(122, 122, 122)";
37727
- const buttonBorderRadius = "3px";
37728
- const basicBorderColor = "#CCCCCC";
37729
- const minContainerWidth = "1000px";
37730
- const maxContainerWidth = "1200px";
37731
- const formHintColor = "#999999";
37732
- const guidelineExampleText = "#606060";
37733
- const hoverCardWidth = "450px";
37734
- const menuHighlightColor = "#96AB4F";
37735
- const menuHighlightBorderColor = "#76A005";
37736
- const menuActiveTextColor = "#FFFFFF";
37737
- const menuInactiveTextColor = "#444444";
37738
- const menuSubheaderTextColor = "rgb(120, 120, 120)";
37739
- const menuBorderColor = "#94A170";
37740
- const unreadBackgroundColor = "#ECEEF4";
37741
- const menuLineHeight = "18px";
37742
- const menuSubheaderLineHeight = "17px";
37743
- const menuSubheaderFontSize = "9px";
37744
- const notificationBarColor = "#4858A4";
37745
- const modalBodyTopPadding = "20px";
37746
- const modalDialogPadding = "15px";
37747
- const modalSectionPadding = "9px";
37748
- const mathDomain1 = "#11ACCD";
37749
- const mathDomain2 = "#63D9EA";
37750
- const mathDomain3 = "#027d97";
37751
- const mathDomain4 = "#085566";
37752
- const partnerContentDomain1 = "#01A995";
37753
- const partnerContentDomain2 = "#01D1C1";
37754
- const partnerContentDomain3 = "#208170";
37755
- const partnerContentDomain4 = "#144F44";
37756
- const csDomain1 = "#1FAB54";
37757
- const csDomain2 = "#74CF70";
37758
- const csDomain3 = "#0D923F";
37759
- const csDomain4 = "#085E29";
37760
- const economicsDomain1 = "#E07D10";
37761
- const economicsDomain2 = "#FFBE26";
37762
- const economicsDomain3 = "#A75A05";
37763
- const economicsDomain4 = "#953C02";
37764
- const collegeCareersMoreDomain1 = "#01A995";
37765
- const collegeCareersMoreDomain2 = "#01D1C1";
37766
- const collegeCareersMoreDomain3 = "#208170";
37767
- const collegeCareersMoreDomain4 = "#144F44";
37768
- const humanitiesDomain1 = "#E84D39";
37769
- const humanitiesDomain2 = "#FF8482";
37770
- const humanitiesDomain3 = "#BE2612";
37771
- const humanitiesDomain4 = "#8C1C0D";
37772
- const gtpDomain1 = "#1865F2";
37773
- const gtpDomain2 = "#1865F2";
37774
- const gtpDomain3 = "#1865F2";
37775
- const gtpDomain4 = "#1865F2";
37776
- const scienceDomain1 = "#CA337C";
37777
- const scienceDomain2 = "#FF92C6";
37778
- const scienceDomain3 = "#9E034E";
37779
- const scienceDomain4 = "#6B0235";
37780
- const testPrepDomain1 = "#1865F2";
37781
- const testPrepDomain2 = "#1865F2";
37782
- const testPrepDomain3 = "#1865F2";
37783
- const testPrepDomain4 = "#1865F2";
37784
- const defaultDomain1 = "#1865F2";
37785
- const defaultDomain2 = "#66afe9";
37786
- const defaultDomain3 = "#1152b0";
37787
- const defaultDomain4 = "#0d3d82";
37788
- const defaultDomainColor = "#314453";
37789
- const defaultSubjectColor = "#4D6779";
37790
- const defaultTopicColor = "#6A8DA6";
37791
- const defaultHoverColor = "#314453";
37792
- const defaultLinkColor = "#314453";
37793
- const defaultLinkHoverColor = "#6A8DA6";
37794
- const scienceDomainColor = "#94424F";
37795
- const scienceSubjectColor = "#9D4A5A";
37796
- const scienceTopicColor = "#C55F73";
37797
- const scienceHoverColor = "#94424F";
37798
- const scienceLinkColor = "#94424F";
37799
- const scienceLinkHoverColor = "#C55F73";
37800
- const humanitiesDomainColor = "#AD3434";
37801
- const humanitiesSubjectColor = "#C13B31";
37802
- const humanitiesTopicColor = "#D24A45";
37803
- const humanitiesHoverColor = "#AD3434";
37804
- const humanitiesLinkColor = "#AD3434";
37805
- const humanitiesLinkHoverColor = "#D24A45";
37806
- const economicsDomainColor = "#B77033";
37807
- const economicsSubjectColor = "#BF7B34";
37808
- const economicsTopicColor = "#D1933B";
37809
- const economicsHoverColor = "#B77033";
37810
- const economicsLinkColor = "#B77033";
37811
- const economicsLinkHoverColor = "#D1933B";
37812
- const csDomainColor = "#437A39";
37813
- const csSubjectColor = "#53893E";
37814
- const csTopicColor = "#689B51";
37815
- const csHoverColor = "#437A39";
37816
- const csLinkColor = "#437A39";
37817
- const csLinkHoverColor = "#689B51";
37818
- const partnerContentDomainColor = "#218270";
37819
- const partnerContentSubjectColor = "#2C8D7B";
37820
- const partnerContentTopicColor = "#329A86";
37821
- const partnerContentHoverColor = "#218270";
37822
- const partnerContentLinkColor = "#218270";
37823
- const partnerContentLinkHoverColor = "#329A86";
37824
- const essayDomainColor = "#0071B3";
37825
- const mathDomainColor = "#1C758A";
37826
- const mathSubjectColor = "#46A8BF";
37827
- const mathTopicColor = "#4FBAD4";
37828
- const mathHoverColor = "#1C758A";
37829
- const mathLinkColor = "#1C758A";
37830
- const mathLinkHoverColor = "#4FBAD4";
37831
- const testPrepDomainColor = "#644172";
37832
- const testPrepSubjectColor = "#7E5F8E";
37833
- const testPrepTopicColor = "#9A72AC";
37834
- const testPrepHoverColor = "#644172";
37835
- const testPrepLinkColor = "#644172";
37836
- const testPrepLinkHoverColor = "#9A72AC";
37837
- const collegeCareersMoreDomainColor = "#218270";
37838
- const collegeCareersMoreSubjectColor = "#2C8D7B";
37839
- const collegeCareersMoreTopicColor = "#329A86";
37840
- const collegeCareersMoreHoverColor = "#218270";
37841
- const collegeCareersMoreLinkColor = "#218270";
37842
- const collegeCareersMoreLinkHoverColor = "#329A86";
37843
- const colorProficient = "#1C758A";
37844
- const colorPracticed = "rgb(158, 221, 235)";
37845
- const colorMastery1 = "rgb(90, 197, 221)";
37846
- const colorMastery2 = "rgb(42, 174, 203)";
37847
- const colorMastery3 = "#1C758A";
37848
- const colorSuggested = "#73982C";
37849
- const colorReview = "#E35D04";
37850
- const colorNotStarted = "#CCCCCC";
37851
- const colorStarted = "#C7E5F7";
37852
- const colorStruggling = "#C30202";
37853
- const colorPersonal = "#00809C";
37854
- const colorPractice = "#96D9FF";
37855
- const colorEnergyPoints = "#005987";
37856
- const pureSmMin = "568px";
37857
- const pureMdMin = "768px";
37858
- const pureLgMin = "1024px";
37859
- const pureXlMin = "1200px";
37860
- const pureXsMax = "567px";
37861
- const pureSmMax = "767px";
37862
- const pureMdMax = "1023px";
37863
- const pureLgMax = "1199px";
37864
-
37865
- var globalConstants = /*#__PURE__*/Object.freeze({
37866
- __proto__: null,
37867
- black: black,
37868
- grayDarker: grayDarker,
37869
- grayDark: grayDark,
37870
- gray: gray,
37871
- grayLight: grayLight,
37872
- grayLighter: grayLighter,
37873
- white: white,
37874
- blue: blue,
37875
- blueDark: blueDark,
37876
- green: green,
37877
- red: red,
37878
- yellow: yellow,
37879
- orange: orange,
37880
- pink: pink,
37881
- purple: purple,
37882
- bodyBackground: bodyBackground,
37883
- textColor: textColor,
37884
- linkColor: linkColor,
37885
- linkColorHover: linkColorHover,
37886
- sansFontFamily: sansFontFamily,
37887
- serifFontFamily: serifFontFamily,
37888
- monoFontFamily: monoFontFamily,
37889
- baseFontSize: baseFontSize,
37890
- baseFontFamily: baseFontFamily,
37891
- baseLineHeight: baseLineHeight,
37892
- altFontFamily: altFontFamily,
37893
- headingsFontFamily: headingsFontFamily,
37894
- headingsFontWeight: headingsFontWeight,
37895
- headingsColor: headingsColor,
37896
- fontSizeLarge: fontSizeLarge,
37897
- fontSizeSmall: fontSizeSmall,
37898
- fontSizeMini: fontSizeMini,
37899
- paddingLarge: paddingLarge,
37900
- paddingSmall: paddingSmall,
37901
- paddingMini: paddingMini,
37902
- baseBorderRadius: baseBorderRadius,
37903
- borderRadiusLarge: borderRadiusLarge,
37904
- borderRadiusSmall: borderRadiusSmall,
37905
- tableBackground: tableBackground,
37906
- tableBackgroundAccent: tableBackgroundAccent,
37907
- tableBackgroundHover: tableBackgroundHover,
37908
- tableBorder: tableBorder,
37909
- btnBackground: btnBackground,
37910
- btnBackgroundHighlight: btnBackgroundHighlight,
37911
- btnBorder: btnBorder,
37912
- btnPrimaryBackground: btnPrimaryBackground,
37913
- btnPrimaryBackgroundHighlight: btnPrimaryBackgroundHighlight,
37914
- btnInfoBackground: btnInfoBackground,
37915
- btnInfoBackgroundHighlight: btnInfoBackgroundHighlight,
37916
- btnSuccessBackground: btnSuccessBackground,
37917
- btnSuccessBackgroundHighlight: btnSuccessBackgroundHighlight,
37918
- btnWarningBackground: btnWarningBackground,
37919
- btnWarningBackgroundHighlight: btnWarningBackgroundHighlight,
37920
- btnDangerBackground: btnDangerBackground,
37921
- btnDangerBackgroundHighlight: btnDangerBackgroundHighlight,
37922
- btnInverseBackground: btnInverseBackground,
37923
- btnInverseBackgroundHighlight: btnInverseBackgroundHighlight,
37924
- inputBackground: inputBackground,
37925
- inputBorder: inputBorder,
37926
- inputBorderRadius: inputBorderRadius,
37927
- inputDisabledBackground: inputDisabledBackground,
37928
- formActionsBackground: formActionsBackground,
37929
- inputHeight: inputHeight,
37930
- dropdownBackground: dropdownBackground,
37931
- dropdownBorder: dropdownBorder,
37932
- dropdownDividerTop: dropdownDividerTop,
37933
- dropdownDividerBottom: dropdownDividerBottom,
37934
- dropdownLinkColor: dropdownLinkColor,
37935
- dropdownLinkColorHover: dropdownLinkColorHover,
37936
- dropdownLinkColorActive: dropdownLinkColorActive,
37937
- dropdownLinkBackgroundActive: dropdownLinkBackgroundActive,
37938
- dropdownLinkBackgroundHover: dropdownLinkBackgroundHover,
37939
- zindexDropdown: zindexDropdown,
37940
- zindexPopover: zindexPopover,
37941
- zindexTooltip: zindexTooltip,
37942
- zindexFixedNavbar: zindexFixedNavbar,
37943
- zindexHeader: zindexHeader,
37944
- zindexModalBackdrop: zindexModalBackdrop,
37945
- zindexModal: zindexModal,
37946
- zindexAboveModal: zindexAboveModal,
37947
- iconSpritePath: iconSpritePath,
37948
- iconWhiteSpritePath: iconWhiteSpritePath,
37949
- placeholderText: placeholderText,
37950
- hrBorder: hrBorder,
37951
- horizontalComponentOffset: horizontalComponentOffset,
37952
- wellBackground: wellBackground,
37953
- navbarCollapseWidth: navbarCollapseWidth,
37954
- navbarCollapseDesktopWidth: navbarCollapseDesktopWidth,
37955
- navbarHeight: navbarHeight,
37956
- navbarBackgroundHighlight: navbarBackgroundHighlight,
37957
- navbarBackground: navbarBackground,
37958
- navbarBorder: navbarBorder,
37959
- navbarText: navbarText,
37960
- navbarLinkColor: navbarLinkColor,
37961
- navbarLinkColorHover: navbarLinkColorHover,
37962
- navbarLinkColorActive: navbarLinkColorActive,
37963
- navbarLinkBackgroundHover: navbarLinkBackgroundHover,
37964
- navbarLinkBackgroundActive: navbarLinkBackgroundActive,
37965
- navbarBrandColor: navbarBrandColor,
37966
- navbarInverseBackground: navbarInverseBackground,
37967
- navbarInverseBackgroundHighlight: navbarInverseBackgroundHighlight,
37968
- navbarInverseBorder: navbarInverseBorder,
37969
- navbarInverseText: navbarInverseText,
37970
- navbarInverseLinkColor: navbarInverseLinkColor,
37971
- navbarInverseLinkColorHover: navbarInverseLinkColorHover,
37972
- navbarInverseLinkColorActive: navbarInverseLinkColorActive,
37973
- navbarInverseLinkBackgroundHover: navbarInverseLinkBackgroundHover,
37974
- navbarInverseLinkBackgroundActive: navbarInverseLinkBackgroundActive,
37975
- navbarInverseSearchBackground: navbarInverseSearchBackground,
37976
- navbarInverseSearchBackgroundFocus: navbarInverseSearchBackgroundFocus,
37977
- navbarInverseSearchBorder: navbarInverseSearchBorder,
37978
- navbarInverseSearchPlaceholderColor: navbarInverseSearchPlaceholderColor,
37979
- navbarInverseBrandColor: navbarInverseBrandColor,
37980
- paginationBackground: paginationBackground,
37981
- paginationBorder: paginationBorder,
37982
- paginationActiveBackground: paginationActiveBackground,
37983
- heroUnitBackground: heroUnitBackground,
37984
- heroUnitHeadingColor: heroUnitHeadingColor,
37985
- heroUnitLeadColor: heroUnitLeadColor,
37986
- warningText: warningText,
37987
- warningBackground: warningBackground,
37988
- warningBorder: warningBorder,
37989
- errorText: errorText,
37990
- errorBackground: errorBackground,
37991
- errorBorder: errorBorder,
37992
- successText: successText,
37993
- successBackground: successBackground,
37994
- successBorder: successBorder,
37995
- infoText: infoText,
37996
- infoBackground: infoBackground,
37997
- infoBorder: infoBorder,
37998
- tooltipColor: tooltipColor,
37999
- tooltipBackground: tooltipBackground,
38000
- tooltipArrowWidth: tooltipArrowWidth,
38001
- tooltipArrowColor: tooltipArrowColor,
38002
- popoverBackground: popoverBackground,
38003
- popoverArrowWidth: popoverArrowWidth,
38004
- popoverArrowColor: popoverArrowColor,
38005
- popoverTitleBackground: popoverTitleBackground,
38006
- popoverArrowOuterWidth: popoverArrowOuterWidth,
38007
- popoverArrowOuterColor: popoverArrowOuterColor,
38008
- gridColumns: gridColumns,
38009
- gridColumnWidth: gridColumnWidth,
38010
- gridGutterWidth: gridGutterWidth,
38011
- gridRowWidth: gridRowWidth,
38012
- gridColumnWidth1200: gridColumnWidth1200,
38013
- gridGutterWidth1200: gridGutterWidth1200,
38014
- gridRowWidth1200: gridRowWidth1200,
38015
- gridColumnWidth768: gridColumnWidth768,
38016
- gridGutterWidth768: gridGutterWidth768,
38017
- gridRowWidth768: gridRowWidth768,
38018
- fluidGridColumnWidth: fluidGridColumnWidth,
38019
- fluidGridGutterWidth: fluidGridGutterWidth,
38020
- fluidGridColumnWidth1200: fluidGridColumnWidth1200,
38021
- fluidGridGutterWidth1200: fluidGridGutterWidth1200,
38022
- fluidGridColumnWidth768: fluidGridColumnWidth768,
38023
- fluidGridGutterWidth768: fluidGridGutterWidth768,
38024
- kaGreen: kaGreen,
38025
- kaGreenLight: kaGreenLight,
38026
- kaBlue: kaBlue,
38027
- kaBlueLight: kaBlueLight,
38028
- grayExtraDark: grayExtraDark,
38029
- grayExtraLight: grayExtraLight,
38030
- graySuperLight: graySuperLight,
38031
- whiteDark: whiteDark,
38032
- dark: dark,
38033
- blueDarkUnsaturated: blueDarkUnsaturated,
38034
- blueDarkSaturated: blueDarkSaturated,
38035
- blueLight: blueLight,
38036
- greenLight: greenLight,
38037
- greenDark: greenDark,
38038
- yellowGreen: yellowGreen,
38039
- okGreen: okGreen,
38040
- alertRed: alertRed,
38041
- coral: coral,
38042
- grayBlue: grayBlue,
38043
- lightPageBackground: lightPageBackground,
38044
- yclaBlue: yclaBlue,
38045
- yclaTeal: yclaTeal,
38046
- yclaTealDark: yclaTealDark,
38047
- yclaGreenDark: yclaGreenDark,
38048
- yclaGreen: yclaGreen,
38049
- learnstormBlue: learnstormBlue,
38050
- homepageBlue: homepageBlue,
38051
- backgroundBlue: backgroundBlue,
38052
- teacherSignupBlue: teacherSignupBlue,
38053
- homepageGreen: homepageGreen,
38054
- streakColor: streakColor,
38055
- facebookColor: facebookColor,
38056
- blueGoogleColor: blueGoogleColor,
38057
- redGoogleColor: redGoogleColor,
38058
- homepageColor: homepageColor,
38059
- lightTextColor: lightTextColor,
38060
- fontSizeTiny: fontSizeTiny,
38061
- fontSizeNormal: fontSizeNormal,
38062
- fontSizeMedium: fontSizeMedium,
38063
- fontSizeExtraLarge: fontSizeExtraLarge,
38064
- fontSizeHuge: fontSizeHuge,
38065
- fontSizeBakana: fontSizeBakana,
38066
- fontSizeBananas: fontSizeBananas,
38067
- fixedWidthFontFamily: fixedWidthFontFamily,
38068
- contentPadding: contentPadding,
38069
- primaryButtonBackground: primaryButtonBackground,
38070
- primaryButtonBorder: primaryButtonBorder,
38071
- buttonTextColor: buttonTextColor,
38072
- buttonBorderRadius: buttonBorderRadius,
38073
- basicBorderColor: basicBorderColor,
38074
- minContainerWidth: minContainerWidth,
38075
- maxContainerWidth: maxContainerWidth,
38076
- formHintColor: formHintColor,
38077
- guidelineExampleText: guidelineExampleText,
38078
- hoverCardWidth: hoverCardWidth,
38079
- menuHighlightColor: menuHighlightColor,
38080
- menuHighlightBorderColor: menuHighlightBorderColor,
38081
- menuActiveTextColor: menuActiveTextColor,
38082
- menuInactiveTextColor: menuInactiveTextColor,
38083
- menuSubheaderTextColor: menuSubheaderTextColor,
38084
- menuBorderColor: menuBorderColor,
38085
- unreadBackgroundColor: unreadBackgroundColor,
38086
- menuLineHeight: menuLineHeight,
38087
- menuSubheaderLineHeight: menuSubheaderLineHeight,
38088
- menuSubheaderFontSize: menuSubheaderFontSize,
38089
- notificationBarColor: notificationBarColor,
38090
- modalBodyTopPadding: modalBodyTopPadding,
38091
- modalDialogPadding: modalDialogPadding,
38092
- modalSectionPadding: modalSectionPadding,
38093
- mathDomain1: mathDomain1,
38094
- mathDomain2: mathDomain2,
38095
- mathDomain3: mathDomain3,
38096
- mathDomain4: mathDomain4,
38097
- partnerContentDomain1: partnerContentDomain1,
38098
- partnerContentDomain2: partnerContentDomain2,
38099
- partnerContentDomain3: partnerContentDomain3,
38100
- partnerContentDomain4: partnerContentDomain4,
38101
- csDomain1: csDomain1,
38102
- csDomain2: csDomain2,
38103
- csDomain3: csDomain3,
38104
- csDomain4: csDomain4,
38105
- economicsDomain1: economicsDomain1,
38106
- economicsDomain2: economicsDomain2,
38107
- economicsDomain3: economicsDomain3,
38108
- economicsDomain4: economicsDomain4,
38109
- collegeCareersMoreDomain1: collegeCareersMoreDomain1,
38110
- collegeCareersMoreDomain2: collegeCareersMoreDomain2,
38111
- collegeCareersMoreDomain3: collegeCareersMoreDomain3,
38112
- collegeCareersMoreDomain4: collegeCareersMoreDomain4,
38113
- humanitiesDomain1: humanitiesDomain1,
38114
- humanitiesDomain2: humanitiesDomain2,
38115
- humanitiesDomain3: humanitiesDomain3,
38116
- humanitiesDomain4: humanitiesDomain4,
38117
- gtpDomain1: gtpDomain1,
38118
- gtpDomain2: gtpDomain2,
38119
- gtpDomain3: gtpDomain3,
38120
- gtpDomain4: gtpDomain4,
38121
- scienceDomain1: scienceDomain1,
38122
- scienceDomain2: scienceDomain2,
38123
- scienceDomain3: scienceDomain3,
38124
- scienceDomain4: scienceDomain4,
38125
- testPrepDomain1: testPrepDomain1,
38126
- testPrepDomain2: testPrepDomain2,
38127
- testPrepDomain3: testPrepDomain3,
38128
- testPrepDomain4: testPrepDomain4,
38129
- defaultDomain1: defaultDomain1,
38130
- defaultDomain2: defaultDomain2,
38131
- defaultDomain3: defaultDomain3,
38132
- defaultDomain4: defaultDomain4,
38133
- defaultDomainColor: defaultDomainColor,
38134
- defaultSubjectColor: defaultSubjectColor,
38135
- defaultTopicColor: defaultTopicColor,
38136
- defaultHoverColor: defaultHoverColor,
38137
- defaultLinkColor: defaultLinkColor,
38138
- defaultLinkHoverColor: defaultLinkHoverColor,
38139
- scienceDomainColor: scienceDomainColor,
38140
- scienceSubjectColor: scienceSubjectColor,
38141
- scienceTopicColor: scienceTopicColor,
38142
- scienceHoverColor: scienceHoverColor,
38143
- scienceLinkColor: scienceLinkColor,
38144
- scienceLinkHoverColor: scienceLinkHoverColor,
38145
- humanitiesDomainColor: humanitiesDomainColor,
38146
- humanitiesSubjectColor: humanitiesSubjectColor,
38147
- humanitiesTopicColor: humanitiesTopicColor,
38148
- humanitiesHoverColor: humanitiesHoverColor,
38149
- humanitiesLinkColor: humanitiesLinkColor,
38150
- humanitiesLinkHoverColor: humanitiesLinkHoverColor,
38151
- economicsDomainColor: economicsDomainColor,
38152
- economicsSubjectColor: economicsSubjectColor,
38153
- economicsTopicColor: economicsTopicColor,
38154
- economicsHoverColor: economicsHoverColor,
38155
- economicsLinkColor: economicsLinkColor,
38156
- economicsLinkHoverColor: economicsLinkHoverColor,
38157
- csDomainColor: csDomainColor,
38158
- csSubjectColor: csSubjectColor,
38159
- csTopicColor: csTopicColor,
38160
- csHoverColor: csHoverColor,
38161
- csLinkColor: csLinkColor,
38162
- csLinkHoverColor: csLinkHoverColor,
38163
- partnerContentDomainColor: partnerContentDomainColor,
38164
- partnerContentSubjectColor: partnerContentSubjectColor,
38165
- partnerContentTopicColor: partnerContentTopicColor,
38166
- partnerContentHoverColor: partnerContentHoverColor,
38167
- partnerContentLinkColor: partnerContentLinkColor,
38168
- partnerContentLinkHoverColor: partnerContentLinkHoverColor,
38169
- essayDomainColor: essayDomainColor,
38170
- mathDomainColor: mathDomainColor,
38171
- mathSubjectColor: mathSubjectColor,
38172
- mathTopicColor: mathTopicColor,
38173
- mathHoverColor: mathHoverColor,
38174
- mathLinkColor: mathLinkColor,
38175
- mathLinkHoverColor: mathLinkHoverColor,
38176
- testPrepDomainColor: testPrepDomainColor,
38177
- testPrepSubjectColor: testPrepSubjectColor,
38178
- testPrepTopicColor: testPrepTopicColor,
38179
- testPrepHoverColor: testPrepHoverColor,
38180
- testPrepLinkColor: testPrepLinkColor,
38181
- testPrepLinkHoverColor: testPrepLinkHoverColor,
38182
- collegeCareersMoreDomainColor: collegeCareersMoreDomainColor,
38183
- collegeCareersMoreSubjectColor: collegeCareersMoreSubjectColor,
38184
- collegeCareersMoreTopicColor: collegeCareersMoreTopicColor,
38185
- collegeCareersMoreHoverColor: collegeCareersMoreHoverColor,
38186
- collegeCareersMoreLinkColor: collegeCareersMoreLinkColor,
38187
- collegeCareersMoreLinkHoverColor: collegeCareersMoreLinkHoverColor,
38188
- colorProficient: colorProficient,
38189
- colorPracticed: colorPracticed,
38190
- colorMastery1: colorMastery1,
38191
- colorMastery2: colorMastery2,
38192
- colorMastery3: colorMastery3,
38193
- colorSuggested: colorSuggested,
38194
- colorReview: colorReview,
38195
- colorNotStarted: colorNotStarted,
38196
- colorStarted: colorStarted,
38197
- colorStruggling: colorStruggling,
38198
- colorPersonal: colorPersonal,
38199
- colorPractice: colorPractice,
38200
- colorEnergyPoints: colorEnergyPoints,
38201
- pureSmMin: pureSmMin,
38202
- pureMdMin: pureMdMin,
38203
- pureLgMin: pureLgMin,
38204
- pureXlMin: pureXlMin,
38205
- pureXsMax: pureXsMax,
38206
- pureSmMax: pureSmMax,
38207
- pureMdMax: pureMdMax,
38208
- pureLgMax: pureLgMax
38209
- });
38210
-
38211
- const colors = {
38212
- white: "#FFFFFF",
38213
- gray98: "#FAFAFA",
38214
- gray97: "#F6F7F7",
38215
- gray95: "#F0F1F2",
38216
- gray90: "#E3E5E6",
38217
- gray85: "#D6D8DA",
38218
- gray76: "#BABEC2",
38219
- gray72: "#999A9D",
38220
- gray68: "#888D93",
38221
- gray55: "#707378",
38222
- gray41: "#626569",
38223
- gray25: "#3B3E40",
38224
- gray17: "#21242C",
38225
- gray10: "#1499af",
38226
- black: "#000000",
38227
- // These colors are pulled out from constants.js, which is itself generated
38228
- // from variables.less. We should probably avoid duplicating these here.
38229
- alertRed,
38230
- homepageBlue,
38231
- kaBlue,
38232
- kaBlueLight,
38233
- kaGreen,
38234
- kaGreenLight,
38235
- learnstormBlue,
38236
- backgroundBlue,
38237
- adminGreen: "#78C008",
38238
- kaBlueExtraLight: "#39c2db",
38239
- kaBlueHover: "#0c7f99",
38240
- kaGreenHover: "#518005",
38241
- // Domain-agnostic colors related to the study guide experience in the
38242
- // content library.
38243
- skyDark: "#001f4d",
38244
- recommendationGold: "#ffbe26",
38245
- starAced: "#ff9c39",
38246
- // We interploate the domain colors into this colors object for
38247
- // historical reasons, but everyone using domain-specific colors
38248
- // should switch to using the domainColors() function to look up
38249
- // colors instead.
38250
- //
38251
- // TODO(davidflanagan): remove this line once everything is
38252
- // working correctly with the domainColors() accessor.
38253
- // ...domainToColorsMap,
38254
- // mission: missionColors,
38255
-
38256
- // Colors for various OAuth providers
38257
- google: "#C1432f",
38258
- modernGoogle: "#be2612",
38259
- modernGoogleHover: "#8c1c0d",
38260
- facebook: "#4267B2",
38261
- modernFacebook: "#4267B2",
38262
- modernFacebookHover: "#00294a",
38263
- wonderBlocksGold: "#FFB100",
38264
- wonderBlocksLightBlue: "#37C5FD",
38265
- // We're using rgba instead of opacity to avoid affecting the focus outline
38266
- // when we just want to make the color transparent.
38267
- breadcrumbs: "rgba(255, 255, 255, 0.7)"
38268
- };
38269
- const borderRadius = 4;
38270
- const breadcrumbStyles = {
38271
- color: colors.breadcrumbs,
37810
+ // TODO: can we use WB typography?
37811
+ const bodyXsmallBold = {
37812
+ // For emphasized metadata, descriptions, etc
38272
37813
  fontFamily: "inherit",
38273
37814
  fontSize: 15,
38274
37815
  fontWeight: "bold",
38275
- letterSpacing: 0.8,
38276
- lineHeight: "19px",
38277
- textTransform: "uppercase",
38278
- // Disable subpixel antialiasing for OSX (improves light on dark text)
38279
- MozOsxFontSmoothing: "grayscale",
38280
- WebkitFontSmoothing: "antialiased"
38281
- };
38282
- const fonts = {
38283
- // The body of the page is setup to use 'Helvetica' by default and
38284
- // 'Lato' if/once it downloads. For your elements to follow this
38285
- // pattern, all you need is to make the text in your element
38286
- // inherit from the body using the "inherit" value.
38287
- regular: "inherit"
38288
- };
38289
- const typography = {
38290
- // For subject names
38291
- subjectHeadingDesktop: {
38292
- fontFamily: "inherit",
38293
- fontSize: 50,
38294
- fontWeight: "bold",
38295
- lineHeight: "61px"
38296
- },
38297
- subjectHeadingMobile: {
38298
- fontFamily: "inherit",
38299
- fontSize: 35,
38300
- fontWeight: "bold",
38301
- lineHeight: "37px"
38302
- },
38303
- // For the most important thing on the page
38304
- conceptHeadingDesktop: {
38305
- fontFamily: "inherit",
38306
- fontSize: 35,
38307
- fontWeight: "bold",
38308
- lineHeight: "37px"
38309
- },
38310
- conceptHeadingMobile: {
38311
- fontFamily: "inherit",
38312
- fontSize: 23,
38313
- fontWeight: "bold",
38314
- lineHeight: "27px"
38315
- },
38316
- // For sections or modules
38317
- subheadingDesktop: {
38318
- fontFamily: "inherit",
38319
- fontSize: 23,
38320
- fontWeight: "bold",
38321
- lineHeight: "27px"
38322
- },
38323
- subheadingMobile: {
38324
- fontFamily: "inherit",
38325
- fontSize: 20,
38326
- fontWeight: "bold",
38327
- lineHeight: "24px"
38328
- },
38329
- smallSubheadingDesktop: {
38330
- fontFamily: "inherit",
38331
- fontSize: 20,
38332
- lineHeight: 1.25
38333
- },
38334
- smallSubheadingMobile: {
38335
- fontFamily: "inherit",
38336
- fontSize: 15,
38337
- lineHeight: 1.25
38338
- },
38339
- // For callouts
38340
- smallHeading: {
38341
- color: colors.gray25,
38342
- fontFamily: "inherit",
38343
- fontSize: 17,
38344
- fontWeight: "bold",
38345
- lineHeight: "23px"
38346
- },
38347
- smallHeadingMobile: {
38348
- color: colors.gray25,
38349
- fontFamily: "inherit",
38350
- fontSize: 15,
38351
- fontWeight: "bold",
38352
- lineHeight: "18px"
38353
- },
38354
- // Accent headings
38355
- accentHeading: {
38356
- color: colors.gray68,
38357
- fontFamily: "inherit",
38358
- fontSize: 15,
38359
- fontWeight: "bold",
38360
- letterSpacing: 0.8,
38361
- lineHeight: "19px",
38362
- textTransform: "uppercase"
38363
- },
38364
- bodyLarge: {
38365
- // For articles with multiple paragraphs
38366
- fontFamily: fonts.regular,
38367
- fontSize: 20,
38368
- lineHeight: "30px"
38369
- },
38370
- bodySmall: {
38371
- // For small blocks of copy
38372
- fontFamily: fonts.regular,
38373
- fontSize: 17,
38374
- fontWeight: "normal",
38375
- lineHeight: "23px"
38376
- },
38377
- bodySmallBold: {
38378
- // For small blocks of copy
38379
- fontFamily: fonts.regular,
38380
- fontSize: 17,
38381
- fontWeight: "bold",
38382
- lineHeight: "23px"
38383
- },
38384
- bodyXsmall: {
38385
- // For metadata, descriptions, etc
38386
- fontFamily: fonts.regular,
38387
- fontSize: 15,
38388
- lineHeight: "22px"
38389
- },
38390
- bodyXsmallBold: {
38391
- // For emphasized metadata, descriptions, etc
38392
- fontFamily: "inherit",
38393
- fontSize: 15,
38394
- fontWeight: "bold",
38395
- lineHeight: "22px"
38396
- },
38397
- breadcrumb: breadcrumbStyles,
38398
- // For in-page tabs, breadcrumbs
38399
- breadcrumbSmall: _extends({}, breadcrumbStyles, {
38400
- fontSize: 12,
38401
- lineHeight: "13px"
38402
- }),
38403
- labelLarge: {
38404
- // For chiclets and thumbnail titles
38405
- fontFamily: "inherit",
38406
- fontSize: 23,
38407
- fontWeight: "bold",
38408
- lineHeight: "27px"
38409
- },
38410
- labelMedium: {
38411
- // For chiclets and thumbnail titles
38412
- fontFamily: "inherit",
38413
- fontSize: 15,
38414
- fontWeight: "bold",
38415
- lineHeight: "18px"
38416
- },
38417
- labelSmall: {
38418
- // Generally used with labelLarge, for breadcrumbs in thumbnails
38419
- fontFamily: fonts.regular,
38420
- fontSize: 12,
38421
- lineHeight: "14px"
38422
- },
38423
- caption: {
38424
- // For image captions
38425
- fontFamily: fonts.regular,
38426
- fontSize: 14,
38427
- fontStyle: "italic",
38428
- lineHeight: "17px"
38429
- },
38430
- desktopScalingOnMobile: {
38431
- // For when you need Mobile Safari and friends to stick to desktop
38432
- // sizes
38433
- MozTextSizeAdjust: "100%",
38434
- MsTextSizeAdjust: "100%",
38435
- WebkitTextSizeAdjust: "100%"
38436
- },
38437
- // TODO (josh): This should move to within <ModalHeader>
38438
- modalSubtitle: {
38439
- fontSize: 12,
38440
- lineHeight: 1.65,
38441
- // NOTE (josh): Font smoothing used here to create a 'medium' weight
38442
- // font, between normal and bold, on webkit browsers.
38443
- // This prop is an a11y risk when used on non-bold text, since it
38444
- // makes text very thin, but it's fine on bold text.
38445
- WebkitFontSmoothing: "antialiased",
38446
- fontWeight: "bold"
38447
- },
38448
- // HACK(kevinb): temporary wonder blocks styles for header only
38449
- // TODO(kevinb): use components from wonder-blocks-typography instead
38450
- wonderBlocksHeader: {
38451
- paddingLeft: 16,
38452
- paddingRight: 16,
38453
- paddingBottom: 24,
38454
- paddingTop: 34,
38455
- [mediaQueries.mdOrLarger]: {
38456
- paddingBottom: 32
38457
- }
38458
- },
38459
- // HACK(kevinb): temporary wonder blocks styles for header only
38460
- // TODO(kevinb): use components from wonder-blocks-typography instead
38461
- wonderBlocksTitle: {
38462
- color: colors.white,
38463
- fontWeight: 900,
38464
- // "Black" weight
38465
- fontSize: 28,
38466
- lineHeight: "32px",
38467
- margin: 0,
38468
- [mediaQueries.mdOrLarger]: {
38469
- fontSize: 36,
38470
- lineHeight: "40px"
38471
- }
38472
- },
38473
- // HACK(kevinb): temporary wonder blocks styles for header only
38474
- // TODO(kevinb): use components from wonder-blocks-typography instead
38475
- wonderBlocksLabelLarge: {
38476
- fontSize: 16,
38477
- lineHeight: "20px",
38478
- fontWeight: 700 // Bold
38479
- }
37816
+ lineHeight: "22px"
38480
37817
  };
38481
37818
 
38482
- var globalStyles = /*#__PURE__*/Object.freeze({
38483
- __proto__: null,
38484
- colors: colors,
38485
- borderRadius: borderRadius,
38486
- fonts: fonts,
38487
- typography: typography
38488
- });
38489
-
38490
37819
  const AnswerChoices = props => {
38491
37820
  const {
38492
37821
  strings
@@ -39322,7 +38651,7 @@ class LabelImage extends React.Component {
39322
38651
  const promptString = isMobile ? multipleAnswers ? strings.tapMultiple : strings.tapSingle : multipleAnswers ? strings.clickMultiple : strings.clickSingle;
39323
38652
  const choicesString = strings.choices;
39324
38653
  return /*#__PURE__*/React.createElement("div", {
39325
- className: classNames("perseus-label-image-widget-instructions", css(styles$a.instructions))
38654
+ className: classNames$1("perseus-label-image-widget-instructions", css(styles$a.instructions))
39326
38655
  }, /*#__PURE__*/React.createElement("div", {
39327
38656
  className: css(styles$a.instructionsCaption)
39328
38657
  }, promptString, " ", !hideChoices && choicesString), !hideChoices && /*#__PURE__*/React.createElement("div", {
@@ -39388,7 +38717,7 @@ const styles$a = StyleSheet.create({
39388
38717
  instructions: {
39389
38718
  paddingBottom: 16
39390
38719
  },
39391
- instructionsCaption: _extends({}, typography.bodyXsmallBold, {
38720
+ instructionsCaption: _extends({}, bodyXsmallBold, {
39392
38721
  paddingBottom: 16
39393
38722
  }),
39394
38723
  instructionsChoices: {
@@ -40659,7 +39988,7 @@ class Matrix extends React.Component {
40659
39988
  const highlightedCol = Math.max(cursorCol, matrixSize[1] - 1);
40660
39989
  const bracketHeight = (highlightedRow + 1) * (INPUT_HEIGHT + 2 * INPUT_MARGIN);
40661
39990
  const bracketOffset = (highlightedCol + 1) * (INPUT_WIDTH + 2 * INPUT_MARGIN);
40662
- const className = classNames({
39991
+ const className = classNames$1({
40663
39992
  "perseus-matrix": true,
40664
39993
  "static-mode": this.props.static,
40665
39994
  "the-matrix": this.state.enterTheMatrix >= 5
@@ -43224,9 +42553,9 @@ const makeSafeUrl = (urlString, locale) => {
43224
42553
  };
43225
42554
  const styles$6 = StyleSheet.create({
43226
42555
  widgetContainer: {
43227
- borderRadius: borderRadiusLarge,
42556
+ borderRadius: 6,
43228
42557
  borderWidth: 1,
43229
- borderColor: basicBorderColor,
42558
+ borderColor: "#CCC",
43230
42559
  padding: spacing.medium_16,
43231
42560
  paddingBottom: 0
43232
42561
  },
@@ -44543,7 +43872,7 @@ const VideoTranscriptLink = props => {
44543
43872
  } = props;
44544
43873
  const {
44545
43874
  useVideo
44546
- } = getDependencies();
43875
+ } = useDependencies();
44547
43876
  const [id, kind] = IS_URL$1.test(location) ? [getYoutubeId(location), "YOUTUBE_ID"] : [location, "READABLE_ID"];
44548
43877
  // The result value conforms to the wonder-blocks-data `Result` type
44549
43878
  // which is used by our GraphQL framework.
@@ -44717,7 +44046,7 @@ const init = function init(options) {
44717
44046
 
44718
44047
  // This file is processed by a Rollup plugin (replace) to inject the production
44719
44048
  const libName = "@khanacademy/perseus";
44720
- const libVersion = "39.0.1";
44049
+ const libVersion = "41.0.0";
44721
44050
  addLibraryVersionToPerseusDebug(libName, libVersion);
44722
44051
 
44723
44052
  const apiVersion = {
@@ -44849,7 +44178,7 @@ class ArticleRenderer extends React.Component {
44849
44178
  const apiOptions = _extends({}, ApiOptions.defaults, this.props.apiOptions, {
44850
44179
  isArticle: true
44851
44180
  });
44852
- const classes = classNames({
44181
+ const classes = classNames$1({
44853
44182
  "framework-perseus": true,
44854
44183
  "perseus-article": true,
44855
44184
  // NOTE(charlie): For exercises, this is applied outside of Perseus
@@ -44929,7 +44258,7 @@ class HintRenderer extends React.Component {
44929
44258
  const {
44930
44259
  isMobile
44931
44260
  } = apiOptions;
44932
- const classNames$1 = classNames("hint", !isMobile && "perseus-hint-renderer", isMobile && css(styles$3.newHint), isMobile && lastRendered && css(styles$3.lastRenderedNewHint), lastHint && "last-hint", lastRendered && "last-rendered", className);
44261
+ const classNames = classNames$1("hint", !isMobile && "perseus-hint-renderer", isMobile && css(styles$3.newHint), isMobile && lastRendered && css(styles$3.lastRenderedNewHint), lastHint && "last-hint", lastRendered && "last-rendered", className);
44933
44262
 
44934
44263
  // TODO(charlie): Allowing `customKeypad` would require that we
44935
44264
  // extend `ItemRenderer` to support nested inputs in the `HintsRenderer`.
@@ -44943,7 +44272,7 @@ class HintRenderer extends React.Component {
44943
44272
  /*#__PURE__*/
44944
44273
  // @ts-expect-error - TS2322 - Type 'string' is not assignable to type 'number | undefined'.
44945
44274
  React.createElement("div", {
44946
- className: classNames$1,
44275
+ className: classNames,
44947
44276
  tabIndex: "-1"
44948
44277
  }, !apiOptions.isMobile && /*#__PURE__*/React.createElement("span", {
44949
44278
  className: "perseus-sr-only"
@@ -44996,7 +44325,7 @@ const styles$3 = StyleSheet.create({
44996
44325
  },
44997
44326
  lastRenderedNewHint: {
44998
44327
  marginBottom: 0,
44999
- borderLeftColor: kaGreen$1
44328
+ borderLeftColor: kaGreen
45000
44329
  }
45001
44330
  });
45002
44331
 
@@ -45147,9 +44476,9 @@ class HintsRenderer extends React.Component {
45147
44476
  } = apiOptions;
45148
44477
  const showGetAnotherHint = getAnotherHint && hintsVisible > 0 && hintsVisible < this.props.hints.length;
45149
44478
  const hintRatioCopy = `(${hintsVisible}/${this.props.hints.length})`;
45150
- const classNames$1 = classNames(this.props.className, apiOptions.isMobile && hintsVisible > 0 && css(styles$2.mobileHintStylesHintsRenderer));
44479
+ const classNames = classNames$1(this.props.className, apiOptions.isMobile && hintsVisible > 0 && css(styles$2.mobileHintStylesHintsRenderer));
45151
44480
  return /*#__PURE__*/React.createElement("div", {
45152
- className: classNames$1
44481
+ className: classNames
45153
44482
  }, apiOptions.isMobile && hintsVisible > 0 && /*#__PURE__*/React.createElement("div", {
45154
44483
  className: css(styles$2.mobileHintStylesHintTitle, sharedStyles.responsiveLabel)
45155
44484
  }, this.context.strings.hints), hints, showGetAnotherHint && /*#__PURE__*/React.createElement("button", {
@@ -45186,7 +44515,7 @@ const styles$2 = StyleSheet.create({
45186
44515
  fontSize: "100%",
45187
44516
  fontFamily: "inherit",
45188
44517
  fontWeight: "bold",
45189
- color: kaGreen$1,
44518
+ color: kaGreen,
45190
44519
  padding: 0,
45191
44520
  position: "relative"
45192
44521
  },
@@ -45581,28 +44910,6 @@ const ref = /*#__PURE__*/React.forwardRef((props, ref) => /*#__PURE__*/React.cre
45581
44910
  ref: ref
45582
44911
  }))));
45583
44912
 
45584
- const devices = {
45585
- PHONE: "phone",
45586
- TABLET: "tablet",
45587
- DESKTOP: "desktop"
45588
- };
45589
-
45590
- // How many pixels do we reserve on the right-hand side of a preview
45591
- // for displaying lint indicators? This space needs to be reserved
45592
- // in DeviceFramer, but it is actually allocated in PerseusFrame
45593
- const lintGutterWidth = 36;
45594
-
45595
- // How wide a border does PerseusFrame draw? We need to allocate enough
45596
- // space for it in DeviceFramer.
45597
- const perseusFrameBorderWidth = 1;
45598
-
45599
- var constants = /*#__PURE__*/Object.freeze({
45600
- __proto__: null,
45601
- devices: devices,
45602
- lintGutterWidth: lintGutterWidth,
45603
- perseusFrameBorderWidth: perseusFrameBorderWidth
45604
- });
45605
-
45606
44913
  /**
45607
44914
  * Traverses a {content, widgets, images} renderer props object,
45608
44915
  * such as `itemData.question`
@@ -48289,5 +47596,17 @@ const EditorJsonify = {
48289
47596
  }
48290
47597
  };
48291
47598
 
48292
- export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, 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, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getClockwiseAngle, getCorrectAnswerForWidgetId, getImagesWithoutAltData, 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, makeSafeUrl, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
47599
+ /**
47600
+ * Main entry point
47601
+ */
47602
+ const GrapherUtil = {
47603
+ allTypes,
47604
+ DEFAULT_GRAPHER_PROPS,
47605
+ chooseType,
47606
+ defaultPlotProps,
47607
+ getEquationString,
47608
+ typeToButton
47609
+ };
47610
+
47611
+ export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, 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, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, addWidget, apiVersion, bodyXsmallBold, buildEmptyItemTreeForShape, buildMapper, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getClockwiseAngle, getCorrectAnswerForWidgetId, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, iconChevronDown, iconTrash, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, makeSafeUrl, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
48293
47612
  //# sourceMappingURL=index.js.map