@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/index.js CHANGED
@@ -9,7 +9,7 @@ var wonderBlocksCore = require('@khanacademy/wonder-blocks-core');
9
9
  var Tooltip$1 = require('@khanacademy/wonder-blocks-tooltip');
10
10
  var wonderBlocksTypography = require('@khanacademy/wonder-blocks-typography');
11
11
  var aphrodite = require('aphrodite');
12
- var classNames = require('classnames');
12
+ var classNames$1 = require('classnames');
13
13
  var React = require('react');
14
14
  var ReactDOM = require('react-dom');
15
15
  var _ = require('underscore');
@@ -67,7 +67,7 @@ function _interopNamespace(e) {
67
67
  var KAS__namespace = /*#__PURE__*/_interopNamespace(KAS);
68
68
  var PerseusLinter__namespace = /*#__PURE__*/_interopNamespace(PerseusLinter);
69
69
  var Tooltip__default = /*#__PURE__*/_interopDefaultLegacy(Tooltip$1);
70
- var classNames__default = /*#__PURE__*/_interopDefaultLegacy(classNames);
70
+ var classNames__default = /*#__PURE__*/_interopDefaultLegacy(classNames$1);
71
71
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
72
72
  var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
73
73
  var ReactDOM__default = /*#__PURE__*/_interopDefaultLegacy(ReactDOM);
@@ -367,7 +367,21 @@ const mockStrings = {
367
367
  simulationLocaleWarning: "Sorry, this simulation isn't available in your language.",
368
368
  addPoint: "Add Point",
369
369
  removePoint: "Remove Point",
370
- graphKeyboardPrompt: "Press Shift + Enter to interact with the graph"
370
+ graphKeyboardPrompt: "Press Shift + Enter to interact with the graph",
371
+ srPointAtCoordinates: _ref27 => {
372
+ let {
373
+ x,
374
+ y
375
+ } = _ref27;
376
+ return `Point at ${x} comma ${y}`;
377
+ },
378
+ srInteractiveElements: _ref28 => {
379
+ let {
380
+ elements
381
+ } = _ref28;
382
+ return `Interactive elements: ${elements}`;
383
+ },
384
+ srNoInteractiveElements: "No interactive elements"
371
385
  };
372
386
 
373
387
  /**
@@ -375,11 +389,13 @@ const mockStrings = {
375
389
  * used inside the Perseus package.
376
390
  *
377
391
  */
378
- // @ts-expect-error - TS2322 - Type 'Context<{ strings: {}; locale: string; }>' is not assignable to type 'Context<I18nContextType>'.
379
- const PerseusI18nContext = /*#__PURE__*/React__namespace.createContext(process.env.NODE_ENV === "test" || process.env.STORYBOOK ? {
392
+ const mockPerseusI18nContext = {
380
393
  strings: mockStrings,
381
394
  locale: "en"
382
- } :
395
+ };
396
+
397
+ // @ts-expect-error - TS2322 - Type 'Context<{ strings: {}; locale: string; }>' is not assignable to type 'Context<I18nContextType>'.
398
+ const PerseusI18nContext = /*#__PURE__*/React__namespace.createContext(process.env.NODE_ENV === "test" || process.env.STORYBOOK ? mockPerseusI18nContext :
383
399
  // We want to return null here, not an empty object, so that we
384
400
  // are will throw an error when attempting to access the
385
401
  // undefined locale or strings, making it easier to debug.
@@ -664,7 +680,7 @@ class InnerMathInput extends React__namespace.Component {
664
680
  closeButtonVisible: true,
665
681
  style: styles$E.popoverContent
666
682
  }, /*#__PURE__*/React__namespace.createElement(mathInput.DesktopKeypad, _extends({
667
- onAnalyticsEvent: this.props.analytics.onAnalyticsEvent,
683
+ onAnalyticsEvent: this.props.onAnalyticsEvent,
668
684
  extraKeys: this.props.extraKeys,
669
685
  onClickKey: this.handleKeypadPress,
670
686
  cursorContext: this.state.cursorContext,
@@ -827,6 +843,9 @@ const getDependencies = () => {
827
843
  const DependenciesContext = /*#__PURE__*/React__namespace.createContext({
828
844
  analytics: {
829
845
  onAnalyticsEvent: async () => {}
846
+ },
847
+ useVideo: () => {
848
+ throw new Error("useVideo dependency not provided in Perseus dependencies");
830
849
  }
831
850
  });
832
851
 
@@ -967,7 +986,7 @@ function getTextWidth(text) {
967
986
  class TextListEditor extends React__namespace.Component {
968
987
  static propTypes = {
969
988
  options: PropTypes__default["default"].array,
970
- layout: PropTypes__default["default"].string,
989
+ layout: PropTypes__default["default"].oneOf(["horizontal", "vertical"]),
971
990
  onChange: PropTypes__default["default"].func.isRequired
972
991
  };
973
992
  static defaultProps = {
@@ -1085,6 +1104,8 @@ class TextListEditor extends React__namespace.Component {
1085
1104
  }
1086
1105
  }
1087
1106
 
1107
+ const EMPTY_ARRAY = [];
1108
+
1088
1109
  /**
1089
1110
  * Stub Tag Editor.
1090
1111
  *
@@ -1098,7 +1119,6 @@ class TextListEditor extends React__namespace.Component {
1098
1119
  * It also gives a nicer interface for the group metadata editor
1099
1120
  * in local demo mode.
1100
1121
  */
1101
- const EMPTY_ARRAY = [];
1102
1122
  class StubTagEditor extends React__namespace.Component {
1103
1123
  static propTypes = {
1104
1124
  value: PropTypes__default["default"].arrayOf(PropTypes__default["default"].string),
@@ -2607,9 +2627,7 @@ class Expression extends React__namespace.Component {
2607
2627
  hasError: this.state.showErrorStyle,
2608
2628
  ariaLabel: this.props.ariaLabel || this.context.strings.mathInputBox,
2609
2629
  extraKeys: this.props.keypadConfiguration?.extraKeys,
2610
- analytics: this.props.analytics ?? {
2611
- onAnalyticsEvent: async () => {}
2612
- }
2630
+ onAnalyticsEvent: this.props.analytics?.onAnalyticsEvent ?? (async () => {})
2613
2631
  }))));
2614
2632
  }
2615
2633
  }
@@ -3999,12 +4017,12 @@ Zoom.prototype.dispose = function () {
3999
4017
  const ZoomService = new ZoomServiceClass();
4000
4018
 
4001
4019
  // @baseFontFamily: "Lato", sans-serif;
4002
- const baseFontFamily$1 = "'Lato', sans-serif";
4020
+ const baseFontFamily = "'Lato', sans-serif";
4003
4021
  // @boldFontFamily: "Lato-Bold", "Lato", sans-serif;
4004
4022
  const boldFontFamily = "'Lato-Bold', 'Lato', sans-serif";
4005
4023
  // @kaGreen: #71b307;
4006
- const kaGreen$1 = "#71B307";
4007
- const white$1 = "#FFFFFF";
4024
+ const kaGreen = "#71B307";
4025
+ const white = "#FFFFFF";
4008
4026
  const gray97 = "#F6F7F7";
4009
4027
  const gray85 = "#D6D8DA";
4010
4028
  const gray76 = "#BABEC2";
@@ -4012,23 +4030,23 @@ const gray68 = "#888D93";
4012
4030
  const gray17 = "#21242c";
4013
4031
 
4014
4032
  // @pure-sm-min: 568px;
4015
- const pureSmMin$2 = "568px";
4033
+ const pureSmMin$1 = "568px";
4016
4034
  // @pure-md-min: 768px;
4017
- const pureMdMin$2 = "768px";
4035
+ const pureMdMin$1 = "768px";
4018
4036
  // @pure-lg-min: 1024px;
4019
- const pureLgMin$2 = "1024px";
4037
+ const pureLgMin$1 = "1024px";
4020
4038
  // @pure-xl-min: 1280px;
4021
- const pureXlMin$2 = "1280px";
4039
+ const pureXlMin$1 = "1280px";
4022
4040
  // @pure-xs-max: (@pure-sm-min - 1);
4023
- const pureXsMax$2 = "567px";
4041
+ const pureXsMax$1 = "567px";
4024
4042
  // @pure-sm-max: (@pure-md-min - 1);
4025
- const pureSmMax$2 = "767px";
4043
+ const pureSmMax$1 = "767px";
4026
4044
  // @pure-md-max: (@pure-lg-min - 1);
4027
- const pureMdMax$2 = "1023px";
4045
+ const pureMdMax$1 = "1023px";
4028
4046
  // @pure-lg-max: (@pure-xl-min - 1);
4029
- const pureLgMax$2 = "1279px";
4047
+ const pureLgMax$1 = "1279px";
4030
4048
  // @tableBackgroundAccent: #f9f9f9; // for striping
4031
- const tableBackgroundAccent$1 = "#F9F9F9";
4049
+ const tableBackgroundAccent = "#F9F9F9";
4032
4050
  // @zIndexAboveScratchpad: @zIndexScratchPad + 1;
4033
4051
  const zIndexAboveScratchpad$1 = 2;
4034
4052
  // @zIndexInteractiveComponent: @zIndexAboveScratchpad + 1;
@@ -4056,26 +4074,26 @@ const checkedColor$1 = "#71B307";
4056
4074
  const articleMaxWidthInPx = 688;
4057
4075
  const articleMaxWidthTableInPx = 512;
4058
4076
 
4059
- var constants$1 = /*#__PURE__*/Object.freeze({
4077
+ var constants = /*#__PURE__*/Object.freeze({
4060
4078
  __proto__: null,
4061
- baseFontFamily: baseFontFamily$1,
4079
+ baseFontFamily: baseFontFamily,
4062
4080
  boldFontFamily: boldFontFamily,
4063
- kaGreen: kaGreen$1,
4064
- white: white$1,
4081
+ kaGreen: kaGreen,
4082
+ white: white,
4065
4083
  gray97: gray97,
4066
4084
  gray85: gray85,
4067
4085
  gray76: gray76,
4068
4086
  gray68: gray68,
4069
4087
  gray17: gray17,
4070
- pureSmMin: pureSmMin$2,
4071
- pureMdMin: pureMdMin$2,
4072
- pureLgMin: pureLgMin$2,
4073
- pureXlMin: pureXlMin$2,
4074
- pureXsMax: pureXsMax$2,
4075
- pureSmMax: pureSmMax$2,
4076
- pureMdMax: pureMdMax$2,
4077
- pureLgMax: pureLgMax$2,
4078
- tableBackgroundAccent: tableBackgroundAccent$1,
4088
+ pureSmMin: pureSmMin$1,
4089
+ pureMdMin: pureMdMin$1,
4090
+ pureLgMin: pureLgMin$1,
4091
+ pureXlMin: pureXlMin$1,
4092
+ pureXsMax: pureXsMax$1,
4093
+ pureSmMax: pureSmMax$1,
4094
+ pureMdMax: pureMdMax$1,
4095
+ pureLgMax: pureLgMax$1,
4096
+ tableBackgroundAccent: tableBackgroundAccent,
4079
4097
  zIndexAboveScratchpad: zIndexAboveScratchpad$1,
4080
4098
  zIndexInteractiveComponent: zIndexInteractiveComponent$1,
4081
4099
  phoneMargin: phoneMargin,
@@ -14942,11 +14960,7 @@ ___default["default"].extend(MovableLine$4.prototype, {
14942
14960
  * A stripped version of Icon.jsx from webapp. Takes an SVG icon and renders it
14943
14961
  * inline like Font Awesome did.
14944
14962
  *
14945
- * If you are looking for an icon that we've used before you should look in
14946
- * webapp's `icon-paths.js` which is a reference file for all the SVG paths
14947
- * that we've used. You'll need to copy the object from that file into
14948
- * whichever file you're using the icon and explicitly pass it in to the
14949
- * <InlineIcon/> React component.
14963
+ * You can refer to `icon-paths.ts` for all available SVG icons.
14950
14964
  *
14951
14965
  * We assume that the viewBox is cropped and aligned to (0, 0), but icons can
14952
14966
  * be defined differently. At some point we might want to add these attributes
@@ -14954,13 +14968,14 @@ ___default["default"].extend(MovableLine$4.prototype, {
14954
14968
  *
14955
14969
  * Sample usage:
14956
14970
  *
14957
- * const editIcon = {
14971
+ * ```
14972
+ * const editIcon = {
14958
14973
  * 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",
14959
14974
  * width: 100,
14960
14975
  * height: 78.912,
14961
- * };
14962
- * <InlineIcon {...editIcon} />
14963
- *
14976
+ * };
14977
+ * <InlineIcon {...editIcon} />
14978
+ * ```
14964
14979
  */
14965
14980
  const InlineIcon = _ref => {
14966
14981
  let {
@@ -14987,11 +15002,7 @@ const InlineIcon = _ref => {
14987
15002
  };
14988
15003
 
14989
15004
  /**
14990
- * Icon paths to be used with `inline-icon.jsx`.
14991
- *
14992
- * These paths are taken directly from webapp's `icon-paths.js`. Unlike the
14993
- * webapp equivalent, these can be directly required within Perseus files since
14994
- * this is all bundled together anyway.
15005
+ * Icon paths to be used with `inline-icon.tsx` and `icon.tsx`.
14995
15006
  */
14996
15007
 
14997
15008
  const iconCheck = {
@@ -14999,77 +15010,26 @@ const iconCheck = {
14999
15010
  width: 24,
15000
15011
  height: 24
15001
15012
  };
15002
- const iconCheckMedium = {
15003
- 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",
15004
- width: 16,
15005
- height: 16
15006
- };
15007
15013
  const iconChevronDown = {
15008
15014
  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",
15009
15015
  width: 100,
15010
15016
  height: 63.034
15011
15017
  };
15012
- const iconChevronRight = {
15013
- 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",
15014
- width: 63.034,
15015
- height: 100
15016
- };
15017
15018
  const iconCircle = {
15018
15019
  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",
15019
15020
  width: 100,
15020
15021
  height: 100
15021
15022
  };
15022
- const iconCircleArrowDown = {
15023
- 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",
15024
- width: 100,
15025
- height: 100
15026
- };
15027
- const iconCircleArrowUp = {
15028
- 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",
15029
- width: 100,
15030
- height: 100
15031
- };
15032
15023
  const iconCircleThin = {
15033
15024
  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",
15034
15025
  width: 100,
15035
15026
  height: 99.944
15036
15027
  };
15037
- const iconDesktop = {
15038
- 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",
15039
- width: 100,
15040
- height: 86.648
15041
- };
15042
- const iconDropdownArrow = {
15043
- 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",
15044
- width: 24,
15045
- height: 24
15046
- };
15047
- const iconExclamationSign = {
15048
- 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",
15049
- width: 100,
15050
- height: 99.944
15051
- };
15052
- // Grabbed from https://github.com/encharm/Font-Awesome-SVG-PNG
15053
- const iconGear = {
15054
- 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",
15055
- width: 1792,
15056
- height: 1792
15057
- };
15058
- const iconMobilePhone = {
15059
- 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",
15060
- width: 60.013,
15061
- height: 100
15062
- };
15063
15028
  const iconOk = {
15064
15029
  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",
15065
15030
  width: 100,
15066
15031
  height: 76.637
15067
15032
  };
15068
- const iconPlus = {
15069
- 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",
15070
- width: 100,
15071
- height: 100
15072
- };
15073
15033
  const iconRemove = {
15074
15034
  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",
15075
15035
  width: 100,
@@ -15085,53 +15045,17 @@ const iconTryAgain = {
15085
15045
  width: 23,
15086
15046
  height: 23
15087
15047
  };
15088
- const iconTablet = {
15089
- 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",
15090
- width: 81.852,
15091
- height: 100
15092
- };
15093
15048
  const iconTrash = {
15094
15049
  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",
15095
15050
  width: 91.681,
15096
15051
  height: 100
15097
15052
  };
15098
- const iconUndo = {
15099
- 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",
15100
- width: 10,
15101
- height: 10
15102
- };
15103
15053
  const iconMinus = {
15104
15054
  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`,
15105
15055
  width: 24,
15106
15056
  height: 24
15107
15057
  };
15108
15058
 
15109
- var iconPaths = /*#__PURE__*/Object.freeze({
15110
- __proto__: null,
15111
- iconCheck: iconCheck,
15112
- iconCheckMedium: iconCheckMedium,
15113
- iconChevronDown: iconChevronDown,
15114
- iconChevronRight: iconChevronRight,
15115
- iconCircle: iconCircle,
15116
- iconCircleArrowDown: iconCircleArrowDown,
15117
- iconCircleArrowUp: iconCircleArrowUp,
15118
- iconCircleThin: iconCircleThin,
15119
- iconDesktop: iconDesktop,
15120
- iconDropdownArrow: iconDropdownArrow,
15121
- iconExclamationSign: iconExclamationSign,
15122
- iconGear: iconGear,
15123
- iconMobilePhone: iconMobilePhone,
15124
- iconOk: iconOk,
15125
- iconPlus: iconPlus,
15126
- iconRemove: iconRemove,
15127
- iconStar: iconStar,
15128
- iconTryAgain: iconTryAgain,
15129
- iconTablet: iconTablet,
15130
- iconTrash: iconTrash,
15131
- iconUndo: iconUndo,
15132
- iconMinus: iconMinus
15133
- });
15134
-
15135
15059
  /* eslint-disable @babel/no-invalid-this */
15136
15060
  const add$1 = {
15137
15061
  constrain: function () {
@@ -17041,6 +16965,15 @@ const Status = {
17041
16965
  LOADED: "loaded",
17042
16966
  FAILED: "failed"
17043
16967
  };
16968
+ /**
16969
+ * Component to display an image (or other React components) while the desired
16970
+ * image is loading.
16971
+ *
16972
+ * Derived from
16973
+ * https://github.com/hzdg/react-imageloader/blob/master/src/index.js
16974
+ * to better suit our environment/build tools. Additionally, this one does
16975
+ * not introduce a wrapper element, which makes styling easier.
16976
+ */
17044
16977
  class ImageLoader extends React__namespace.Component {
17045
16978
  img;
17046
16979
  constructor(props) {
@@ -18400,7 +18333,7 @@ const styles$C = aphrodite.StyleSheet.create({
18400
18333
  },
18401
18334
  ":hover ~ div span[data-lint-inside-table]": {
18402
18335
  backgroundColor: warningColor,
18403
- color: white$1
18336
+ color: white
18404
18337
  }
18405
18338
  },
18406
18339
  // This is how we position the hover target for inline lint.
@@ -18436,7 +18369,7 @@ const styles$C = aphrodite.StyleSheet.create({
18436
18369
  // draw attention to the linty content.
18437
18370
  ":hover ~ span": {
18438
18371
  backgroundColor: warningColor,
18439
- color: white$1
18372
+ color: white
18440
18373
  }
18441
18374
  },
18442
18375
  radioWidgetHoverTarget: {
@@ -18478,7 +18411,7 @@ const styles$C = aphrodite.StyleSheet.create({
18478
18411
  // draw attention to the linty content.
18479
18412
  ":hover ~ span": {
18480
18413
  backgroundColor: warningColor,
18481
- color: white$1
18414
+ color: white
18482
18415
  }
18483
18416
  },
18484
18417
  // This is the class for the lint indicator in the margin.
@@ -18515,10 +18448,10 @@ const styles$C = aphrodite.StyleSheet.create({
18515
18448
  // When it is displayed, it goes on top!
18516
18449
  zIndex: 1000,
18517
18450
  // These styles control what the tooltip looks like
18518
- color: white$1,
18451
+ color: white,
18519
18452
  backgroundColor: gray17,
18520
18453
  opacity: 0.9,
18521
- fontFamily: baseFontFamily$1,
18454
+ fontFamily: baseFontFamily,
18522
18455
  fontSize: "12px",
18523
18456
  lineHeight: "15px",
18524
18457
  width: "320px",
@@ -19536,7 +19469,7 @@ var preprocessTex = (texCode => texCode
19536
19469
 
19537
19470
  const {
19538
19471
  interactiveSizes
19539
- } = constants$1;
19472
+ } = constants;
19540
19473
 
19541
19474
  // Note: these size cutoffs represent content-width cutoffs as specified in
19542
19475
  // http://zpl.io/1mVmvU (broken link - we no longer use Zeplin)
@@ -19569,13 +19502,6 @@ const getInteractiveBoxFromSizeClass = sizeClass => {
19569
19502
  return [interactiveSizes.defaultBoxSize, interactiveSizes.defaultBoxSize];
19570
19503
  };
19571
19504
 
19572
- var sizingUtils = /*#__PURE__*/Object.freeze({
19573
- __proto__: null,
19574
- containerSizeClass: containerSizeClass,
19575
- getClassFromWidth: getClassFromWidth,
19576
- getInteractiveBoxFromSizeClass: getInteractiveBoxFromSizeClass
19577
- });
19578
-
19579
19505
  class WidgetContainer extends React__namespace.Component {
19580
19506
  widgetRef = /*#__PURE__*/React__namespace.createRef();
19581
19507
  static defaultProps = {
@@ -21252,7 +21178,7 @@ const Triangle = props => {
21252
21178
  width: 0,
21253
21179
  position: "absolute",
21254
21180
  left: props.left,
21255
- top: props["top"],
21181
+ top: props.top,
21256
21182
  borderLeft: borderLeft,
21257
21183
  borderRight: borderRight,
21258
21184
  borderTop: borderTop,
@@ -21333,6 +21259,47 @@ const HORIZONTAL_ALIGNMNENTS = {
21333
21259
  arrowLeft: arrowSize => -arrowSize - 2
21334
21260
  }
21335
21261
  };
21262
+ /**
21263
+ * DEPRECATED! Use Wonder Blocks tooltip instead.
21264
+ *
21265
+ * A generic tooltip library for React.js
21266
+ *
21267
+ * ```
21268
+ * import Tooltip from "./tooltip";
21269
+ * <Tooltip
21270
+ * className="class-for-tooltip-contents"
21271
+ * horizontalPosition={HoriziontalDirection.Left}
21272
+ * horizontalAlign={HoriziontalDirection.Left}
21273
+ * verticalPosition={VerticalDirection.Top}
21274
+ * arrowSize={10} // arrow size in pixels
21275
+ * borderColor="#ccc" // color of the border for the tooltip
21276
+ * show={true} // whether the tooltip should currently be visible
21277
+ * targetContainerStyle={targetContainerStyle}
21278
+ * >
21279
+ * <TargetElementOfTheTooltip />
21280
+ * <TooltipContents1 />
21281
+ * <TooltipContents2 />
21282
+ * </Tooltip>
21283
+ * ```
21284
+ *
21285
+ * To show/hide the tooltip, the parent component should call the
21286
+ * `.show()` and `.hide()` methods of the tooltip when appropriate.
21287
+ * (These are usually set up as handlers of events on the target element.)
21288
+ *
21289
+ * Notes:
21290
+ * `className` should not specify a border; that is handled by `borderColor`
21291
+ * so that the arrow and tooltip match
21292
+ *
21293
+ * ```
21294
+ * __,,--``\\
21295
+ * _,,-''`` \\ ,
21296
+ * '----------_.------'-.___|\__
21297
+ * _.--''`` `)__ )__ @\__
21298
+ * ( .. ''---/___,,E/__,E'------`
21299
+ * `-''`''
21300
+ * Here be dragons.
21301
+ * ```
21302
+ */
21336
21303
  class Tooltip extends React__namespace.Component {
21337
21304
  static defaultProps = {
21338
21305
  className: "",
@@ -21560,10 +21527,7 @@ class InputWithExamples extends React__namespace.Component {
21560
21527
  return "- " + example;
21561
21528
  }).join("\n");
21562
21529
  const showExamples = this.props.shouldShowExamples && this.state.showExamples;
21563
- return /*#__PURE__*/React__namespace.createElement(Tooltip
21564
- // eslint-disable-next-line react/no-string-refs
21565
- , {
21566
- ref: "tooltip",
21530
+ return /*#__PURE__*/React__namespace.createElement(Tooltip, {
21567
21531
  className: "perseus-formats-tooltip preview-measure",
21568
21532
  horizontalPosition: HorizontalDirection.Left,
21569
21533
  horizontalAlign: HorizontalDirection.Left,
@@ -24794,29 +24758,29 @@ var PassageRef$1 = {
24794
24758
  * });
24795
24759
  */
24796
24760
  const {
24797
- pureXsMax: pureXsMax$1,
24798
- pureSmMin: pureSmMin$1,
24799
- pureSmMax: pureSmMax$1,
24800
- pureMdMin: pureMdMin$1,
24801
- pureMdMax: pureMdMax$1,
24802
- pureLgMin: pureLgMin$1,
24803
- pureLgMax: pureLgMax$1,
24804
- pureXlMin: pureXlMin$1
24805
- } = constants$1;
24761
+ pureXsMax,
24762
+ pureSmMin,
24763
+ pureSmMax,
24764
+ pureMdMin,
24765
+ pureMdMax,
24766
+ pureLgMin,
24767
+ pureLgMax,
24768
+ pureXlMin
24769
+ } = constants;
24806
24770
  var mediaQueries = {
24807
- xs: `@media screen and (max-width: ${pureXsMax$1})`,
24808
- sm: `@media screen and (min-width: ${pureSmMin$1}) and ` + `(max-width: ${pureSmMax$1})`,
24809
- md: `@media screen and (min-width: ${pureMdMin$1}) and ` + `(max-width: ${pureMdMax$1})`,
24810
- lg: `@media screen and (min-width: ${pureLgMin$1}) and ` + `(max-width: ${pureLgMax$1})`,
24811
- xl: `@media screen and (min-width: ${pureXlMin$1})`,
24812
- xsOrSmaller: `@media screen and (max-width: ${pureXsMax$1})`,
24813
- smOrSmaller: `@media screen and (max-width: ${pureSmMax$1})`,
24814
- mdOrSmaller: `@media screen and (max-width: ${pureMdMax$1})`,
24815
- lgOrSmaller: `@media screen and (max-width: ${pureLgMax$1})`,
24816
- smOrLarger: `@media screen and (min-width: ${pureSmMin$1})`,
24817
- mdOrLarger: `@media screen and (min-width: ${pureMdMin$1})`,
24818
- lgOrLarger: `@media screen and (min-width: ${pureLgMin$1})`,
24819
- xlOrLarger: `@media screen and (min-width: ${pureXlMin$1})`
24771
+ xs: `@media screen and (max-width: ${pureXsMax})`,
24772
+ sm: `@media screen and (min-width: ${pureSmMin}) and ` + `(max-width: ${pureSmMax})`,
24773
+ md: `@media screen and (min-width: ${pureMdMin}) and ` + `(max-width: ${pureMdMax})`,
24774
+ lg: `@media screen and (min-width: ${pureLgMin}) and ` + `(max-width: ${pureLgMax})`,
24775
+ xl: `@media screen and (min-width: ${pureXlMin})`,
24776
+ xsOrSmaller: `@media screen and (max-width: ${pureXsMax})`,
24777
+ smOrSmaller: `@media screen and (max-width: ${pureSmMax})`,
24778
+ mdOrSmaller: `@media screen and (max-width: ${pureMdMax})`,
24779
+ lgOrSmaller: `@media screen and (max-width: ${pureLgMax})`,
24780
+ smOrLarger: `@media screen and (min-width: ${pureSmMin})`,
24781
+ mdOrLarger: `@media screen and (min-width: ${pureMdMin})`,
24782
+ lgOrLarger: `@media screen and (min-width: ${pureLgMin})`,
24783
+ xlOrLarger: `@media screen and (min-width: ${pureXlMin})`
24820
24784
  };
24821
24785
 
24822
24786
  const {
@@ -24826,7 +24790,7 @@ const {
24826
24790
  checkedColor,
24827
24791
  circleSize,
24828
24792
  radioMarginWidth
24829
- } = constants$1;
24793
+ } = constants;
24830
24794
  var sharedStyles = aphrodite.StyleSheet.create({
24831
24795
  perseusInteractive: {
24832
24796
  zIndex: zIndexInteractiveComponent,
@@ -25060,6 +25024,37 @@ const BASE_ICON_SIZE = 10;
25060
25024
  // If you pass in an object you need to specify an SVG path to
25061
25025
  // render along with the width and height of the path (to ensure
25062
25026
  // that it gets scaled correctly).
25027
+ /**
25028
+ * An SVG Icon
25029
+ *
25030
+ * This component is designed to take in SVG paths and render icons based upon
25031
+ * them. If you are looking for an icon that we've used before you should look
25032
+ * in `icon-paths.js` which is a reference file for all the SVG paths that
25033
+ * we've used. You'll need to copy the object from that file into whichever
25034
+ * file you're using the icon and explicitly pass it in to the <Icon/> React
25035
+ * component.
25036
+ *
25037
+ * Sample usage:
25038
+ *
25039
+ * ```
25040
+ * const dropdownIcon = `M5,6L0,0L10,0`;
25041
+ * <Icon icon={dropdownIcon} />
25042
+ * ```
25043
+ *
25044
+ * Or:
25045
+ *
25046
+ * ```
25047
+ * const dropdownIcon = {
25048
+ * path: `M5,6L0,0L10,0`,
25049
+ * height: 10,
25050
+ * width: 10,
25051
+ * };
25052
+ * <Icon icon={dropdownIcon} size={20} />
25053
+ * ```
25054
+ *
25055
+ * If you want to add an entirely new icon please read the note inside
25056
+ * the `icon-paths.ts` file.
25057
+ */
25063
25058
  class Icon extends React__namespace.Component {
25064
25059
  static defaultProps = {
25065
25060
  color: "currentColor"
@@ -25182,7 +25177,7 @@ function getA11yText(letter, checked, correct, crossedOut, showCorrectness, stri
25182
25177
  const FocusRing = function (_ref) {
25183
25178
  let {
25184
25179
  visible = true,
25185
- color = kaGreen$1,
25180
+ color = kaGreen,
25186
25181
  multipleSelect = false,
25187
25182
  children
25188
25183
  } = _ref;
@@ -25283,7 +25278,7 @@ function ChoiceInner(props) {
25283
25278
  } = usePerseusI18n();
25284
25279
  const letter = getChoiceLetter(pos, strings);
25285
25280
  if (!showCorrectness) {
25286
- return letter;
25281
+ return /*#__PURE__*/React__namespace.createElement("span", null, letter);
25287
25282
  }
25288
25283
  if (correct) {
25289
25284
  return /*#__PURE__*/React__namespace.createElement(InlineIcon, _extends({}, iconCheck, {
@@ -25310,7 +25305,7 @@ function getDynamicStyles(checked, showCorrectness, pressed, multipleSelect, cor
25310
25305
  backgroundColor = "transparent";
25311
25306
  } else if (checked) {
25312
25307
  const bg = showCorrectness && correct ? wonderBlocksTokens.color.green : wonderBlocksTokens.color.blue;
25313
- color = white$1;
25308
+ color = white;
25314
25309
  backgroundColor = bg;
25315
25310
  borderColor = bg;
25316
25311
  } else {
@@ -25353,7 +25348,6 @@ const ChoiceIcon = function (props) {
25353
25348
  visible: focused || hovered,
25354
25349
  multipleSelect: multipleSelect
25355
25350
  }, /*#__PURE__*/React__namespace.createElement("div", {
25356
- // @ts-expect-error - TS2322 - Type '{ backgroundColor: string | null | undefined; borderColor: string; color: string; borderRadius: number; }' is not assignable to type 'Properties<string | number, string & {}>'.
25357
25351
  style: dynamicStyles,
25358
25352
  "data-testid": "choice-icon__library-choice-icon",
25359
25353
  className: aphrodite.css(styles$s.circle, showCorrectness && correct && styles$s.circleCorrect, showCorrectness && !correct && styles$s.circleIncorrect, showCorrectness && !correct && (checked || previouslyAnswered) && styles$s.circleIncorrectAnswered)
@@ -25388,7 +25382,7 @@ const styles$s = aphrodite.StyleSheet.create({
25388
25382
  borderStyle: "solid",
25389
25383
  borderWidth: 2,
25390
25384
  // The default icons have letters in them. Style those letters.
25391
- fontFamily: baseFontFamily$1,
25385
+ fontFamily: baseFontFamily,
25392
25386
  // NOTE(emily): We explicitly set the font weight instead of using the
25393
25387
  // "bold font family" so that characters which fall back to the default
25394
25388
  // font get bolded too.
@@ -27804,7 +27798,7 @@ const styles$j = aphrodite.StyleSheet.create({
27804
27798
  gradedGroup: {
27805
27799
  borderTop: `1px solid ${gray76}`,
27806
27800
  borderBottom: `1px solid ${gray76}`,
27807
- backgroundColor: tableBackgroundAccent$1,
27801
+ backgroundColor: tableBackgroundAccent,
27808
27802
  marginLeft: negativePhoneMargin,
27809
27803
  marginRight: negativePhoneMargin,
27810
27804
  paddingBottom: phoneMargin,
@@ -28093,7 +28087,7 @@ const styles$i = aphrodite.StyleSheet.create({
28093
28087
  container: {
28094
28088
  borderTop: `1px solid ${gray76}`,
28095
28089
  borderBottom: `1px solid ${gray76}`,
28096
- backgroundColor: tableBackgroundAccent$1,
28090
+ backgroundColor: tableBackgroundAccent,
28097
28091
  marginLeft: negativePhoneMargin,
28098
28092
  marginRight: negativePhoneMargin,
28099
28093
  paddingBottom: phoneMargin,
@@ -28698,21 +28692,6 @@ const typeToButton = type => {
28698
28692
  };
28699
28693
  };
28700
28694
 
28701
- var util = /*#__PURE__*/Object.freeze({
28702
- __proto__: null,
28703
- DEFAULT_BACKGROUND_IMAGE: DEFAULT_BACKGROUND_IMAGE,
28704
- allTypes: allTypes,
28705
- functionForType: functionForType,
28706
- getEquationString: getEquationString,
28707
- pointsFromNormalized: pointsFromNormalized,
28708
- maybePointsFromNormalized: maybePointsFromNormalized,
28709
- defaultPlotProps: defaultPlotProps,
28710
- chooseType: chooseType,
28711
- getGridAndSnapSteps: getGridAndSnapSteps,
28712
- DEFAULT_GRAPHER_PROPS: DEFAULT_GRAPHER_PROPS,
28713
- typeToButton: typeToButton
28714
- });
28715
-
28716
28695
  function getCoefficientsByType(data) {
28717
28696
  if (data.type === "exponential" || data.type === "logarithm") {
28718
28697
  const grader = functionForType(data.type);
@@ -31282,6 +31261,10 @@ const HiddenIcon = () => /*#__PURE__*/React__namespace.createElement("svg", {
31282
31261
  }, /*#__PURE__*/React__namespace.createElement("path", {
31283
31262
  d: "M0 0h24v24H0z"
31284
31263
  }))));
31264
+ /**
31265
+ * A "heads-up display" (HUD) indicator that includes a short message (usually
31266
+ * used for linting errors). The indicator can be disabled.
31267
+ */
31285
31268
  const HUD = _ref => {
31286
31269
  let {
31287
31270
  message,
@@ -31314,7 +31297,7 @@ const styles$g = aphrodite.StyleSheet.create({
31314
31297
  fontFamily: boldFontFamily,
31315
31298
  fontSize: "15px",
31316
31299
  lineHeight: "18px",
31317
- color: white$1,
31300
+ color: white,
31318
31301
  userSelect: "none",
31319
31302
  borderWidth: 0 // <button> gives us a border by default
31320
31303
  },
@@ -31495,18 +31478,24 @@ const {
31495
31478
  const toNumericString = KhanMath.toNumericString;
31496
31479
  const getNumericFormat = KhanMath.getNumericFormat;
31497
31480
 
31498
- /* An input box that accepts only numeric strings
31481
+ /**
31482
+ * An input box that accepts only numeric strings
31499
31483
  *
31500
- * Calls onChange(value, format) for valid numbers.
31501
- * Reverts to the current value onBlur or on [ENTER],
31484
+ * Calls `onChange(value, format)` for valid numbers.
31485
+ *
31486
+ * Reverts to the current value `onBlur` or on [ENTER],
31502
31487
  * but maintains the format (i.e. 3/2, 1 1/2, 150%)
31503
- * Accepts empty input and sends it to onChange as null
31504
- * if no numeric placeholder is set.
31505
- * If given a checkValidity function, will turn
31506
- * the background/outline red when invalid
31507
- * If useArrowKeys is set to true, up/down arrows will
31508
- * increment/decrement integers
31509
- * Optionally takes a size ("mini", "small", "normal")
31488
+ *
31489
+ * Accepts empty input and sends it to `onChange` as `null` if no numeric
31490
+ * placeholder is set.
31491
+ *
31492
+ * If given a `checkValidity` function, will turn the background/outline red
31493
+ * when invalid.
31494
+ *
31495
+ * If `useArrowKeys` is set to `true`, up/down arrows will increment/decrement
31496
+ * integers.
31497
+ *
31498
+ * Optionally takes a `size` (`"mini"`, `"small"`,` `"normal"`)
31510
31499
  */
31511
31500
  class NumberInput extends React__namespace.Component {
31512
31501
  static contextType = PerseusI18nContext;
@@ -31517,7 +31506,7 @@ class NumberInput extends React__namespace.Component {
31517
31506
  onChange: PropTypes__default["default"].func.isRequired,
31518
31507
  onFormatChange: PropTypes__default["default"].func,
31519
31508
  checkValidity: PropTypes__default["default"].func,
31520
- size: PropTypes__default["default"].string,
31509
+ size: PropTypes__default["default"].oneOf(["mini", "small", "normal"]),
31521
31510
  label: PropTypes__default["default"].oneOf(["put your labels outside your inputs!"])
31522
31511
  };
31523
31512
  static defaultProps = {
@@ -31687,8 +31676,8 @@ class NumberInput extends React__namespace.Component {
31687
31676
 
31688
31677
  const truth = () => true;
31689
31678
 
31690
- /* A minor abstraction on top of NumberInput for ranges
31691
- *
31679
+ /**
31680
+ * A minor abstraction on top of `NumberInput` for ranges
31692
31681
  */
31693
31682
  class RangeInput extends React__namespace.Component {
31694
31683
  static propTypes = {
@@ -32388,7 +32377,12 @@ const actions = {
32388
32377
  },
32389
32378
  polygon: {
32390
32379
  movePoint,
32391
- moveAll
32380
+ moveAll,
32381
+ addPoint,
32382
+ removePoint,
32383
+ focusPoint,
32384
+ blurPoint,
32385
+ clickPoint
32392
32386
  },
32393
32387
  quadratic: {
32394
32388
  movePoint
@@ -32527,11 +32521,341 @@ function reinitialize(params) {
32527
32521
  };
32528
32522
  }
32529
32523
 
32524
+ /**
32525
+ * Wrapper with Mafs transform styles for SVG elements.
32526
+ * Scales and translates the SVG element to match the Mafs view.
32527
+ * Use if you do not need/want to use the hook in `use-transform.ts`.
32528
+ */
32529
+ const MafsCssTransformWrapper = _ref => {
32530
+ let {
32531
+ children
32532
+ } = _ref;
32533
+ return /*#__PURE__*/React__namespace.createElement("g", {
32534
+ style: {
32535
+ transform: `var(--mafs-view-transform) var(--mafs-user-transform)`
32536
+ }
32537
+ }, children);
32538
+ };
32539
+
32540
+ const TextLabel = _ref => {
32541
+ let {
32542
+ children,
32543
+ ...rest
32544
+ } = _ref;
32545
+ return /*#__PURE__*/React__default["default"].createElement(mafs.Text, _extends({
32546
+ size: 16,
32547
+ svgTextProps: {
32548
+ filter: "url(#background)",
32549
+ fontWeight: "bold"
32550
+ }
32551
+ }, rest), children);
32552
+ };
32553
+ const SvgDefs = () => /*#__PURE__*/React__default["default"].createElement("defs", null, /*#__PURE__*/React__default["default"].createElement("filter", {
32554
+ id: "background",
32555
+ x: "-5%",
32556
+ width: "110%",
32557
+ y: "0%",
32558
+ height: "100%"
32559
+ }, /*#__PURE__*/React__default["default"].createElement("feFlood", {
32560
+ floodColor: "#FFF",
32561
+ floodOpacity: "0.64"
32562
+ }), /*#__PURE__*/React__default["default"].createElement("feComposite", {
32563
+ operator: "over",
32564
+ in: "SourceGraphic"
32565
+ })));
32566
+
32567
+ const PolygonAngle = _ref => {
32568
+ let {
32569
+ centerPoint,
32570
+ endPoints,
32571
+ range,
32572
+ polygonLines,
32573
+ showAngles,
32574
+ snapTo
32575
+ } = _ref;
32576
+ const [centerX, centerY] = centerPoint;
32577
+ const areClockwise = clockwise$1([centerPoint, ...endPoints]);
32578
+ const [[startX, startY], [endX, endY]] = areClockwise ? endPoints : endPoints.reverse();
32579
+ const radius = 0.3;
32580
+ const a = mafs.vec.dist(centerPoint, endPoints[0]);
32581
+ const b = mafs.vec.dist(centerPoint, endPoints[1]);
32582
+ const c = mafs.vec.dist(endPoints[0], endPoints[1]);
32583
+
32584
+ // Law of cosines
32585
+ const angle = Math.acos((a ** 2 + b ** 2 - c ** 2) / (2 * a * b));
32586
+ const y1 = centerY + (startY - centerY) / a * radius;
32587
+ const x2 = centerX + (endX - centerX) / b * radius;
32588
+ const x1 = centerX + (startX - centerX) / a * radius;
32589
+ const y2 = centerY + (endY - centerY) / b * radius;
32590
+
32591
+ // Fourth point that would create a rhomboid
32592
+ // Used to calculate the inside of the angle and to create square
32593
+ const [x3, y3] = mafs.vec.add(centerPoint, mafs.vec.add(mafs.vec.sub([x1, y1], centerPoint), mafs.vec.sub([x2, y2], centerPoint)));
32594
+ if (!showAngles) {
32595
+ return isRightPolygonAngle(angle) ? /*#__PURE__*/React__namespace.createElement(RightAngleSquare, {
32596
+ start: [x1, y1],
32597
+ vertex: [x2, y2],
32598
+ end: [x3, y3]
32599
+ }) : null;
32600
+ }
32601
+
32602
+ // Midpoint betwen ends of arc
32603
+ const isOutside = shouldDrawArcOutside([x3, y3], centerPoint, range, polygonLines);
32604
+ const largeArcFlag = isOutside ? 1 : 0;
32605
+ const sweepFlag = isOutside ? 1 : 0;
32606
+ const arc = `M ${x1} ${y1} A ${radius} ${radius} 0 ${largeArcFlag} ${sweepFlag} ${x2} ${y2}`;
32607
+ let angleInDegrees = angle * (180 / Math.PI);
32608
+ // If we have triggered "largArcFlag", the angle should be greater than 180
32609
+ if (isOutside) {
32610
+ angleInDegrees = 360 - angleInDegrees;
32611
+ }
32612
+
32613
+ // Only want to show whole numbers when in angles mode
32614
+ const angleLabelNumber = parseFloat(angleInDegrees.toFixed(snapTo === "angles" ? 0 : 1));
32615
+ const angleLabel = Number.isInteger(angleLabelNumber) ? angleLabelNumber : "≈ " + angleLabelNumber;
32616
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("filter", {
32617
+ id: "background",
32618
+ x: "-5%",
32619
+ width: "110%",
32620
+ y: "0%",
32621
+ height: "100%"
32622
+ }, /*#__PURE__*/React__namespace.createElement("feFlood", {
32623
+ floodColor: "#FFF",
32624
+ floodOpacity: "0.5"
32625
+ }), /*#__PURE__*/React__namespace.createElement("feComposite", {
32626
+ operator: "over",
32627
+ in: "SourceGraphic"
32628
+ }))), !isOutside && isRightAngle(angle) ? /*#__PURE__*/React__namespace.createElement(RightAngleSquare, {
32629
+ start: [x1, y1],
32630
+ vertex: [x2, y2],
32631
+ end: [x3, y3]
32632
+ }) : /*#__PURE__*/React__namespace.createElement(Arc, {
32633
+ arc: arc
32634
+ }), /*#__PURE__*/React__namespace.createElement(TextLabel, {
32635
+ x: x3,
32636
+ y: y3,
32637
+ attach: y3 - centerY > 0 ? "s" : "n",
32638
+ attachDistance: Math.abs(y3 - centerY) < 0.2 || mafs.vec.dist([x3, y3], centerPoint) < 0.3 ? 20 : 10
32639
+ }, angleLabel, "\xB0"));
32640
+ };
32641
+ const Angle = _ref2 => {
32642
+ let {
32643
+ vertex,
32644
+ coords,
32645
+ showAngles,
32646
+ allowReflexAngles,
32647
+ range
32648
+ } = _ref2;
32649
+ // Check if the points are clockwise or not, depending on whether we allow reflex angles
32650
+ const areClockwise = clockwise$1([...coords, vertex]);
32651
+ const shouldReverseCoords = areClockwise && !allowReflexAngles;
32652
+
32653
+ // Reverse the coordinates accordingly to ensure the angle is calculated correctly
32654
+ const clockwiseCoords = shouldReverseCoords ? coords : coords.reverse();
32655
+
32656
+ // Calculate the angles between the two points
32657
+ const startAngle = getAngleFromVertex(clockwiseCoords[0], vertex);
32658
+ const endAngle = getAngleFromVertex(clockwiseCoords[1], vertex);
32659
+ const angle = (startAngle + 360 - endAngle) % 360;
32660
+
32661
+ // Check if the angle is reflexive
32662
+ const isReflexive = angle > 180;
32663
+
32664
+ // Break out the necessary points for the arc calculation
32665
+ const [centerX, centerY] = vertex;
32666
+ const [point1, point2] = clockwiseCoords;
32667
+ const [startX, startY] = point1;
32668
+ const [endX, endY] = point2;
32669
+ const a = mafs.vec.dist(vertex, point1);
32670
+ const b = mafs.vec.dist(vertex, point2);
32671
+
32672
+ // Set the radius of the arc
32673
+ const radius = 2;
32674
+
32675
+ // Calculate the end points of the arc
32676
+ const y1 = centerY + (startY - centerY) / a * radius;
32677
+ const x2 = centerX + (endX - centerX) / b * radius;
32678
+ const x1 = centerX + (startX - centerX) / a * radius;
32679
+ const y2 = centerY + (endY - centerY) / b * radius;
32680
+
32681
+ // Used to calculate the inside of the angle and to create square
32682
+ const [x3, y3] = mafs.vec.add(vertex, mafs.vec.add(mafs.vec.sub([x1, y1], vertex), mafs.vec.sub([x2, y2], vertex)));
32683
+
32684
+ // Determine whether the arc should be drawn outside the angle
32685
+ const isOutside = shouldDrawArcOutside([x3, y3], vertex, range, [[vertex, point1], [vertex, point2]]);
32686
+
32687
+ // Determine the flags for the arc
32688
+ // If the angle is outside OR reflexive, we want to draw a large arc
32689
+ // If the angle is outside AND reflexive, we want to sweep the arc
32690
+ const largeArcFlag = isOutside || isReflexive ? 1 : 0;
32691
+ const sweepFlag = isOutside && isReflexive ? 1 : 0;
32692
+
32693
+ // Create the SVG path for the arc
32694
+ const arc = `M ${x1} ${y1} A ${radius} ${radius} 0 ${largeArcFlag} ${sweepFlag} ${x2} ${y2}`;
32695
+
32696
+ // We only ever want to show whole angles
32697
+ const angleLabel = parseFloat(angle.toFixed(0)); // Only want to show whole angles
32698
+
32699
+ // Calculate the text position based on the angle and whether we allow reflex angles
32700
+ // Let's try the angle bisector method to find the midpoint of the arc
32701
+ const [textX, textY] = calculateBisectorPoint(point1, point2, vertex, isReflexive, allowReflexAngles, radius);
32702
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("filter", {
32703
+ id: "background",
32704
+ x: "-5%",
32705
+ width: "110%",
32706
+ y: "0%",
32707
+ height: "100%"
32708
+ }, /*#__PURE__*/React__namespace.createElement("feFlood", {
32709
+ floodColor: "#FFF",
32710
+ floodOpacity: "0.5"
32711
+ }), /*#__PURE__*/React__namespace.createElement("feComposite", {
32712
+ operator: "over",
32713
+ in: "SourceGraphic"
32714
+ }))), isRightAngle(angle) ? /*#__PURE__*/React__namespace.createElement(RightAngleSquare, {
32715
+ start: [x1, y1],
32716
+ vertex: [x2, y2],
32717
+ end: [x3, y3],
32718
+ className: "arc-right-angle"
32719
+ }) : /*#__PURE__*/React__namespace.createElement(Arc, {
32720
+ arc: arc,
32721
+ className: "angle-arc"
32722
+ }), showAngles && /*#__PURE__*/React__namespace.createElement(TextLabel, {
32723
+ x: textX,
32724
+ y: textY,
32725
+ color: wonderBlocksTokens.color.blue
32726
+ }, angleLabel, "\xB0"));
32727
+ };
32728
+
32729
+ /**
32730
+ * This is broken out into its own component so that it can be used for an early return
32731
+ * (see line 55 or https://github.com/Khan/perseus/blob/84bbd882b11d16871e1b813a0b901f3b903d5479/packages/perseus/src/widgets/interactive-graphs/graphs/components/angle.tsx#L53-L57)
32732
+ */
32733
+ const RightAngleSquare = _ref3 => {
32734
+ let {
32735
+ start: [x1, y1],
32736
+ vertex: [x2, y2],
32737
+ end: [x3, y3],
32738
+ className
32739
+ } = _ref3;
32740
+ return /*#__PURE__*/React__namespace.createElement(MafsCssTransformWrapper, null, /*#__PURE__*/React__namespace.createElement("path", {
32741
+ d: `M ${x1} ${y1} L ${x3} ${y3} M ${x3} ${y3} L ${x2} ${y2}`,
32742
+ strokeWidth: 0.02,
32743
+ fill: "none",
32744
+ className: className
32745
+ }));
32746
+ };
32747
+
32748
+ // We're conditionally adding the class name here so that we can style the arc differently
32749
+ // based on whether it's an angle or a polygon angle
32750
+ const Arc = _ref4 => {
32751
+ let {
32752
+ arc,
32753
+ className
32754
+ } = _ref4;
32755
+ return /*#__PURE__*/React__namespace.createElement(MafsCssTransformWrapper, null, /*#__PURE__*/React__namespace.createElement("path", {
32756
+ d: arc,
32757
+ strokeWidth: 0.02,
32758
+ fill: "none",
32759
+ className: className
32760
+ }));
32761
+ };
32762
+ const isRightPolygonAngle = angle => Math.abs(angle - Math.PI / 2) < 0.01;
32763
+ const isRightAngle = angle => Math.round(angle) === 90;
32764
+
32765
+ /**
32766
+ * Determines if an angle is an inside (false) or outside (true) angle.
32767
+ * They way, we know to flip the `largeArc` and `sweepArc` flags.
32768
+ * Uses the priciple that a ray from a point inside a polygon will intersect
32769
+ * with an odd number of lines, while a ray from a point outside the polygon
32770
+ * will intersect with an even number of lines.
32771
+ * https://stackoverflow.com/questions/217578/how-can-i-determine-whether-a-2d-point-is-within-a-polygon
32772
+ */
32773
+ const shouldDrawArcOutside = (midpoint, vertex, range, polygonLines) => {
32774
+ // Create a ray from the midpoint (inside angle) to the edge of the range
32775
+ const rangeIntersectionPoint = getIntersectionOfRayWithBox(midpoint, vertex, range);
32776
+ let lineIntersectionCount = 0;
32777
+ polygonLines.forEach(line => segmentsIntersect([vertex, rangeIntersectionPoint], line) && lineIntersectionCount++);
32778
+
32779
+ // If the number of intersections is even, the angle is inside the polygon
32780
+ return !isEven(lineIntersectionCount);
32781
+ };
32782
+ const isEven = n => n % 2 === 0;
32783
+
32784
+ // This function calculates the bisector point of an angle formed by two points
32785
+ // and a vertex. It is used to position the angle label so that it is always
32786
+ // outside the arc at the exact midpoint of the angle.
32787
+ // Reflex angles make use of polar coordinates to determine the correct direction
32788
+ function calculateBisectorPoint(point1, point2, vertex, isReflex, allowReflex, arcRadius) {
32789
+ const [originX, originY] = vertex;
32790
+ const [x1, y1] = point1;
32791
+ const [x2, y2] = point2;
32792
+
32793
+ // Convert the Cartesian Coordinates to vectors
32794
+ const vectorA = [x1 - originX, y1 - originY];
32795
+ const vectorB = [x2 - originX, y2 - originY];
32796
+
32797
+ // Calculate the average angle of the vectors
32798
+ // so that we can point the bisector in the correct direction using polar coordinates
32799
+ const angleA = Math.atan2(vectorA[1], vectorA[0]);
32800
+ const angleB = Math.atan2(vectorB[1], vectorB[0]);
32801
+
32802
+ // Calculate the average of these two angles
32803
+ let averageAngle = (angleA + angleB) / 2;
32804
+ const angleRadians = Math.abs(angleA - angleB);
32805
+
32806
+ // Adjust the average angle to point in the correct direction
32807
+ // and to account for reflex angles and a bug with 180 degrees
32808
+ // that occurs due to our angleOffsetDegrees option in the graph
32809
+ if (allowReflex) {
32810
+ // If the resulting angles are less than 180 degrees,
32811
+ // or if the user has flipped the points on the interactive element
32812
+ // ensure that we have the correct direction for the reflex angle
32813
+ if (angleRadians <= Math.PI && isReflex || angleB > angleA) {
32814
+ averageAngle += Math.PI; // Add 180 degrees
32815
+ }
32816
+ } else {
32817
+ // If the resulting angles are greater than 180 degrees, adjust
32818
+ // to get the correct angle, as we already know that the angle is not reflexive
32819
+ // This fixes a bug that occurs at 180 degrees due to our angleOffsetDegrees option in the graph
32820
+ if (angleRadians > Math.PI) {
32821
+ averageAngle -= Math.PI; // Subract 180 degrees
32822
+ }
32823
+ }
32824
+
32825
+ // Convert the average angle back to Cartesian coordinates
32826
+ const sum = [Math.cos(averageAngle), Math.sin(averageAngle)];
32827
+
32828
+ // Calculate the magnitude of the sum to normalize it
32829
+ const sumMagnitude = Math.sqrt(sum[0] ** 2 + sum[1] ** 2);
32830
+ const bisectorDirection = [sum[0] / sumMagnitude, sum[1] / sumMagnitude];
32831
+
32832
+ // Calculate the initial distance of the bisector direction from the origin
32833
+ const initialDistance = Math.sqrt(bisectorDirection[0] ** 2 + bisectorDirection[1] ** 2);
32834
+
32835
+ // Determine the minimum radius to ensure that the text is always outside the arc
32836
+ const requiredDistance = arcRadius * 1.75;
32837
+ let radius = requiredDistance / initialDistance;
32838
+
32839
+ // Apply radius only if the initial distance is less than required
32840
+ if (initialDistance >= requiredDistance) {
32841
+ radius = 1; // Use the unit vector as is
32842
+ }
32843
+
32844
+ // Scale the bisector direction by the radius
32845
+ const bisectorPoint = [bisectorDirection[0] * radius, bisectorDirection[1] * radius];
32846
+
32847
+ // Add the vertex to the bisector point to get the final position
32848
+ // to ensure that the angle label moves with the interactive element
32849
+ const scaledBisector = mafs.vec.add(bisectorPoint, vertex);
32850
+ return scaledBisector;
32851
+ }
32852
+
32530
32853
  /**
32531
32854
  * 44 is touch best practice and AAA compliant for WCAG
32532
32855
  * https://www.w3.org/WAI/WCAG21/Understanding/target-size.html
32533
32856
  */
32534
32857
  const TARGET_SIZE = 44;
32858
+ const REMOVE_BUTTON_ID = "perseus_mafs_remove_button";
32535
32859
 
32536
32860
  // same as pointsFromNormalized in interactive-graph.tsx
32537
32861
  const normalizePoints = (range, step, coordsList, noSnap) => coordsList.map(coords =>
@@ -32563,6 +32887,9 @@ function bound$1(_ref) {
32563
32887
  const boundingBox = inset(snapStep, range);
32564
32888
  return clampToBox(boundingBox, point);
32565
32889
  }
32890
+ function isUnlimitedGraphState(state) {
32891
+ return state.type === "point" && state.numPoints === "unlimited" || state.type === "polygon" && state.numSides === "unlimited";
32892
+ }
32566
32893
 
32567
32894
  var isProduction = process.env.NODE_ENV === 'production';
32568
32895
  var prefix = 'Invariant failed';
@@ -32730,6 +33057,14 @@ function range(min, max) {
32730
33057
  return result;
32731
33058
  }
32732
33059
 
33060
+ function srFormatNumber(a, locale) {
33061
+ // adding zero here converts negative zero to positive zero.
33062
+ return (0 + a).toLocaleString(locale, {
33063
+ maximumFractionDigits: 3,
33064
+ useGrouping: false // no thousands separators
33065
+ });
33066
+ }
33067
+
32733
33068
  // The hitbox size of 48px by 48px is preserved from the legacy interactive
32734
33069
  // graph.
32735
33070
  const hitboxSizePx = 48;
@@ -32737,25 +33072,18 @@ const hitboxSizePx = 48;
32737
33072
  // MovablePointView is a purely presentational component (i.e. it is a pure
32738
33073
  // function with no state or effects) that renders the SVG for a movable point
32739
33074
  // on an interactive graph.
32740
- //
32741
- // It has two modes for managing tabbing / focus: "controlled" (where the caller
32742
- // manages the display of the focus ring, and the point itself cannot be
32743
- // focused) and "uncontrolled" (where the point is focusable, and the browser
32744
- // manages the focus state and styling). For context on why we did this, see
32745
- // the description of https://github.com/Khan/perseus/pull/1240
32746
33075
  const MovablePointView = /*#__PURE__*/React.forwardRef((props, hitboxRef) => {
32747
33076
  const {
32748
33077
  range,
32749
33078
  markings,
32750
- showTooltips,
32751
- disableKeyboardInteraction
33079
+ showTooltips
32752
33080
  } = useGraphConfig();
32753
33081
  const {
32754
33082
  point,
32755
33083
  color = wonderBlocksTokens.color.blue,
32756
33084
  dragging,
32757
- focusBehavior,
32758
33085
  cursor,
33086
+ showFocusRing,
32759
33087
  onClick = () => {}
32760
33088
  } = props;
32761
33089
 
@@ -32765,7 +33093,7 @@ const MovablePointView = /*#__PURE__*/React.forwardRef((props, hitboxRef) => {
32765
33093
  let [_, value] = _ref;
32766
33094
  return value === color;
32767
33095
  })?.[0] ?? "blue";
32768
- const pointClasses = `movable-point ${dragging ? "movable-point--dragging" : ""} ${focusClass(focusBehavior)}`;
33096
+ const pointClasses = classNames("movable-point", dragging && "movable-point--dragging", showFocusRing && "movable-point--focus");
32769
33097
  const [[x, y]] = useTransformVectorsToPixels(point);
32770
33098
  const [[xMin, xMax], [yMin, yMax]] = range;
32771
33099
  const [[verticalStartX]] = useTransformVectorsToPixels([xMin, 0]);
@@ -32794,15 +33122,6 @@ const MovablePointView = /*#__PURE__*/React.forwardRef((props, hitboxRef) => {
32794
33122
  cursor
32795
33123
  },
32796
33124
  "data-testid": "movable-point",
32797
- tabIndex: disableKeyboardInteraction ? -1 : tabIndex(focusBehavior),
32798
- onFocus: event => {
32799
- const callback = getOnFocusChangeCallback(focusBehavior);
32800
- callback && callback(event, true);
32801
- },
32802
- onBlur: event => {
32803
- const callback = getOnFocusChangeCallback(focusBehavior);
32804
- callback && callback(event, false);
32805
- },
32806
33125
  onClick: onClick
32807
33126
  }, /*#__PURE__*/React__namespace.createElement("circle", {
32808
33127
  className: "movable-point-hitbox",
@@ -32839,25 +33158,115 @@ const MovablePointView = /*#__PURE__*/React.forwardRef((props, hitboxRef) => {
32839
33158
  }
32840
33159
  }, svgForPoint) : svgForPoint);
32841
33160
  });
32842
- function focusClass(config) {
32843
- if (config.type === "controlled" && config.showFocusRing) {
32844
- return "movable-point--focus";
33161
+
33162
+ // TODO(benchristel): Move this to a more central location if it's reused.
33163
+ // Or install the library.
33164
+ function classNames() {
33165
+ for (var _len = arguments.length, names = new Array(_len), _key = 0; _key < _len; _key++) {
33166
+ names[_key] = arguments[_key];
32845
33167
  }
32846
- return "";
33168
+ return names.filter(Boolean).join(" ");
32847
33169
  }
32848
- function tabIndex(config) {
32849
- if (config.type === "uncontrolled") {
32850
- return config.tabIndex;
32851
- }
32852
- return undefined;
33170
+
33171
+ function useControlPoint(params) {
33172
+ const {
33173
+ snapStep,
33174
+ disableKeyboardInteraction
33175
+ } = useGraphConfig();
33176
+ const {
33177
+ point,
33178
+ color,
33179
+ cursor,
33180
+ constrain = p => snap(snapStep, p),
33181
+ onMove = noop$1,
33182
+ onClick = noop$1,
33183
+ onFocus = noop$1,
33184
+ onBlur = noop$1,
33185
+ forwardedRef = noop$1
33186
+ } = params;
33187
+ const {
33188
+ strings,
33189
+ locale
33190
+ } = usePerseusI18n();
33191
+ const [focused, setFocused] = React.useState(false);
33192
+ const focusableHandleRef = React.useRef(null);
33193
+ useDraggable({
33194
+ gestureTarget: focusableHandleRef,
33195
+ point,
33196
+ onMove,
33197
+ constrainKeyboardMovement: constrain
33198
+ });
33199
+ const visiblePointRef = React.useRef(null);
33200
+ const {
33201
+ dragging
33202
+ } = useDraggable({
33203
+ gestureTarget: visiblePointRef,
33204
+ point,
33205
+ onMove,
33206
+ constrainKeyboardMovement: constrain
33207
+ });
33208
+ React.useLayoutEffect(() => {
33209
+ setForwardedRef(forwardedRef, focusableHandleRef.current);
33210
+ }, [forwardedRef]);
33211
+ const focusableHandle = /*#__PURE__*/React__namespace.createElement("g", {
33212
+ "data-testid": "movable-point__focusable-handle",
33213
+ className: "movable-point__focusable-handle",
33214
+ tabIndex: disableKeyboardInteraction ? -1 : 0,
33215
+ ref: focusableHandleRef,
33216
+ role: "button",
33217
+ "aria-label": strings.srPointAtCoordinates({
33218
+ x: srFormatNumber(point[X], locale),
33219
+ y: srFormatNumber(point[Y], locale)
33220
+ })
33221
+ // aria-live="assertive" causes the new location of the point to be
33222
+ // announced immediately on move.
33223
+ ,
33224
+ "aria-live": "assertive",
33225
+ onFocus: event => {
33226
+ onFocus(event);
33227
+ setFocused(true);
33228
+ },
33229
+ onBlur: event => {
33230
+ onBlur(event);
33231
+ setFocused(false);
33232
+ }
33233
+ });
33234
+ const visiblePoint = /*#__PURE__*/React__namespace.createElement(MovablePointView, {
33235
+ cursor: cursor,
33236
+ onClick: () => {
33237
+ onClick();
33238
+ focusableHandleRef.current?.focus();
33239
+ },
33240
+ point: point,
33241
+ dragging: dragging,
33242
+ color: color,
33243
+ ref: visiblePointRef,
33244
+ showFocusRing: focused
33245
+ });
33246
+ return {
33247
+ focusableHandle,
33248
+ visiblePoint,
33249
+ focusableHandleRef,
33250
+ visiblePointRef
33251
+ };
32853
33252
  }
32854
- function getOnFocusChangeCallback(config) {
32855
- if (config.type === "uncontrolled") {
32856
- return config.onFocusChange;
33253
+
33254
+ // TODO(benchristel): move this to a more central place if we want to reuse it.
33255
+ function setForwardedRef(ref, value) {
33256
+ if (typeof ref === "function") {
33257
+ ref(value);
33258
+ } else if (ref !== null) {
33259
+ ref.current = value;
32857
33260
  }
32858
- return () => {};
32859
33261
  }
32860
33262
 
33263
+ // This `noop` const is an optimization. Above, we use the forwardedRef
33264
+ // variable, which defaults to `noop`, in a useEffect deps array. Using `noop`
33265
+ // as the default value instead of `() => {}` ensures that the forwardedRef
33266
+ // variable will have the same value on repeated renders, preventing the
33267
+ // useEffect callback from re-running needlessly.
33268
+ const noop$1 = () => {};
33269
+
32861
33270
  const MovableLine = props => {
32862
33271
  const {
32863
33272
  onMoveLine = () => {},
@@ -32881,11 +33290,19 @@ const MovableLine = props => {
32881
33290
  const {
32882
33291
  visiblePoint: visiblePoint1,
32883
33292
  focusableHandle: focusableHandle1
32884
- } = useControlPoint(start, color, p => onMovePoint(0, p));
33293
+ } = useControlPoint({
33294
+ point: start,
33295
+ color,
33296
+ onMove: p => onMovePoint(0, p)
33297
+ });
32885
33298
  const {
32886
33299
  visiblePoint: visiblePoint2,
32887
33300
  focusableHandle: focusableHandle2
32888
- } = useControlPoint(end, color, p => onMovePoint(1, p));
33301
+ } = useControlPoint({
33302
+ point: end,
33303
+ color,
33304
+ onMove: p => onMovePoint(1, p)
33305
+ });
32889
33306
  const line = /*#__PURE__*/React__namespace.createElement(Line$1, {
32890
33307
  start: start,
32891
33308
  end: end,
@@ -32895,51 +33312,6 @@ const MovableLine = props => {
32895
33312
  });
32896
33313
  return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, focusableHandle1, line, focusableHandle2, visiblePoint1, visiblePoint2);
32897
33314
  };
32898
- function useControlPoint(point, color, onMovePoint) {
32899
- const {
32900
- snapStep,
32901
- disableKeyboardInteraction
32902
- } = useGraphConfig();
32903
- const [focused, setFocused] = React.useState(false);
32904
- const keyboardHandleRef = React.useRef(null);
32905
- useDraggable({
32906
- gestureTarget: keyboardHandleRef,
32907
- point,
32908
- onMove: onMovePoint,
32909
- constrainKeyboardMovement: p => snap(snapStep, p)
32910
- });
32911
- const visiblePointRef = React.useRef(null);
32912
- const {
32913
- dragging
32914
- } = useDraggable({
32915
- gestureTarget: visiblePointRef,
32916
- point,
32917
- onMove: onMovePoint,
32918
- constrainKeyboardMovement: p => snap(snapStep, p)
32919
- });
32920
- const focusableHandle = /*#__PURE__*/React__namespace.createElement("g", {
32921
- "data-testid": "movable-point__focusable-handle",
32922
- className: "movable-point__focusable-handle",
32923
- tabIndex: disableKeyboardInteraction ? -1 : 0,
32924
- ref: keyboardHandleRef,
32925
- onFocus: () => setFocused(true),
32926
- onBlur: () => setFocused(false)
32927
- });
32928
- const visiblePoint = /*#__PURE__*/React__namespace.createElement(MovablePointView, {
32929
- point: point,
32930
- dragging: dragging,
32931
- color: color,
32932
- ref: visiblePointRef,
32933
- focusBehavior: {
32934
- type: "controlled",
32935
- showFocusRing: focused
32936
- }
32937
- });
32938
- return {
32939
- focusableHandle,
32940
- visiblePoint
32941
- };
32942
- }
32943
33315
  const defaultStroke = "var(--movable-line-stroke-color)";
32944
33316
  const Line$1 = props => {
32945
33317
  const {
@@ -33029,25 +33401,351 @@ function trimRange(range, graphDimensionsInPixels) {
33029
33401
  return inset([graphUnitsToTrimX, graphUnitsToTrimY], range);
33030
33402
  }
33031
33403
 
33032
- const SegmentGraph = props => {
33404
+ const MovablePoint$1 = /*#__PURE__*/React__namespace.forwardRef((props, pointRef) => {
33033
33405
  const {
33034
- dispatch
33406
+ visiblePoint,
33407
+ focusableHandle
33408
+ } = useControlPoint({
33409
+ ...props,
33410
+ forwardedRef: pointRef
33411
+ });
33412
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, focusableHandle, visiblePoint);
33413
+ });
33414
+
33415
+ function renderAngleGraph(state, dispatch) {
33416
+ return {
33417
+ graph: /*#__PURE__*/React__namespace.createElement(AngleGraph, {
33418
+ graphState: state,
33419
+ dispatch: dispatch
33420
+ }),
33421
+ interactiveElementsDescription: null
33422
+ };
33423
+ }
33424
+ function AngleGraph(props) {
33425
+ const {
33426
+ dispatch,
33427
+ graphState
33035
33428
  } = props;
33036
33429
  const {
33037
- coords: segments
33038
- } = props.graphState;
33039
- return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, segments?.map((segment, i) => /*#__PURE__*/React__namespace.createElement(MovableLine, {
33040
- key: i,
33041
- points: segment,
33042
- onMoveLine: delta => {
33043
- dispatch(actions.segment.moveLine(i, delta));
33044
- },
33045
- onMovePoint: (endpointIndex, destination) => {
33046
- dispatch(actions.segment.movePointInFigure(i, endpointIndex, destination));
33430
+ graphDimensionsInPixels
33431
+ } = useGraphConfig();
33432
+ const {
33433
+ coords,
33434
+ showAngles,
33435
+ range,
33436
+ allowReflexAngles,
33437
+ snapDegrees
33438
+ } = graphState;
33439
+
33440
+ // Break the coords into the two end points and the center point
33441
+ const endPoints = [coords[0], coords[2]];
33442
+ const centerPoint = coords[1];
33443
+
33444
+ // Convert the vectors to pixels for rendering the svg lines
33445
+ const angleLines = [[centerPoint, endPoints[0]], [centerPoint, endPoints[1]]];
33446
+ const linePixelCoords = [useTransformVectorsToPixels(centerPoint, endPoints[0]), useTransformVectorsToPixels(centerPoint, endPoints[1])];
33447
+
33448
+ // Create the SVG lines for the angle
33449
+ const svgLines = linePixelCoords.map((_ref, i) => {
33450
+ let [startPtPx, endPtPx] = _ref;
33451
+ const trimmedRange = trimRange(range, graphDimensionsInPixels);
33452
+ const endExtend = getIntersectionOfRayWithBox(angleLines[i][0], angleLines[i][1], trimmedRange);
33453
+ return /*#__PURE__*/React__namespace.createElement("g", {
33454
+ key: `line-${i}`
33455
+ }, /*#__PURE__*/React__namespace.createElement(SVGLine, {
33456
+ start: startPtPx,
33457
+ end: endPtPx,
33458
+ style: {
33459
+ stroke: "var(--movable-line-stroke-color)",
33460
+ strokeWidth: "var(--movable-line-stroke-weight)"
33461
+ }
33462
+ }), /*#__PURE__*/React__namespace.createElement(Vector, {
33463
+ tail: angleLines[i][1],
33464
+ tip: endExtend,
33465
+ color: "var(--movable-line-stroke-color)"
33466
+ }));
33467
+ });
33468
+
33469
+ // Create the angle indicator parameters
33470
+ const angleParams = {
33471
+ vertex: centerPoint,
33472
+ coords: endPoints,
33473
+ allowReflexAngles: allowReflexAngles || false,
33474
+ // Whether to allow reflex angles or not
33475
+ snapDegrees: snapDegrees || 1,
33476
+ // The multiple of degrees to snap to
33477
+ range: range,
33478
+ showAngles: showAngles || false // Whether to show the angle or not
33479
+ };
33480
+
33481
+ // Render the lines, angle, and then movable points
33482
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, svgLines, /*#__PURE__*/React__namespace.createElement(Angle, angleParams), /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
33483
+ point: coords[1],
33484
+ constrain: p => p,
33485
+ onMove: destination => dispatch(actions.angle.movePoint(1, destination))
33486
+ }), /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
33487
+ point: coords[0],
33488
+ constrain: getAngleSideConstraint(coords[0], coords[1], snapDegrees || 1),
33489
+ onMove: destination => dispatch(actions.angle.movePoint(0, destination))
33490
+ }), /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
33491
+ point: coords[2],
33492
+ constrain: getAngleSideConstraint(coords[2], coords[1], snapDegrees || 1),
33493
+ onMove: destination => dispatch(actions.angle.movePoint(2, destination))
33494
+ }));
33495
+ }
33496
+ const positiveX = [1, 0];
33497
+ const negativeX = [-1, 0];
33498
+ const positiveY = [0, 1];
33499
+ const negativeY = [0, -1];
33500
+ function getAngleSideConstraint(sidePoint, vertex, snapDegrees) {
33501
+ const currentAngle = calculateAngleInDegrees(mafs.vec.sub(sidePoint, vertex));
33502
+
33503
+ // Find the rays that start at the current point and point up, down, left
33504
+ // and right.
33505
+ const leftRay = [sidePoint, mafs.vec.add(sidePoint, negativeX)];
33506
+ const rightRay = [sidePoint, mafs.vec.add(sidePoint, positiveX)];
33507
+ const upRay = [sidePoint, mafs.vec.add(sidePoint, positiveY)];
33508
+ const downRay = [sidePoint, mafs.vec.add(sidePoint, negativeY)];
33509
+
33510
+ // find the angles that lie one snap step clockwise and counter-clockwise
33511
+ // from the current angle. These are the angles to which the side can be
33512
+ // moved.
33513
+ const oneStepCounterClockwise = currentAngle + snapDegrees;
33514
+ const oneStepClockwise = currentAngle - snapDegrees;
33515
+
33516
+ // find the rays that start from the vertex and point in the direction of
33517
+ // the angles we just computed.
33518
+ const counterClockwiseRay = [vertex, mafs.vec.add(vertex, polar(1, oneStepCounterClockwise))];
33519
+ const clockwiseRay = [vertex, mafs.vec.add(vertex, polar(1, oneStepClockwise))];
33520
+
33521
+ // find the intersections of those rays with the horizontal and vertical
33522
+ // rays extending from the sidePoint. These intersections are the points to
33523
+ // which sidePoint can move that will result in a rotation of `snapDegrees`.
33524
+ const left = findIntersectionOfRays(leftRay, counterClockwiseRay) ?? findIntersectionOfRays(leftRay, clockwiseRay);
33525
+ const right = findIntersectionOfRays(rightRay, counterClockwiseRay) ?? findIntersectionOfRays(rightRay, clockwiseRay);
33526
+ const up = findIntersectionOfRays(upRay, counterClockwiseRay) ?? findIntersectionOfRays(upRay, clockwiseRay);
33527
+ const down = findIntersectionOfRays(downRay, counterClockwiseRay) ?? findIntersectionOfRays(downRay, clockwiseRay);
33528
+ return {
33529
+ up: up ?? sidePoint,
33530
+ down: down ?? sidePoint,
33531
+ left: left ?? sidePoint,
33532
+ right: right ?? sidePoint
33533
+ };
33534
+ }
33535
+
33536
+ function getGradableGraph(state, initialGraph) {
33537
+ if (!state.hasBeenInteractedWith) {
33538
+ return {
33539
+ ...initialGraph
33540
+ };
33541
+ }
33542
+ if (initialGraph.type === "linear-system" && state.type === "linear-system") {
33543
+ return {
33544
+ ...initialGraph,
33545
+ coords: state.coords
33546
+ };
33547
+ }
33548
+ if (state.type === "segment" && initialGraph.type === "segment") {
33549
+ return {
33550
+ ...initialGraph,
33551
+ coords: state.coords
33552
+ };
33553
+ }
33554
+ if (state.type === "linear" && initialGraph.type === "linear") {
33555
+ return {
33556
+ ...initialGraph,
33557
+ coords: state.coords
33558
+ };
33559
+ }
33560
+ if (state.type === "ray" && initialGraph.type === "ray") {
33561
+ return {
33562
+ ...initialGraph,
33563
+ coords: state.coords
33564
+ };
33565
+ }
33566
+ if (state.type === "polygon" && initialGraph.type === "polygon") {
33567
+ return {
33568
+ ...initialGraph,
33569
+ coords: state.coords
33570
+ };
33571
+ }
33572
+ if (state.type === "point" && initialGraph.type === "point") {
33573
+ return {
33574
+ ...initialGraph,
33575
+ coords: state.coords
33576
+ };
33577
+ }
33578
+ if (state.type === "circle" && initialGraph.type === "circle") {
33579
+ return {
33580
+ ...initialGraph,
33581
+ center: state.center,
33582
+ radius: getRadius(state)
33583
+ };
33584
+ }
33585
+ if (state.type === "quadratic" && initialGraph.type === "quadratic") {
33586
+ return {
33587
+ ...initialGraph,
33588
+ coords: state.coords
33589
+ };
33590
+ }
33591
+ if (state.type === "sinusoid" && initialGraph.type === "sinusoid") {
33592
+ return {
33593
+ ...initialGraph,
33594
+ coords: state.coords
33595
+ };
33596
+ }
33597
+ if (state.type === "angle" && initialGraph.type === "angle") {
33598
+ // We're going to reverse the coords for scoring if the angle is clockwise and
33599
+ // we don't allow reflex angles. This is because the angle is largely defined
33600
+ // by the order of the points in the coords array, and we want to maintain
33601
+ // the same angle scoring with the legacy graph (which had a bug).
33602
+ // (LEMS-2190): When we remove the legacy graph, move this logic to the scoring function.
33603
+ const areClockwise = clockwise$1([state.coords[0], state.coords[2], state.coords[1]]);
33604
+ const shouldReverseCoords = areClockwise && !state.allowReflexAngles;
33605
+ const coords = shouldReverseCoords ? state.coords.slice().reverse() : state.coords;
33606
+ return {
33607
+ ...initialGraph,
33608
+ coords,
33609
+ allowReflexAngles: state.allowReflexAngles
33610
+ };
33611
+ }
33612
+ if (state.type === "none" && initialGraph.type === "none") {
33613
+ return {
33614
+ type: "none"
33615
+ };
33616
+ }
33617
+ throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
33618
+ }
33619
+
33620
+ /**
33621
+ * determine radius of a circle graph
33622
+ *
33623
+ * @param graph - the graph object containing the circle
33624
+ * @returns the radius of the circle
33625
+ */
33626
+ function getRadius(graph) {
33627
+ const [centerX, centerY] = graph.center;
33628
+ const [edgeX, edgeY] = graph.radiusPoint;
33629
+ return Math.sqrt(Math.pow(edgeX - centerX, 2) + Math.pow(edgeY - centerY, 2));
33630
+ }
33631
+
33632
+ function renderCircleGraph(state, dispatch) {
33633
+ return {
33634
+ graph: /*#__PURE__*/React__namespace.createElement(CircleGraph, {
33635
+ graphState: state,
33636
+ dispatch: dispatch
33637
+ }),
33638
+ interactiveElementsDescription: null
33639
+ };
33640
+ }
33641
+ function CircleGraph(props) {
33642
+ const {
33643
+ dispatch,
33644
+ graphState
33645
+ } = props;
33646
+ const {
33647
+ center,
33648
+ radiusPoint
33649
+ } = graphState;
33650
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(MovableCircle, {
33651
+ center: center,
33652
+ radius: getRadius(graphState),
33653
+ onMove: c => dispatch(actions.circle.moveCenter(c))
33654
+ }), /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
33655
+ point: radiusPoint,
33656
+ cursor: "ew-resize",
33657
+ onMove: newRadiusPoint => {
33658
+ dispatch(actions.circle.moveRadiusPoint(newRadiusPoint));
33047
33659
  }
33048
- })));
33049
- };
33660
+ }));
33661
+ }
33662
+ function MovableCircle(props) {
33663
+ const {
33664
+ center,
33665
+ radius,
33666
+ onMove
33667
+ } = props;
33668
+ const {
33669
+ snapStep,
33670
+ disableKeyboardInteraction
33671
+ } = useGraphConfig();
33672
+ const draggableRef = React.useRef(null);
33673
+ const {
33674
+ dragging
33675
+ } = useDraggable({
33676
+ gestureTarget: draggableRef,
33677
+ point: center,
33678
+ onMove,
33679
+ constrainKeyboardMovement: p => snap(snapStep, p)
33680
+ });
33681
+ const [centerPx] = useTransformVectorsToPixels(center);
33682
+ const [radiiPx] = useTransformDimensionsToPixels([radius, radius]);
33683
+ return /*#__PURE__*/React__namespace.createElement("g", {
33684
+ ref: draggableRef,
33685
+ tabIndex: disableKeyboardInteraction ? -1 : 0,
33686
+ className: `movable-circle ${dragging ? "movable-circle--dragging" : ""}`
33687
+ }, /*#__PURE__*/React__namespace.createElement("ellipse", {
33688
+ className: "focus-ring",
33689
+ cx: centerPx[X],
33690
+ cy: centerPx[Y],
33691
+ rx: radiiPx[X] + 3,
33692
+ ry: radiiPx[Y] + 3
33693
+ }), /*#__PURE__*/React__namespace.createElement("ellipse", {
33694
+ className: "circle",
33695
+ cx: centerPx[X],
33696
+ cy: centerPx[Y],
33697
+ rx: radiiPx[X],
33698
+ ry: radiiPx[Y]
33699
+ }), /*#__PURE__*/React__namespace.createElement(DragHandle, {
33700
+ center: center
33701
+ }));
33702
+ }
33703
+ const dragHandleDimensions = [24, 14];
33704
+ const dragHandleDotPositions = crossProduct([-4.4, 0, 4.4], [-2.1, 2.1]);
33705
+ function DragHandle(props) {
33706
+ const {
33707
+ center
33708
+ } = props;
33709
+ const cornerRadius = Math.min(...dragHandleDimensions) / 2;
33710
+ const [centerPx] = useTransformVectorsToPixels(center);
33711
+ const topLeft = mafs.vec.sub(centerPx, mafs.vec.scale(dragHandleDimensions, 0.5));
33712
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("rect", {
33713
+ className: "movable-circle-handle",
33714
+ x: topLeft[X],
33715
+ y: topLeft[Y],
33716
+ width: dragHandleDimensions[X],
33717
+ height: dragHandleDimensions[Y],
33718
+ rx: cornerRadius,
33719
+ ry: cornerRadius
33720
+ }), dragHandleDotPositions.map(offsetPx => {
33721
+ const [xPx, yPx] = mafs.vec.add(offsetPx, centerPx);
33722
+ return /*#__PURE__*/React__namespace.createElement("circle", {
33723
+ key: `circle-${xPx}-${yPx}`,
33724
+ className: "movable-circle-handle-dot",
33725
+ cx: xPx,
33726
+ cy: yPx
33727
+ });
33728
+ }));
33729
+ }
33730
+ function crossProduct(as, bs) {
33731
+ const result = [];
33732
+ for (const a of as) {
33733
+ for (const b of bs) {
33734
+ result.push([a, b]);
33735
+ }
33736
+ }
33737
+ return result;
33738
+ }
33050
33739
 
33740
+ function renderLinearGraph(state, dispatch) {
33741
+ return {
33742
+ graph: /*#__PURE__*/React__namespace.createElement(LinearGraph, {
33743
+ graphState: state,
33744
+ dispatch: dispatch
33745
+ }),
33746
+ interactiveElementsDescription: null
33747
+ };
33748
+ }
33051
33749
  const LinearGraph = (props, key) => {
33052
33750
  const {
33053
33751
  dispatch
@@ -33074,6 +33772,15 @@ const LinearGraph = (props, key) => {
33074
33772
  });
33075
33773
  };
33076
33774
 
33775
+ function renderLinearSystemGraph(state, dispatch) {
33776
+ return {
33777
+ graph: /*#__PURE__*/React__namespace.createElement(LinearSystemGraph, {
33778
+ graphState: state,
33779
+ dispatch: dispatch
33780
+ }),
33781
+ interactiveElementsDescription: null
33782
+ };
33783
+ }
33077
33784
  const LinearSystemGraph = props => {
33078
33785
  const {
33079
33786
  dispatch
@@ -33096,412 +33803,125 @@ const LinearSystemGraph = props => {
33096
33803
  })), ";");
33097
33804
  };
33098
33805
 
33099
- const RayGraph = props => {
33806
+ function renderPointGraph(state, dispatch) {
33807
+ return {
33808
+ graph: /*#__PURE__*/React__namespace.createElement(PointGraph, {
33809
+ graphState: state,
33810
+ dispatch: dispatch
33811
+ }),
33812
+ interactiveElementsDescription: /*#__PURE__*/React__namespace.createElement(PointGraphDescription, {
33813
+ state: state
33814
+ })
33815
+ };
33816
+ }
33817
+ function PointGraph(props) {
33818
+ const numPoints = props.graphState.numPoints;
33819
+ if (numPoints === "unlimited") {
33820
+ return UnlimitedPointGraph(props);
33821
+ }
33822
+ return LimitedPointGraph(props);
33823
+ }
33824
+ function LimitedPointGraph(props) {
33100
33825
  const {
33101
33826
  dispatch
33102
33827
  } = props;
33828
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, props.graphState.coords.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
33829
+ key: i,
33830
+ point: point,
33831
+ onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination))
33832
+ })));
33833
+ }
33834
+ function UnlimitedPointGraph(props) {
33103
33835
  const {
33104
- coords: line
33105
- } = props.graphState;
33106
- const handleMoveLine = delta => dispatch(actions.ray.moveRay(delta));
33107
- const handleMovePoint = (pointIndex, newPoint) => dispatch(actions.ray.movePoint(pointIndex, newPoint));
33108
-
33109
- // Ray graphs only have one line
33110
- return /*#__PURE__*/React__namespace.createElement(MovableLine, {
33111
- points: line,
33112
- onMoveLine: handleMoveLine,
33113
- onMovePoint: handleMovePoint,
33114
- extend: {
33115
- start: false,
33116
- end: true
33836
+ dispatch
33837
+ } = props;
33838
+ const graphConfig = useGraphConfig();
33839
+ const {
33840
+ range: [[minX, maxX], [minY, maxY]]
33841
+ } = graphConfig;
33842
+ const width = maxX - minX;
33843
+ const height = maxY - minY;
33844
+ const [[widthPx, heightPx]] = useTransformDimensionsToPixels([width, height]);
33845
+ const [[left, top]] = useTransformVectorsToPixels([minX, maxY]);
33846
+ const itemsRef = React__namespace.useRef([]);
33847
+ React__namespace.useEffect(() => {
33848
+ const focusedIndex = props.graphState.focusedPointIndex;
33849
+ if (focusedIndex != null) {
33850
+ itemsRef.current[focusedIndex]?.focus();
33117
33851
  }
33118
- });
33119
- };
33120
-
33121
- /**
33122
- * Wrapper with Mafs transform styles for SVG elements.
33123
- * Scales and translates the SVG element to match the Mafs view.
33124
- * Use if you do not need/want to use the hook in `use-transform.ts`.
33125
- */
33126
- const MafsCssTransformWrapper = _ref => {
33127
- let {
33128
- children
33129
- } = _ref;
33130
- return /*#__PURE__*/React__namespace.createElement("g", {
33852
+ }, [props.graphState.focusedPointIndex, itemsRef]);
33853
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("rect", {
33131
33854
  style: {
33132
- transform: `var(--mafs-view-transform) var(--mafs-user-transform)`
33855
+ fill: "rgba(0,0,0,0)",
33856
+ cursor: "crosshair"
33857
+ },
33858
+ width: widthPx,
33859
+ height: heightPx,
33860
+ x: left,
33861
+ y: top,
33862
+ onClick: event => {
33863
+ const elementRect = event.currentTarget.getBoundingClientRect();
33864
+ const x = event.clientX - elementRect.x;
33865
+ const y = event.clientY - elementRect.y;
33866
+ const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
33867
+ dispatch(actions.pointGraph.addPoint(graphCoordinates[0]));
33133
33868
  }
33134
- }, children);
33135
- };
33136
-
33137
- const TextLabel = _ref => {
33138
- let {
33139
- children,
33140
- ...rest
33141
- } = _ref;
33142
- return /*#__PURE__*/React__default["default"].createElement(mafs.Text, _extends({
33143
- size: 16,
33144
- svgTextProps: {
33145
- filter: "url(#background)",
33146
- fontWeight: "bold"
33869
+ }), props.graphState.coords.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
33870
+ key: i,
33871
+ point: point,
33872
+ onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
33873
+ ref: ref => {
33874
+ itemsRef.current[i] = ref;
33875
+ },
33876
+ onFocus: () => {
33877
+ dispatch(actions.pointGraph.focusPoint(i));
33878
+ },
33879
+ onClick: () => {
33880
+ dispatch(actions.pointGraph.clickPoint(i));
33147
33881
  }
33148
- }, rest), children);
33149
- };
33150
- const SvgDefs = () => /*#__PURE__*/React__default["default"].createElement("defs", null, /*#__PURE__*/React__default["default"].createElement("filter", {
33151
- id: "background",
33152
- x: "-5%",
33153
- width: "110%",
33154
- y: "0%",
33155
- height: "100%"
33156
- }, /*#__PURE__*/React__default["default"].createElement("feFlood", {
33157
- floodColor: "#FFF",
33158
- floodOpacity: "0.64"
33159
- }), /*#__PURE__*/React__default["default"].createElement("feComposite", {
33160
- operator: "over",
33161
- in: "SourceGraphic"
33162
- })));
33163
-
33164
- const PolygonAngle = _ref => {
33882
+ })));
33883
+ }
33884
+ function PointGraphDescription(_ref) {
33165
33885
  let {
33166
- centerPoint,
33167
- endPoints,
33168
- range,
33169
- polygonLines,
33170
- showAngles,
33171
- snapTo
33886
+ state
33172
33887
  } = _ref;
33173
- const [centerX, centerY] = centerPoint;
33174
- const areClockwise = clockwise$1([centerPoint, ...endPoints]);
33175
- const [[startX, startY], [endX, endY]] = areClockwise ? endPoints : endPoints.reverse();
33176
- const radius = 0.3;
33177
- const a = mafs.vec.dist(centerPoint, endPoints[0]);
33178
- const b = mafs.vec.dist(centerPoint, endPoints[1]);
33179
- const c = mafs.vec.dist(endPoints[0], endPoints[1]);
33180
-
33181
- // Law of cosines
33182
- const angle = Math.acos((a ** 2 + b ** 2 - c ** 2) / (2 * a * b));
33183
- const y1 = centerY + (startY - centerY) / a * radius;
33184
- const x2 = centerX + (endX - centerX) / b * radius;
33185
- const x1 = centerX + (startX - centerX) / a * radius;
33186
- const y2 = centerY + (endY - centerY) / b * radius;
33187
-
33188
- // Fourth point that would create a rhomboid
33189
- // Used to calculate the inside of the angle and to create square
33190
- const [x3, y3] = mafs.vec.add(centerPoint, mafs.vec.add(mafs.vec.sub([x1, y1], centerPoint), mafs.vec.sub([x2, y2], centerPoint)));
33191
- if (!showAngles) {
33192
- return isRightPolygonAngle(angle) ? /*#__PURE__*/React__namespace.createElement(RightAngleSquare, {
33193
- start: [x1, y1],
33194
- vertex: [x2, y2],
33195
- end: [x3, y3]
33196
- }) : null;
33197
- }
33198
-
33199
- // Midpoint betwen ends of arc
33200
- const isOutside = shouldDrawArcOutside([x3, y3], centerPoint, range, polygonLines);
33201
- const largeArcFlag = isOutside ? 1 : 0;
33202
- const sweepFlag = isOutside ? 1 : 0;
33203
- const arc = `M ${x1} ${y1} A ${radius} ${radius} 0 ${largeArcFlag} ${sweepFlag} ${x2} ${y2}`;
33204
- let angleInDegrees = angle * (180 / Math.PI);
33205
- // If we have triggered "largArcFlag", the angle should be greater than 180
33206
- if (isOutside) {
33207
- angleInDegrees = 360 - angleInDegrees;
33208
- }
33209
-
33210
- // Only want to show whole numbers when in angles mode
33211
- const angleLabelNumber = parseFloat(angleInDegrees.toFixed(snapTo === "angles" ? 0 : 1));
33212
- const angleLabel = Number.isInteger(angleLabelNumber) ? angleLabelNumber : "≈ " + angleLabelNumber;
33213
- return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("filter", {
33214
- id: "background",
33215
- x: "-5%",
33216
- width: "110%",
33217
- y: "0%",
33218
- height: "100%"
33219
- }, /*#__PURE__*/React__namespace.createElement("feFlood", {
33220
- floodColor: "#FFF",
33221
- floodOpacity: "0.5"
33222
- }), /*#__PURE__*/React__namespace.createElement("feComposite", {
33223
- operator: "over",
33224
- in: "SourceGraphic"
33225
- }))), !isOutside && isRightAngle(angle) ? /*#__PURE__*/React__namespace.createElement(RightAngleSquare, {
33226
- start: [x1, y1],
33227
- vertex: [x2, y2],
33228
- end: [x3, y3]
33229
- }) : /*#__PURE__*/React__namespace.createElement(Arc, {
33230
- arc: arc
33231
- }), /*#__PURE__*/React__namespace.createElement(TextLabel, {
33232
- x: x3,
33233
- y: y3,
33234
- attach: y3 - centerY > 0 ? "s" : "n",
33235
- attachDistance: Math.abs(y3 - centerY) < 0.2 || mafs.vec.dist([x3, y3], centerPoint) < 0.3 ? 20 : 10
33236
- }, angleLabel, "\xB0"));
33237
- };
33238
- const Angle = _ref2 => {
33239
- let {
33240
- vertex,
33241
- coords,
33242
- showAngles,
33243
- allowReflexAngles,
33244
- range
33245
- } = _ref2;
33246
- // Check if the points are clockwise or not, depending on whether we allow reflex angles
33247
- const areClockwise = clockwise$1([...coords, vertex]);
33248
- const shouldReverseCoords = areClockwise && !allowReflexAngles;
33249
-
33250
- // Reverse the coordinates accordingly to ensure the angle is calculated correctly
33251
- const clockwiseCoords = shouldReverseCoords ? coords : coords.reverse();
33252
-
33253
- // Calculate the angles between the two points
33254
- const startAngle = getAngleFromVertex(clockwiseCoords[0], vertex);
33255
- const endAngle = getAngleFromVertex(clockwiseCoords[1], vertex);
33256
- const angle = (startAngle + 360 - endAngle) % 360;
33257
-
33258
- // Check if the angle is reflexive
33259
- const isReflexive = angle > 180;
33260
-
33261
- // Break out the necessary points for the arc calculation
33262
- const [centerX, centerY] = vertex;
33263
- const [point1, point2] = clockwiseCoords;
33264
- const [startX, startY] = point1;
33265
- const [endX, endY] = point2;
33266
- const a = mafs.vec.dist(vertex, point1);
33267
- const b = mafs.vec.dist(vertex, point2);
33268
-
33269
- // Set the radius of the arc
33270
- const radius = 2;
33271
-
33272
- // Calculate the end points of the arc
33273
- const y1 = centerY + (startY - centerY) / a * radius;
33274
- const x2 = centerX + (endX - centerX) / b * radius;
33275
- const x1 = centerX + (startX - centerX) / a * radius;
33276
- const y2 = centerY + (endY - centerY) / b * radius;
33277
-
33278
- // Used to calculate the inside of the angle and to create square
33279
- const [x3, y3] = mafs.vec.add(vertex, mafs.vec.add(mafs.vec.sub([x1, y1], vertex), mafs.vec.sub([x2, y2], vertex)));
33280
-
33281
- // Determine whether the arc should be drawn outside the angle
33282
- const isOutside = shouldDrawArcOutside([x3, y3], vertex, range, [[vertex, point1], [vertex, point2]]);
33283
-
33284
- // Determine the flags for the arc
33285
- // If the angle is outside OR reflexive, we want to draw a large arc
33286
- // If the angle is outside AND reflexive, we want to sweep the arc
33287
- const largeArcFlag = isOutside || isReflexive ? 1 : 0;
33288
- const sweepFlag = isOutside && isReflexive ? 1 : 0;
33289
-
33290
- // Create the SVG path for the arc
33291
- const arc = `M ${x1} ${y1} A ${radius} ${radius} 0 ${largeArcFlag} ${sweepFlag} ${x2} ${y2}`;
33292
-
33293
- // We only ever want to show whole angles
33294
- const angleLabel = parseFloat(angle.toFixed(0)); // Only want to show whole angles
33295
-
33296
- // Calculate the text position based on the angle and whether we allow reflex angles
33297
- // Let's try the angle bisector method to find the midpoint of the arc
33298
- const [textX, textY] = calculateBisectorPoint(point1, point2, vertex, isReflexive, allowReflexAngles, radius);
33299
- return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("filter", {
33300
- id: "background",
33301
- x: "-5%",
33302
- width: "110%",
33303
- y: "0%",
33304
- height: "100%"
33305
- }, /*#__PURE__*/React__namespace.createElement("feFlood", {
33306
- floodColor: "#FFF",
33307
- floodOpacity: "0.5"
33308
- }), /*#__PURE__*/React__namespace.createElement("feComposite", {
33309
- operator: "over",
33310
- in: "SourceGraphic"
33311
- }))), isRightAngle(angle) ? /*#__PURE__*/React__namespace.createElement(RightAngleSquare, {
33312
- start: [x1, y1],
33313
- vertex: [x2, y2],
33314
- end: [x3, y3],
33315
- className: "arc-right-angle"
33316
- }) : /*#__PURE__*/React__namespace.createElement(Arc, {
33317
- arc: arc,
33318
- className: "angle-arc"
33319
- }), showAngles && /*#__PURE__*/React__namespace.createElement(TextLabel, {
33320
- x: textX,
33321
- y: textY,
33322
- color: wonderBlocksTokens.color.blue
33323
- }, angleLabel, "\xB0"));
33324
- };
33325
-
33326
- /**
33327
- * This is broken out into its own component so that it can be used for an early return
33328
- * (see line 55 or https://github.com/Khan/perseus/blob/84bbd882b11d16871e1b813a0b901f3b903d5479/packages/perseus/src/widgets/interactive-graphs/graphs/components/angle.tsx#L53-L57)
33329
- */
33330
- const RightAngleSquare = _ref3 => {
33331
- let {
33332
- start: [x1, y1],
33333
- vertex: [x2, y2],
33334
- end: [x3, y3],
33335
- className
33336
- } = _ref3;
33337
- return /*#__PURE__*/React__namespace.createElement(MafsCssTransformWrapper, null, /*#__PURE__*/React__namespace.createElement("path", {
33338
- d: `M ${x1} ${y1} L ${x3} ${y3} M ${x3} ${y3} L ${x2} ${y2}`,
33339
- strokeWidth: 0.02,
33340
- fill: "none",
33341
- className: className
33342
- }));
33343
- };
33344
-
33345
- // We're conditionally adding the class name here so that we can style the arc differently
33346
- // based on whether it's an angle or a polygon angle
33347
- const Arc = _ref4 => {
33348
- let {
33349
- arc,
33350
- className
33351
- } = _ref4;
33352
- return /*#__PURE__*/React__namespace.createElement(MafsCssTransformWrapper, null, /*#__PURE__*/React__namespace.createElement("path", {
33353
- d: arc,
33354
- strokeWidth: 0.02,
33355
- fill: "none",
33356
- className: className
33357
- }));
33358
- };
33359
- const isRightPolygonAngle = angle => Math.abs(angle - Math.PI / 2) < 0.01;
33360
- const isRightAngle = angle => Math.round(angle) === 90;
33361
-
33362
- /**
33363
- * Determines if an angle is an inside (false) or outside (true) angle.
33364
- * They way, we know to flip the `largeArc` and `sweepArc` flags.
33365
- * Uses the priciple that a ray from a point inside a polygon will intersect
33366
- * with an odd number of lines, while a ray from a point outside the polygon
33367
- * will intersect with an even number of lines.
33368
- * https://stackoverflow.com/questions/217578/how-can-i-determine-whether-a-2d-point-is-within-a-polygon
33369
- */
33370
- const shouldDrawArcOutside = (midpoint, vertex, range, polygonLines) => {
33371
- // Create a ray from the midpoint (inside angle) to the edge of the range
33372
- const rangeIntersectionPoint = getIntersectionOfRayWithBox(midpoint, vertex, range);
33373
- let lineIntersectionCount = 0;
33374
- polygonLines.forEach(line => segmentsIntersect([vertex, rangeIntersectionPoint], line) && lineIntersectionCount++);
33375
-
33376
- // If the number of intersections is even, the angle is inside the polygon
33377
- return !isEven(lineIntersectionCount);
33378
- };
33379
- const isEven = n => n % 2 === 0;
33380
-
33381
- // This function calculates the bisector point of an angle formed by two points
33382
- // and a vertex. It is used to position the angle label so that it is always
33383
- // outside the arc at the exact midpoint of the angle.
33384
- // Reflex angles make use of polar coordinates to determine the correct direction
33385
- function calculateBisectorPoint(point1, point2, vertex, isReflex, allowReflex, arcRadius) {
33386
- const [originX, originY] = vertex;
33387
- const [x1, y1] = point1;
33388
- const [x2, y2] = point2;
33389
-
33390
- // Convert the Cartesian Coordinates to vectors
33391
- const vectorA = [x1 - originX, y1 - originY];
33392
- const vectorB = [x2 - originX, y2 - originY];
33393
-
33394
- // Calculate the average angle of the vectors
33395
- // so that we can point the bisector in the correct direction using polar coordinates
33396
- const angleA = Math.atan2(vectorA[1], vectorA[0]);
33397
- const angleB = Math.atan2(vectorB[1], vectorB[0]);
33398
-
33399
- // Calculate the average of these two angles
33400
- let averageAngle = (angleA + angleB) / 2;
33401
- const angleRadians = Math.abs(angleA - angleB);
33402
-
33403
- // Adjust the average angle to point in the correct direction
33404
- // and to account for reflex angles and a bug with 180 degrees
33405
- // that occurs due to our angleOffsetDegrees option in the graph
33406
- if (allowReflex) {
33407
- // If the resulting angles are less than 180 degrees,
33408
- // or if the user has flipped the points on the interactive element
33409
- // ensure that we have the correct direction for the reflex angle
33410
- if (angleRadians <= Math.PI && isReflex || angleB > angleA) {
33411
- averageAngle += Math.PI; // Add 180 degrees
33412
- }
33413
- } else {
33414
- // If the resulting angles are greater than 180 degrees, adjust
33415
- // to get the correct angle, as we already know that the angle is not reflexive
33416
- // This fixes a bug that occurs at 180 degrees due to our angleOffsetDegrees option in the graph
33417
- if (angleRadians > Math.PI) {
33418
- averageAngle -= Math.PI; // Subract 180 degrees
33419
- }
33420
- }
33421
-
33422
- // Convert the average angle back to Cartesian coordinates
33423
- const sum = [Math.cos(averageAngle), Math.sin(averageAngle)];
33424
-
33425
- // Calculate the magnitude of the sum to normalize it
33426
- const sumMagnitude = Math.sqrt(sum[0] ** 2 + sum[1] ** 2);
33427
- const bisectorDirection = [sum[0] / sumMagnitude, sum[1] / sumMagnitude];
33428
-
33429
- // Calculate the initial distance of the bisector direction from the origin
33430
- const initialDistance = Math.sqrt(bisectorDirection[0] ** 2 + bisectorDirection[1] ** 2);
33431
-
33432
- // Determine the minimum radius to ensure that the text is always outside the arc
33433
- const requiredDistance = arcRadius * 1.75;
33434
- let radius = requiredDistance / initialDistance;
33435
-
33436
- // Apply radius only if the initial distance is less than required
33437
- if (initialDistance >= requiredDistance) {
33438
- radius = 1; // Use the unit vector as is
33439
- }
33440
-
33441
- // Scale the bisector direction by the radius
33442
- const bisectorPoint = [bisectorDirection[0] * radius, bisectorDirection[1] * radius];
33443
-
33444
- // Add the vertex to the bisector point to get the final position
33445
- // to ensure that the angle label moves with the interactive element
33446
- const scaledBisector = mafs.vec.add(bisectorPoint, vertex);
33447
- return scaledBisector;
33888
+ // PointGraphDescription needs to `usePerseusI18n`, so it has to be a
33889
+ // component rather than a function that simply returns a string.
33890
+ const i18n = usePerseusI18n();
33891
+ return describePointGraph(state, i18n);
33448
33892
  }
33449
33893
 
33450
- const MovablePoint$1 = /*#__PURE__*/React__namespace.forwardRef((props, pointRef) => {
33894
+ // Exported for testing
33895
+ function describePointGraph(state, i18n) {
33451
33896
  const {
33452
- snapStep
33453
- } = useGraphConfig();
33454
- const elementRef = React.useRef(null);
33455
- const {
33456
- point,
33457
- onMove = () => {},
33458
- onFocusChange = () => {},
33459
- onClick = () => {},
33460
- cursor,
33461
- color = wonderBlocksTokens.color.blue,
33462
- constrain = p => snap(snapStep, p)
33463
- } = props;
33464
- const {
33465
- dragging
33466
- } = useDraggable({
33467
- gestureTarget: elementRef,
33468
- point,
33469
- onMove,
33470
- constrainKeyboardMovement: constrain
33897
+ strings,
33898
+ locale
33899
+ } = i18n;
33900
+ if (state.coords.length === 0) {
33901
+ return strings.srNoInteractiveElements;
33902
+ }
33903
+ const pointDescriptions = state.coords.map(_ref2 => {
33904
+ let [x, y] = _ref2;
33905
+ return strings.srPointAtCoordinates({
33906
+ x: srFormatNumber(x, locale),
33907
+ y: srFormatNumber(y, locale)
33908
+ });
33471
33909
  });
33472
- return /*#__PURE__*/React__namespace.createElement(MovablePointView, {
33473
- ref: ref => {
33474
- // Note: This handles the case where pointRef is either a
33475
- // function or a MutableRefObject. Mostly this is to satisfy
33476
- // the type system, the function branch seems to be the only
33477
- // one that occurs
33478
- if (typeof pointRef === "function") {
33479
- pointRef(ref);
33480
- } else if (pointRef !== null) {
33481
- pointRef.current = ref;
33482
- }
33483
-
33484
- // This ref is for the useDraggable and is not passed up the
33485
- // chain
33486
- elementRef.current = ref;
33487
- },
33488
- point: point,
33489
- color: color,
33490
- dragging: dragging,
33491
- focusBehavior: {
33492
- type: "uncontrolled",
33493
- tabIndex: 0,
33494
- onFocusChange
33495
- },
33496
- onClick: () => {
33497
- onClick && onClick();
33498
- elementRef.current?.focus();
33499
- },
33500
- cursor: cursor
33910
+ return strings.srInteractiveElements({
33911
+ elements: pointDescriptions.join(", ")
33501
33912
  });
33502
- });
33913
+ }
33503
33914
 
33504
- const PolygonGraph = props => {
33915
+ function renderPolygonGraph(state, dispatch) {
33916
+ return {
33917
+ graph: /*#__PURE__*/React__namespace.createElement(PolygonGraph, {
33918
+ graphState: state,
33919
+ dispatch: dispatch
33920
+ }),
33921
+ interactiveElementsDescription: null
33922
+ };
33923
+ }
33924
+ const LimitedPolygonGraph = props => {
33505
33925
  const [hovered, setHovered] = React__namespace.useState(false);
33506
33926
  // This is more so required for the re-rendering that occurs when state
33507
33927
  // updates; specifically with regard to line weighting and polygon focus.
@@ -33613,6 +34033,153 @@ const PolygonGraph = props => {
33613
34033
  }
33614
34034
  })));
33615
34035
  };
34036
+
34037
+ // TODO(catjohnson): reduce redundancy between LimitedPolygonGraph and UnlimitedPolygonGraph
34038
+ // both components are vary similar, however more implementation is needed to be added before
34039
+ // it is clear what can and can't be shared between components.
34040
+ const UnlimitedPolygonGraph = props => {
34041
+ const [hovered, setHovered] = React__namespace.useState(false);
34042
+ // This is more so required for the re-rendering that occurs when state
34043
+ // updates; specifically with regard to line weighting and polygon focus.
34044
+ const [focusVisible, setFocusVisible] = React__namespace.useState(false);
34045
+ const {
34046
+ dispatch
34047
+ } = props;
34048
+ const {
34049
+ coords,
34050
+ showAngles,
34051
+ showSides,
34052
+ range,
34053
+ snapStep,
34054
+ snapTo = "grid"
34055
+ } = props.graphState;
34056
+ const graphConfig = useGraphConfig();
34057
+
34058
+ // TODO(catjohnson): Explore abstracting this code as it is similar to point.tsx
34059
+ // and hopefully we can cut down ont the unlimited graph redundancy.
34060
+ const {
34061
+ range: [x, y],
34062
+ disableKeyboardInteraction,
34063
+ graphDimensionsInPixels
34064
+ } = graphConfig;
34065
+ const widthPx = graphDimensionsInPixels[0];
34066
+ const heightPx = graphDimensionsInPixels[1];
34067
+ const [[left, top]] = useTransformVectorsToPixels([x[0], y[1]]);
34068
+ const pointRef = React__namespace.useRef([]);
34069
+
34070
+ // TODO(benchristel): can the default set of points be removed here? I don't
34071
+ // think coords can be null.
34072
+ const points = coords ?? [[0, 0]];
34073
+ const polygonRef = React__namespace.useRef(null);
34074
+ const dragReferencePoint = points[0];
34075
+ const constrain = ["angles", "sides"].includes(snapTo) ? p => p : p => snap(snapStep, p);
34076
+ const {
34077
+ dragging
34078
+ } = useDraggable({
34079
+ gestureTarget: polygonRef,
34080
+ point: dragReferencePoint,
34081
+ onMove: newPoint => {
34082
+ const delta = mafs.vec.sub(newPoint, dragReferencePoint);
34083
+ dispatch(actions.polygon.moveAll(delta));
34084
+ },
34085
+ constrainKeyboardMovement: constrain
34086
+ });
34087
+ const lines = getLines(points);
34088
+ React__namespace.useEffect(() => {
34089
+ const focusedIndex = props.graphState.focusedPointIndex;
34090
+ if (focusedIndex != null) {
34091
+ pointRef.current[focusedIndex]?.focus();
34092
+ }
34093
+ }, [props.graphState.focusedPointIndex, pointRef]);
34094
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("rect", {
34095
+ style: {
34096
+ fill: "rgba(0,0,0,0)",
34097
+ cursor: "crosshair"
34098
+ },
34099
+ width: widthPx,
34100
+ height: heightPx,
34101
+ x: left,
34102
+ y: top,
34103
+ onClick: event => {
34104
+ const elementRect = event.currentTarget.getBoundingClientRect();
34105
+ const x = event.clientX - elementRect.x;
34106
+ const y = event.clientY - elementRect.y;
34107
+ const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
34108
+ dispatch(actions.polygon.addPoint(graphCoordinates[0]));
34109
+ }
34110
+ }), /*#__PURE__*/React__namespace.createElement(mafs.Polygon, {
34111
+ points: [...points],
34112
+ color: "var(--movable-line-stroke-color)",
34113
+ svgPolygonProps: {
34114
+ strokeWidth: focusVisible ? "var(--movable-line-stroke-weight-active)" : "var(--movable-line-stroke-weight)",
34115
+ style: {
34116
+ fill: "transparent"
34117
+ }
34118
+ }
34119
+ }), props.graphState.coords.map((point, i) => {
34120
+ const pt1 = points.at(i - 1);
34121
+ const pt2 = points[(i + 1) % points.length];
34122
+ if (!pt1 || !pt2) {
34123
+ return null;
34124
+ }
34125
+ return /*#__PURE__*/React__namespace.createElement(PolygonAngle, {
34126
+ key: "angle-" + i,
34127
+ centerPoint: point,
34128
+ endPoints: [pt1, pt2],
34129
+ range: range,
34130
+ polygonLines: lines,
34131
+ showAngles: !!showAngles,
34132
+ snapTo: snapTo
34133
+ });
34134
+ }), showSides && lines.map((_ref2, i) => {
34135
+ let [start, end] = _ref2;
34136
+ const [x, y] = mafs.vec.midpoint(start, end);
34137
+ const length = parseFloat(mafs.vec.dist(start, end).toFixed(snapTo === "sides" ? 0 : 1));
34138
+ return /*#__PURE__*/React__namespace.createElement(TextLabel, {
34139
+ key: "side-" + i,
34140
+ x: x,
34141
+ y: y
34142
+ }, !Number.isInteger(length) && "≈ ", length);
34143
+ }), /*#__PURE__*/React__namespace.createElement(mafs.Polygon, {
34144
+ points: [...points],
34145
+ color: "transparent",
34146
+ svgPolygonProps: {
34147
+ ref: polygonRef,
34148
+ tabIndex: disableKeyboardInteraction ? -1 : 0,
34149
+ strokeWidth: TARGET_SIZE,
34150
+ style: {
34151
+ cursor: dragging ? "grabbing" : "grab",
34152
+ fill: hovered ? "var(--mafs-blue)" : "transparent"
34153
+ },
34154
+ onMouseEnter: () => setHovered(true),
34155
+ onMouseLeave: () => setHovered(false),
34156
+ // Required to remove line weighting when user clicks away
34157
+ // from the focused polygon
34158
+ onKeyDownCapture: () => {
34159
+ setFocusVisible(hasFocusVisible(polygonRef.current));
34160
+ },
34161
+ // Required for lines to darken on focus
34162
+ onFocus: () => setFocusVisible(hasFocusVisible(polygonRef.current)),
34163
+ // Required for line weighting to update on blur. Without this,
34164
+ // the user has to hover over the shape for it to update
34165
+ onBlur: () => setFocusVisible(hasFocusVisible(polygonRef.current)),
34166
+ className: "movable-polygon"
34167
+ }
34168
+ }), props.graphState.coords.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
34169
+ key: i,
34170
+ point: point,
34171
+ onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
34172
+ ref: ref => {
34173
+ pointRef.current[i] = ref;
34174
+ },
34175
+ onFocus: () => {
34176
+ dispatch(actions.pointGraph.focusPoint(i));
34177
+ },
34178
+ onClick: () => {
34179
+ dispatch(actions.pointGraph.clickPoint(i));
34180
+ }
34181
+ })));
34182
+ };
33616
34183
  function getLines(points) {
33617
34184
  return points.map((point, i) => {
33618
34185
  const next = points[(i + 1) % points.length];
@@ -33630,202 +34197,20 @@ const hasFocusVisible = element => {
33630
34197
  return matches(":focus");
33631
34198
  }
33632
34199
  };
34200
+ const PolygonGraph = props => {
34201
+ const numSides = props.graphState.numSides;
34202
+ return numSides === "unlimited" ? UnlimitedPolygonGraph(props) : LimitedPolygonGraph(props);
34203
+ };
33633
34204
 
33634
- function getGradableGraph(state, initialGraph) {
33635
- if (!state.hasBeenInteractedWith) {
33636
- return {
33637
- ...initialGraph
33638
- };
33639
- }
33640
- if (initialGraph.type === "linear-system" && state.type === "linear-system") {
33641
- return {
33642
- ...initialGraph,
33643
- coords: state.coords
33644
- };
33645
- }
33646
- if (state.type === "segment" && initialGraph.type === "segment") {
33647
- return {
33648
- ...initialGraph,
33649
- coords: state.coords
33650
- };
33651
- }
33652
- if (state.type === "linear" && initialGraph.type === "linear") {
33653
- return {
33654
- ...initialGraph,
33655
- coords: state.coords
33656
- };
33657
- }
33658
- if (state.type === "ray" && initialGraph.type === "ray") {
33659
- return {
33660
- ...initialGraph,
33661
- coords: state.coords
33662
- };
33663
- }
33664
- if (state.type === "polygon" && initialGraph.type === "polygon") {
33665
- return {
33666
- ...initialGraph,
33667
- coords: state.coords
33668
- };
33669
- }
33670
- if (state.type === "point" && initialGraph.type === "point") {
33671
- return {
33672
- ...initialGraph,
33673
- coords: state.coords
33674
- };
33675
- }
33676
- if (state.type === "circle" && initialGraph.type === "circle") {
33677
- return {
33678
- ...initialGraph,
33679
- center: state.center,
33680
- radius: getRadius(state)
33681
- };
33682
- }
33683
- if (state.type === "quadratic" && initialGraph.type === "quadratic") {
33684
- return {
33685
- ...initialGraph,
33686
- coords: state.coords
33687
- };
33688
- }
33689
- if (state.type === "sinusoid" && initialGraph.type === "sinusoid") {
33690
- return {
33691
- ...initialGraph,
33692
- coords: state.coords
33693
- };
33694
- }
33695
- if (state.type === "angle" && initialGraph.type === "angle") {
33696
- // We're going to reverse the coords for scoring if the angle is clockwise and
33697
- // we don't allow reflex angles. This is because the angle is largely defined
33698
- // by the order of the points in the coords array, and we want to maintain
33699
- // the same angle scoring with the legacy graph (which had a bug).
33700
- // (LEMS-2190): When we remove the legacy graph, move this logic to the scoring function.
33701
- const areClockwise = clockwise$1([state.coords[0], state.coords[2], state.coords[1]]);
33702
- const shouldReverseCoords = areClockwise && !state.allowReflexAngles;
33703
- const coords = shouldReverseCoords ? state.coords.slice().reverse() : state.coords;
33704
- return {
33705
- ...initialGraph,
33706
- coords,
33707
- allowReflexAngles: state.allowReflexAngles
33708
- };
33709
- }
33710
- if (state.type === "none" && initialGraph.type === "none") {
33711
- return {
33712
- type: "none"
33713
- };
33714
- }
33715
- throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
33716
- }
33717
-
33718
- /**
33719
- * determine radius of a circle graph
33720
- *
33721
- * @param graph - the graph object containing the circle
33722
- * @returns the radius of the circle
33723
- */
33724
- function getRadius(graph) {
33725
- const [centerX, centerY] = graph.center;
33726
- const [edgeX, edgeY] = graph.radiusPoint;
33727
- return Math.sqrt(Math.pow(edgeX - centerX, 2) + Math.pow(edgeY - centerY, 2));
33728
- }
33729
-
33730
- function CircleGraph(props) {
33731
- const {
33732
- dispatch,
33733
- graphState
33734
- } = props;
33735
- const {
33736
- center,
33737
- radiusPoint
33738
- } = graphState;
33739
- return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(MovableCircle, {
33740
- center: center,
33741
- radius: getRadius(graphState),
33742
- onMove: c => dispatch(actions.circle.moveCenter(c))
33743
- }), /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
33744
- point: radiusPoint,
33745
- cursor: "ew-resize",
33746
- onMove: newRadiusPoint => {
33747
- dispatch(actions.circle.moveRadiusPoint(newRadiusPoint));
33748
- }
33749
- }));
33750
- }
33751
- function MovableCircle(props) {
33752
- const {
33753
- center,
33754
- radius,
33755
- onMove
33756
- } = props;
33757
- const {
33758
- snapStep,
33759
- disableKeyboardInteraction
33760
- } = useGraphConfig();
33761
- const draggableRef = React.useRef(null);
33762
- const {
33763
- dragging
33764
- } = useDraggable({
33765
- gestureTarget: draggableRef,
33766
- point: center,
33767
- onMove,
33768
- constrainKeyboardMovement: p => snap(snapStep, p)
33769
- });
33770
- const [centerPx] = useTransformVectorsToPixels(center);
33771
- const [radiiPx] = useTransformDimensionsToPixels([radius, radius]);
33772
- return /*#__PURE__*/React__namespace.createElement("g", {
33773
- ref: draggableRef,
33774
- tabIndex: disableKeyboardInteraction ? -1 : 0,
33775
- className: `movable-circle ${dragging ? "movable-circle--dragging" : ""}`
33776
- }, /*#__PURE__*/React__namespace.createElement("ellipse", {
33777
- className: "focus-ring",
33778
- cx: centerPx[X],
33779
- cy: centerPx[Y],
33780
- rx: radiiPx[X] + 3,
33781
- ry: radiiPx[Y] + 3
33782
- }), /*#__PURE__*/React__namespace.createElement("ellipse", {
33783
- className: "circle",
33784
- cx: centerPx[X],
33785
- cy: centerPx[Y],
33786
- rx: radiiPx[X],
33787
- ry: radiiPx[Y]
33788
- }), /*#__PURE__*/React__namespace.createElement(DragHandle, {
33789
- center: center
33790
- }));
33791
- }
33792
- const dragHandleDimensions = [24, 14];
33793
- const dragHandleDotPositions = crossProduct([-4.4, 0, 4.4], [-2.1, 2.1]);
33794
- function DragHandle(props) {
33795
- const {
33796
- center
33797
- } = props;
33798
- const cornerRadius = Math.min(...dragHandleDimensions) / 2;
33799
- const [centerPx] = useTransformVectorsToPixels(center);
33800
- const topLeft = mafs.vec.sub(centerPx, mafs.vec.scale(dragHandleDimensions, 0.5));
33801
- return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("rect", {
33802
- className: "movable-circle-handle",
33803
- x: topLeft[X],
33804
- y: topLeft[Y],
33805
- width: dragHandleDimensions[X],
33806
- height: dragHandleDimensions[Y],
33807
- rx: cornerRadius,
33808
- ry: cornerRadius
33809
- }), dragHandleDotPositions.map(offsetPx => {
33810
- const [xPx, yPx] = mafs.vec.add(offsetPx, centerPx);
33811
- return /*#__PURE__*/React__namespace.createElement("circle", {
33812
- key: `circle-${xPx}-${yPx}`,
33813
- className: "movable-circle-handle-dot",
33814
- cx: xPx,
33815
- cy: yPx
33816
- });
33817
- }));
33818
- }
33819
- function crossProduct(as, bs) {
33820
- const result = [];
33821
- for (const a of as) {
33822
- for (const b of bs) {
33823
- result.push([a, b]);
33824
- }
33825
- }
33826
- return result;
34205
+ function renderQuadraticGraph(state, dispatch) {
34206
+ return {
34207
+ graph: /*#__PURE__*/React__namespace.createElement(QuadraticGraph, {
34208
+ graphState: state,
34209
+ dispatch: dispatch
34210
+ }),
34211
+ interactiveElementsDescription: null
34212
+ };
33827
34213
  }
33828
-
33829
34214
  function QuadraticGraph(props) {
33830
34215
  const {
33831
34216
  dispatch,
@@ -33880,6 +34265,74 @@ const getQuadraticCoefficients$1 = coords => {
33880
34265
  return [a, b, c];
33881
34266
  };
33882
34267
 
34268
+ function renderRayGraph(state, dispatch) {
34269
+ return {
34270
+ graph: /*#__PURE__*/React__namespace.createElement(RayGraph, {
34271
+ graphState: state,
34272
+ dispatch: dispatch
34273
+ }),
34274
+ interactiveElementsDescription: null
34275
+ };
34276
+ }
34277
+ const RayGraph = props => {
34278
+ const {
34279
+ dispatch
34280
+ } = props;
34281
+ const {
34282
+ coords: line
34283
+ } = props.graphState;
34284
+ const handleMoveLine = delta => dispatch(actions.ray.moveRay(delta));
34285
+ const handleMovePoint = (pointIndex, newPoint) => dispatch(actions.ray.movePoint(pointIndex, newPoint));
34286
+
34287
+ // Ray graphs only have one line
34288
+ return /*#__PURE__*/React__namespace.createElement(MovableLine, {
34289
+ points: line,
34290
+ onMoveLine: handleMoveLine,
34291
+ onMovePoint: handleMovePoint,
34292
+ extend: {
34293
+ start: false,
34294
+ end: true
34295
+ }
34296
+ });
34297
+ };
34298
+
34299
+ function renderSegmentGraph(state, dispatch) {
34300
+ return {
34301
+ graph: /*#__PURE__*/React__namespace.createElement(SegmentGraph, {
34302
+ graphState: state,
34303
+ dispatch: dispatch
34304
+ }),
34305
+ interactiveElementsDescription: null
34306
+ };
34307
+ }
34308
+ const SegmentGraph = props => {
34309
+ const {
34310
+ dispatch
34311
+ } = props;
34312
+ const {
34313
+ coords: segments
34314
+ } = props.graphState;
34315
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, segments?.map((segment, i) => /*#__PURE__*/React__namespace.createElement(MovableLine, {
34316
+ key: i,
34317
+ points: segment,
34318
+ onMoveLine: delta => {
34319
+ dispatch(actions.segment.moveLine(i, delta));
34320
+ },
34321
+ onMovePoint: (endpointIndex, destination) => {
34322
+ dispatch(actions.segment.movePointInFigure(i, endpointIndex, destination));
34323
+ }
34324
+ })));
34325
+ };
34326
+
34327
+ function renderSinusoidGraph(state, dispatch) {
34328
+ return {
34329
+ graph: /*#__PURE__*/React__namespace.createElement(SinusoidGraph, {
34330
+ graphState: state,
34331
+ dispatch: dispatch
34332
+ }),
34333
+ interactiveElementsDescription: null
34334
+ };
34335
+ }
33883
34336
  function SinusoidGraph(props) {
33884
34337
  const {
33885
34338
  dispatch,
@@ -33955,201 +34408,6 @@ const getSinusoidCoefficients$1 = coords => {
33955
34408
  };
33956
34409
  };
33957
34410
 
33958
- function AngleGraph(props) {
33959
- const {
33960
- dispatch,
33961
- graphState
33962
- } = props;
33963
- const {
33964
- graphDimensionsInPixels
33965
- } = useGraphConfig();
33966
- const {
33967
- coords,
33968
- showAngles,
33969
- range,
33970
- allowReflexAngles,
33971
- snapDegrees
33972
- } = graphState;
33973
-
33974
- // Break the coords into the two end points and the center point
33975
- const endPoints = [coords[0], coords[2]];
33976
- const centerPoint = coords[1];
33977
-
33978
- // Convert the vectors to pixels for rendering the svg lines
33979
- const angleLines = [[centerPoint, endPoints[0]], [centerPoint, endPoints[1]]];
33980
- const linePixelCoords = [useTransformVectorsToPixels(centerPoint, endPoints[0]), useTransformVectorsToPixels(centerPoint, endPoints[1])];
33981
-
33982
- // Create the SVG lines for the angle
33983
- const svgLines = linePixelCoords.map((_ref, i) => {
33984
- let [startPtPx, endPtPx] = _ref;
33985
- const trimmedRange = trimRange(range, graphDimensionsInPixels);
33986
- const endExtend = getIntersectionOfRayWithBox(angleLines[i][0], angleLines[i][1], trimmedRange);
33987
- return /*#__PURE__*/React__namespace.createElement("g", {
33988
- key: `line-${i}`
33989
- }, /*#__PURE__*/React__namespace.createElement(SVGLine, {
33990
- start: startPtPx,
33991
- end: endPtPx,
33992
- style: {
33993
- stroke: "var(--movable-line-stroke-color)",
33994
- strokeWidth: "var(--movable-line-stroke-weight)"
33995
- }
33996
- }), /*#__PURE__*/React__namespace.createElement(Vector, {
33997
- tail: angleLines[i][1],
33998
- tip: endExtend,
33999
- color: "var(--movable-line-stroke-color)"
34000
- }));
34001
- });
34002
-
34003
- // Create the angle indicator parameters
34004
- const angleParams = {
34005
- vertex: centerPoint,
34006
- coords: endPoints,
34007
- allowReflexAngles: allowReflexAngles || false,
34008
- // Whether to allow reflex angles or not
34009
- snapDegrees: snapDegrees || 1,
34010
- // The multiple of degrees to snap to
34011
- range: range,
34012
- showAngles: showAngles || false // Whether to show the angle or not
34013
- };
34014
-
34015
- // Render the lines, angle, and then movable points
34016
- return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, svgLines, /*#__PURE__*/React__namespace.createElement(Angle, angleParams), /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
34017
- point: coords[1],
34018
- constrain: p => p,
34019
- onMove: destination => dispatch(actions.angle.movePoint(1, destination))
34020
- }), /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
34021
- point: coords[0],
34022
- constrain: getAngleSideConstraint(coords[0], coords[1], snapDegrees || 1),
34023
- onMove: destination => dispatch(actions.angle.movePoint(0, destination))
34024
- }), /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
34025
- point: coords[2],
34026
- constrain: getAngleSideConstraint(coords[2], coords[1], snapDegrees || 1),
34027
- onMove: destination => dispatch(actions.angle.movePoint(2, destination))
34028
- }));
34029
- }
34030
- const positiveX = [1, 0];
34031
- const negativeX = [-1, 0];
34032
- const positiveY = [0, 1];
34033
- const negativeY = [0, -1];
34034
- function getAngleSideConstraint(sidePoint, vertex, snapDegrees) {
34035
- const currentAngle = calculateAngleInDegrees(mafs.vec.sub(sidePoint, vertex));
34036
-
34037
- // Find the rays that start at the current point and point up, down, left
34038
- // and right.
34039
- const leftRay = [sidePoint, mafs.vec.add(sidePoint, negativeX)];
34040
- const rightRay = [sidePoint, mafs.vec.add(sidePoint, positiveX)];
34041
- const upRay = [sidePoint, mafs.vec.add(sidePoint, positiveY)];
34042
- const downRay = [sidePoint, mafs.vec.add(sidePoint, negativeY)];
34043
-
34044
- // find the angles that lie one snap step clockwise and counter-clockwise
34045
- // from the current angle. These are the angles to which the side can be
34046
- // moved.
34047
- const oneStepCounterClockwise = currentAngle + snapDegrees;
34048
- const oneStepClockwise = currentAngle - snapDegrees;
34049
-
34050
- // find the rays that start from the vertex and point in the direction of
34051
- // the angles we just computed.
34052
- const counterClockwiseRay = [vertex, mafs.vec.add(vertex, polar(1, oneStepCounterClockwise))];
34053
- const clockwiseRay = [vertex, mafs.vec.add(vertex, polar(1, oneStepClockwise))];
34054
-
34055
- // find the intersections of those rays with the horizontal and vertical
34056
- // rays extending from the sidePoint. These intersections are the points to
34057
- // which sidePoint can move that will result in a rotation of `snapDegrees`.
34058
- const left = findIntersectionOfRays(leftRay, counterClockwiseRay) ?? findIntersectionOfRays(leftRay, clockwiseRay);
34059
- const right = findIntersectionOfRays(rightRay, counterClockwiseRay) ?? findIntersectionOfRays(rightRay, clockwiseRay);
34060
- const up = findIntersectionOfRays(upRay, counterClockwiseRay) ?? findIntersectionOfRays(upRay, clockwiseRay);
34061
- const down = findIntersectionOfRays(downRay, counterClockwiseRay) ?? findIntersectionOfRays(downRay, clockwiseRay);
34062
- return {
34063
- up: up ?? sidePoint,
34064
- down: down ?? sidePoint,
34065
- left: left ?? sidePoint,
34066
- right: right ?? sidePoint
34067
- };
34068
- }
34069
-
34070
- function LimitedPointGraph(props) {
34071
- const {
34072
- dispatch
34073
- } = props;
34074
- return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, props.graphState.coords.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
34075
- key: i,
34076
- point: point,
34077
- onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination))
34078
- })));
34079
- }
34080
- function UnlimitedPointGraph(props) {
34081
- const {
34082
- dispatch
34083
- } = props;
34084
- const graphState = useGraphConfig();
34085
- const {
34086
- range: [[minX, maxX], [minY, maxY]]
34087
- } = graphState;
34088
- const width = maxX - minX;
34089
- const height = maxY - minY;
34090
- const [[widthPx, heightPx]] = useTransformDimensionsToPixels([width, height]);
34091
- const [[left, top]] = useTransformVectorsToPixels([minX, maxY]);
34092
- const itemsRef = React__namespace.useRef([]);
34093
- React__namespace.useEffect(() => {
34094
- const focusedIndex = props.graphState.focusedPointIndex;
34095
- if (focusedIndex != null) {
34096
- itemsRef.current[focusedIndex]?.focus();
34097
- }
34098
- }, [props.graphState.focusedPointIndex, itemsRef]);
34099
- return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("rect", {
34100
- style: {
34101
- fill: "rgba(0,0,0,0)",
34102
- cursor: "crosshair"
34103
- },
34104
- width: widthPx,
34105
- height: heightPx,
34106
- x: left,
34107
- y: top,
34108
- onClick: event => {
34109
- const elementRect = event.currentTarget.getBoundingClientRect();
34110
- const x = event.clientX - elementRect.x;
34111
- const y = event.clientY - elementRect.y;
34112
- const graphCoordinates = pixelsToVectors([[x, y]], graphState);
34113
- dispatch(actions.pointGraph.addPoint(graphCoordinates[0]));
34114
- }
34115
- }), props.graphState.coords.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
34116
- key: i,
34117
- point: point,
34118
- onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
34119
- ref: ref => {
34120
- itemsRef.current[i] = ref;
34121
- },
34122
- onFocusChange: (event, isFocused) => {
34123
- if (isFocused) {
34124
- dispatch(actions.pointGraph.focusPoint(i));
34125
- } else {
34126
- if (event.relatedTarget?.id === REMOVE_BUTTON_ID) {
34127
- return;
34128
- // This is an optimization: If the next target
34129
- // is a point then don't blur because it casues
34130
- // the remove button to get taken off the page
34131
- // and then put back on The new point will
34132
- // receive focus and set the correct state in
34133
- // the reducer
34134
- } else if (event.relatedTarget?.classList.contains("movable-point")) {
34135
- return;
34136
- }
34137
- dispatch(actions.pointGraph.blurPoint());
34138
- }
34139
- },
34140
- onClick: () => {
34141
- dispatch(actions.pointGraph.clickPoint(i));
34142
- }
34143
- })));
34144
- }
34145
- function PointGraph(props) {
34146
- const numPoints = props.graphState.numPoints;
34147
- if (numPoints === "unlimited") {
34148
- return UnlimitedPointGraph(props);
34149
- }
34150
- return LimitedPointGraph(props);
34151
- }
34152
-
34153
34411
  const protractorImage = "https://ka-perseus-graphie.s3.amazonaws.com/e9d032f2ab8b95979f674fbfa67056442ba1ff6a.png";
34154
34412
 
34155
34413
  // The vector from the center of the protractor to the top left corner of the
@@ -34297,7 +34555,6 @@ function useDraggablePx(args) {
34297
34555
  * - Protractor
34298
34556
  * - Interactive Graph Elements
34299
34557
  */
34300
- const REMOVE_BUTTON_ID = "perseus_mafs_remove_button";
34301
34558
  const MafsGraph = props => {
34302
34559
  const {
34303
34560
  state,
@@ -34314,6 +34571,7 @@ const MafsGraph = props => {
34314
34571
  const tickStep = props.step;
34315
34572
  const uniqueId = React__namespace.useId();
34316
34573
  const descriptionId = `interactive-graph-description-${uniqueId}`;
34574
+ const interactiveElementsDescriptionId = `interactive-graph-interactive-elements-description-${uniqueId}`;
34317
34575
  const graphRef = React__namespace.useRef(null);
34318
34576
  const {
34319
34577
  analytics
@@ -34337,6 +34595,7 @@ const MafsGraph = props => {
34337
34595
  const {
34338
34596
  strings
34339
34597
  } = usePerseusI18n();
34598
+ const interactionPrompt = isUnlimitedGraphState(state) && state.showKeyboardInteractionInvitation;
34340
34599
  wonderBlocksCore.useOnMountEffect(() => {
34341
34600
  analytics.onAnalyticsEvent({
34342
34601
  type: "perseus:interactive-graph-widget:rendered",
@@ -34347,6 +34606,13 @@ const MafsGraph = props => {
34347
34606
  }
34348
34607
  });
34349
34608
  });
34609
+ const {
34610
+ graph,
34611
+ interactiveElementsDescription
34612
+ } = renderGraphElements({
34613
+ state,
34614
+ dispatch
34615
+ });
34350
34616
  return /*#__PURE__*/React__namespace.createElement(GraphConfigContext.Provider, {
34351
34617
  value: {
34352
34618
  range: state.range,
@@ -34378,7 +34644,7 @@ const MafsGraph = props => {
34378
34644
  handleKeyboardEvent(event, state, dispatch);
34379
34645
  },
34380
34646
  "aria-label": fullGraphAriaLabel,
34381
- "aria-describedby": fullGraphAriaDescription ? descriptionId : undefined,
34647
+ "aria-describedby": describedByIds(fullGraphAriaDescription && descriptionId, interactiveElementsDescription && interactiveElementsDescriptionId),
34382
34648
  ref: graphRef,
34383
34649
  tabIndex: 0,
34384
34650
  onFocus: event => {
@@ -34390,12 +34656,12 @@ const MafsGraph = props => {
34390
34656
  }, fullGraphAriaDescription && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
34391
34657
  id: descriptionId,
34392
34658
  tabIndex: -1,
34393
- style: {
34394
- width: 0,
34395
- height: 0,
34396
- overflow: "hidden"
34397
- }
34398
- }, fullGraphAriaDescription), /*#__PURE__*/React__namespace.createElement(LegacyGrid, {
34659
+ className: "mafs-sr-only"
34660
+ }, fullGraphAriaDescription), interactiveElementsDescription && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
34661
+ id: interactiveElementsDescriptionId,
34662
+ tabIndex: -1,
34663
+ className: "mafs-sr-only"
34664
+ }, interactiveElementsDescription), /*#__PURE__*/React__namespace.createElement(LegacyGrid, {
34399
34665
  box: props.box,
34400
34666
  backgroundImage: props.backgroundImage
34401
34667
  }), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
@@ -34446,10 +34712,7 @@ const MafsGraph = props => {
34446
34712
  zoom: false,
34447
34713
  width: width,
34448
34714
  height: height
34449
- }, /*#__PURE__*/React__namespace.createElement("svg", nestedSVGAttributes, props.showProtractor && /*#__PURE__*/React__namespace.createElement(Protractor, null), renderGraph({
34450
- state,
34451
- dispatch
34452
- }))))), state.type === "point" && state.showKeyboardInteractionInvitation && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
34715
+ }, /*#__PURE__*/React__namespace.createElement("svg", nestedSVGAttributes, props.showProtractor && /*#__PURE__*/React__namespace.createElement(Protractor, null), graph)))), interactionPrompt && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
34453
34716
  style: {
34454
34717
  textAlign: "center",
34455
34718
  backgroundColor: "white",
@@ -34466,7 +34729,7 @@ const MafsGraph = props => {
34466
34729
  state,
34467
34730
  dispatch,
34468
34731
  width,
34469
- strings
34732
+ perseusStrings: strings
34470
34733
  })));
34471
34734
  };
34472
34735
  const renderPointGraphControls = props => {
@@ -34476,7 +34739,7 @@ const renderPointGraphControls = props => {
34476
34739
  focusedPointIndex
34477
34740
  } = props.state;
34478
34741
  const {
34479
- strings
34742
+ perseusStrings
34480
34743
  } = props;
34481
34744
  const shouldShowRemoveButton = showRemovePointButton && focusedPointIndex !== null;
34482
34745
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
@@ -34494,7 +34757,7 @@ const renderPointGraphControls = props => {
34494
34757
  onClick: () => {
34495
34758
  props.dispatch(actions.pointGraph.addPoint([0, 0]));
34496
34759
  }
34497
- }, strings.addPoint), interactionMode === "mouse" && /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
34760
+ }, perseusStrings.addPoint), interactionMode === "mouse" && /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
34498
34761
  id: REMOVE_BUTTON_ID,
34499
34762
  kind: "secondary",
34500
34763
  color: "destructive"
@@ -34507,17 +34770,66 @@ const renderPointGraphControls = props => {
34507
34770
  marginLeft: "20px",
34508
34771
  visibility: shouldShowRemoveButton ? "visible" : "hidden"
34509
34772
  },
34510
- onClick: event => {
34773
+ onClick: _event => {
34511
34774
  props.dispatch(actions.pointGraph.removePoint(props.state.focusedPointIndex));
34512
34775
  }
34513
- }, strings.removePoint));
34776
+ }, perseusStrings.removePoint));
34777
+ };
34778
+
34779
+ /**
34780
+ * TODO(catjohnson): The polygon controls will be vastly different from the point controls.
34781
+ * Will be keeping this function as is for the time being, even if it's a copy of the point graph
34782
+ * controls.
34783
+ */
34784
+ const renderPolygonGraphControls = props => {
34785
+ const {
34786
+ interactionMode,
34787
+ showRemovePointButton,
34788
+ focusedPointIndex
34789
+ } = props.state;
34790
+ const {
34791
+ perseusStrings
34792
+ } = props;
34793
+ const shouldShowRemoveButton = showRemovePointButton && focusedPointIndex !== null;
34794
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
34795
+ style: {
34796
+ flexDirection: "row",
34797
+ width: props.width
34798
+ }
34799
+ }, interactionMode === "keyboard" && /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
34800
+ kind: "secondary",
34801
+ style: {
34802
+ width: "100%",
34803
+ marginLeft: "20px"
34804
+ },
34805
+ tabIndex: 0,
34806
+ onClick: () => {
34807
+ props.dispatch(actions.polygon.addPoint([0, 0]));
34808
+ }
34809
+ }, perseusStrings.addPoint), interactionMode === "mouse" && /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
34810
+ id: REMOVE_BUTTON_ID,
34811
+ kind: "secondary",
34812
+ color: "destructive"
34813
+ // This button is meant to be interacted with by the mouse only
34814
+ // Never allow learners to tab to this button
34815
+ ,
34816
+ tabIndex: -1,
34817
+ style: {
34818
+ width: "100%",
34819
+ marginLeft: "20px",
34820
+ visibility: shouldShowRemoveButton ? "visible" : "hidden"
34821
+ },
34822
+ onClick: _event => {
34823
+ props.dispatch(actions.polygon.removePoint(props.state.focusedPointIndex));
34824
+ }
34825
+ }, perseusStrings.removePoint));
34514
34826
  };
34515
34827
  const renderGraphControls = props => {
34516
34828
  const {
34517
34829
  state,
34518
34830
  dispatch,
34519
34831
  width,
34520
- strings
34832
+ perseusStrings
34521
34833
  } = props;
34522
34834
  const {
34523
34835
  type
@@ -34529,7 +34841,17 @@ const renderGraphControls = props => {
34529
34841
  state,
34530
34842
  dispatch,
34531
34843
  width,
34532
- strings
34844
+ perseusStrings
34845
+ });
34846
+ }
34847
+ return null;
34848
+ case "polygon":
34849
+ if (state.numSides === "unlimited") {
34850
+ return renderPolygonGraphControls({
34851
+ state,
34852
+ dispatch,
34853
+ width,
34854
+ perseusStrings
34533
34855
  });
34534
34856
  }
34535
34857
  return null;
@@ -34538,21 +34860,28 @@ const renderGraphControls = props => {
34538
34860
  }
34539
34861
  };
34540
34862
  function handleFocusEvent(event, state, dispatch) {
34541
- if (state.type === "point" && state.numPoints === "unlimited") {
34863
+ if (isUnlimitedGraphState(state)) {
34542
34864
  if (event.target.classList.contains("mafs-graph") && state.interactionMode === "mouse") {
34543
34865
  dispatch(actions.global.changeKeyboardInvitationVisibility(true));
34544
34866
  }
34545
34867
  }
34546
34868
  }
34547
- function handleBlurEvent(event, state, dispatch) {
34548
- if (state.type === "point" && state.numPoints === "unlimited") {
34869
+ function handleBlurEvent(_event, state, dispatch) {
34870
+ if (isUnlimitedGraphState(state)) {
34549
34871
  dispatch(actions.global.changeKeyboardInvitationVisibility(false));
34550
34872
  }
34551
34873
  }
34552
34874
  function handleKeyboardEvent(event, state, dispatch) {
34553
- if (state.type === "point" && state.numPoints === "unlimited") {
34875
+ if (isUnlimitedGraphState(state)) {
34554
34876
  if (event.key === "Backspace") {
34555
- dispatch(actions.global.deleteIntent());
34877
+ // TODO(benchristel): Checking classList here is a hack to prevent
34878
+ // points from being deleted if the user presses the backspace key
34879
+ // while the whole graph is focused. Instead of doing this, we
34880
+ // should move the keyboard event handler to the movable point
34881
+ // handle element.
34882
+ if (document.activeElement?.classList.contains("movable-point__focusable-handle")) {
34883
+ dispatch(actions.global.deleteIntent());
34884
+ }
34556
34885
 
34557
34886
  // After removing a point blur
34558
34887
  // It would be nice if this could focus on the graph but doing so
@@ -34615,7 +34944,7 @@ const calculateNestedSVGCoords = (range, width, height) => {
34615
34944
  viewboxY
34616
34945
  };
34617
34946
  };
34618
- const renderGraph = props => {
34947
+ const renderGraphElements = props => {
34619
34948
  const {
34620
34949
  state,
34621
34950
  dispatch
@@ -34625,62 +34954,44 @@ const renderGraph = props => {
34625
34954
  } = state;
34626
34955
  switch (type) {
34627
34956
  case "angle":
34628
- return /*#__PURE__*/React__namespace.createElement(AngleGraph, {
34629
- graphState: state,
34630
- dispatch: dispatch
34631
- });
34957
+ return renderAngleGraph(state, dispatch);
34632
34958
  case "segment":
34633
- return /*#__PURE__*/React__namespace.createElement(SegmentGraph, {
34634
- graphState: state,
34635
- dispatch: dispatch
34636
- });
34959
+ return renderSegmentGraph(state, dispatch);
34637
34960
  case "linear-system":
34638
- return /*#__PURE__*/React__namespace.createElement(LinearSystemGraph, {
34639
- graphState: state,
34640
- dispatch: dispatch
34641
- });
34961
+ return renderLinearSystemGraph(state, dispatch);
34642
34962
  case "linear":
34643
- return /*#__PURE__*/React__namespace.createElement(LinearGraph, {
34644
- graphState: state,
34645
- dispatch: dispatch
34646
- });
34963
+ return renderLinearGraph(state, dispatch);
34647
34964
  case "ray":
34648
- return /*#__PURE__*/React__namespace.createElement(RayGraph, {
34649
- graphState: state,
34650
- dispatch: dispatch
34651
- });
34965
+ return renderRayGraph(state, dispatch);
34652
34966
  case "polygon":
34653
- return /*#__PURE__*/React__namespace.createElement(PolygonGraph, {
34654
- graphState: state,
34655
- dispatch: dispatch
34656
- });
34967
+ return renderPolygonGraph(state, dispatch);
34657
34968
  case "point":
34658
- return /*#__PURE__*/React__namespace.createElement(PointGraph, {
34659
- graphState: state,
34660
- dispatch: dispatch
34661
- });
34969
+ return renderPointGraph(state, dispatch);
34662
34970
  case "circle":
34663
- return /*#__PURE__*/React__namespace.createElement(CircleGraph, {
34664
- graphState: state,
34665
- dispatch: dispatch
34666
- });
34971
+ return renderCircleGraph(state, dispatch);
34667
34972
  case "quadratic":
34668
- return /*#__PURE__*/React__namespace.createElement(QuadraticGraph, {
34669
- graphState: state,
34670
- dispatch: dispatch
34671
- });
34973
+ return renderQuadraticGraph(state, dispatch);
34672
34974
  case "sinusoid":
34673
- return /*#__PURE__*/React__namespace.createElement(SinusoidGraph, {
34674
- graphState: state,
34675
- dispatch: dispatch
34676
- });
34975
+ return renderSinusoidGraph(state, dispatch);
34677
34976
  case "none":
34678
- return null;
34977
+ return {
34978
+ graph: null,
34979
+ interactiveElementsDescription: null
34980
+ };
34679
34981
  default:
34680
34982
  throw new wonderStuffCore.UnreachableCaseError(type);
34681
34983
  }
34682
34984
  };
34683
34985
 
34986
+ // Returns a space-separated string like "foo bar" given several optional
34987
+ // string IDs. If all args are falsy, returns undefined.
34988
+ function describedByIds() {
34989
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
34990
+ args[_key] = arguments[_key];
34991
+ }
34992
+ return args.filter(Boolean).join(" ") || undefined;
34993
+ }
34994
+
34684
34995
  // Converts the state of a StatefulMafsGraph back to the format used to
34685
34996
  // represent graph state in the widget JSON.
34686
34997
  //
@@ -34811,10 +35122,15 @@ function initializeGraphState(params) {
34811
35122
  return {
34812
35123
  ...shared,
34813
35124
  type: "polygon",
35125
+ numSides: graph.numSides || 0,
34814
35126
  showAngles: Boolean(graph.showAngles),
34815
35127
  showSides: Boolean(graph.showSides),
34816
35128
  coords: getPolygonCoords(graph, range, step),
34817
- snapTo: graph.snapTo ?? "grid"
35129
+ snapTo: graph.snapTo ?? "grid",
35130
+ focusedPointIndex: null,
35131
+ showRemovePointButton: false,
35132
+ interactionMode: "mouse",
35133
+ showKeyboardInteractionInvitation: false
34818
35134
  };
34819
35135
  case "point":
34820
35136
  return {
@@ -35108,10 +35424,9 @@ function interactiveGraphReducer(state, action) {
35108
35424
  }
35109
35425
  function doDeleteIntent(state, action) {
35110
35426
  // For unlimited point graphs
35111
- if (state.type === "point" && state.numPoints === "unlimited") {
35112
- // if there's a focused point
35427
+ if (isUnlimitedGraphState(state)) {
35428
+ // Remove the last point that was focused, if any
35113
35429
  if (state.focusedPointIndex !== null) {
35114
- // Remove the focused focus
35115
35430
  return doRemovePoint(state, actions.pointGraph.removePoint(state.focusedPointIndex));
35116
35431
  }
35117
35432
  }
@@ -35119,6 +35434,7 @@ function doDeleteIntent(state, action) {
35119
35434
  }
35120
35435
  function doFocusPoint(state, action) {
35121
35436
  switch (state.type) {
35437
+ case "polygon":
35122
35438
  case "point":
35123
35439
  return {
35124
35440
  ...state,
@@ -35130,6 +35446,7 @@ function doFocusPoint(state, action) {
35130
35446
  }
35131
35447
  function doBlurPoint(state, action) {
35132
35448
  switch (state.type) {
35449
+ case "polygon":
35133
35450
  case "point":
35134
35451
  const nextState = {
35135
35452
  ...state,
@@ -35144,10 +35461,7 @@ function doBlurPoint(state, action) {
35144
35461
  }
35145
35462
  }
35146
35463
  function doClickPoint(state, action) {
35147
- if (state.type !== "point") {
35148
- return state;
35149
- }
35150
- if (state.numPoints === "unlimited") {
35464
+ if (isUnlimitedGraphState(state)) {
35151
35465
  return {
35152
35466
  ...state,
35153
35467
  focusedPointIndex: action.index,
@@ -35157,10 +35471,7 @@ function doClickPoint(state, action) {
35157
35471
  return state;
35158
35472
  }
35159
35473
  function doChangeInteractionMode(state, action) {
35160
- if (state.type !== "point") {
35161
- return state;
35162
- }
35163
- if (state.numPoints === "unlimited") {
35474
+ if (isUnlimitedGraphState(state)) {
35164
35475
  const nextKeyboardInvitation = action.mode === "keyboard" ? false : state.showKeyboardInteractionInvitation;
35165
35476
  return {
35166
35477
  ...state,
@@ -35171,10 +35482,7 @@ function doChangeInteractionMode(state, action) {
35171
35482
  return state;
35172
35483
  }
35173
35484
  function doChangeKeyboardInvitationVisibility(state, action) {
35174
- if (state.type !== "point") {
35175
- return state;
35176
- }
35177
- if (state.numPoints === "unlimited") {
35485
+ if (isUnlimitedGraphState(state)) {
35178
35486
  return {
35179
35487
  ...state,
35180
35488
  showKeyboardInteractionInvitation: action.shouldShow,
@@ -35521,7 +35829,7 @@ function doChangeRange(state, action) {
35521
35829
  };
35522
35830
  }
35523
35831
  function doAddPoint(state, action) {
35524
- if (state.type !== "point") {
35832
+ if (!isUnlimitedGraphState(state)) {
35525
35833
  return state;
35526
35834
  }
35527
35835
  const {
@@ -35546,7 +35854,7 @@ function doAddPoint(state, action) {
35546
35854
  };
35547
35855
  }
35548
35856
  function doRemovePoint(state, action) {
35549
- if (state.type !== "point") {
35857
+ if (!isUnlimitedGraphState(state)) {
35550
35858
  return state;
35551
35859
  }
35552
35860
  let nextFocusedPointIndex;
@@ -37835,9 +38143,6 @@ function shouldUseMafs(mafsFlags, graph) {
37835
38143
  return Boolean(mafsFlags["point"]);
37836
38144
  case "polygon":
37837
38145
  if (graph.numSides === UNLIMITED) {
37838
- // TODO(benchristel): add a feature flag for the "unlimited"
37839
- // case once we've implemented polygon graphs with unlimited
37840
- // sides
37841
38146
  return Boolean(mafsFlags["unlimited-polygon"]);
37842
38147
  }
37843
38148
  return Boolean(mafsFlags["polygon"]);
@@ -37855,976 +38160,15 @@ var InteractiveGraph$1 = {
37855
38160
  validator: interactiveGraphValidator
37856
38161
  };
37857
38162
 
37858
- const black = "#000000";
37859
- const grayDarker = "#333333";
37860
- const grayDark = "#555555";
37861
- const gray = "#999999";
37862
- const grayLight = "#AAAAAA";
37863
- const grayLighter = "#DDDDDD";
37864
- const white = "#FFFFFF";
37865
- const blue = "#005A88";
37866
- const blueDark = "#2C3747";
37867
- const green = "#76A005";
37868
- const red = "#CF5044";
37869
- const yellow = "#FFC40D";
37870
- const orange = "#BF4F04";
37871
- const pink = "#C3325F";
37872
- const purple = "#7A43B6";
37873
- const bodyBackground = "#FFFFFF";
37874
- const textColor = "#444444";
37875
- const linkColor = "#005987";
37876
- const linkColorHover = "#678D00";
37877
- const sansFontFamily = "'Helvetica Neue',Helvetica,Arial,sans-serif";
37878
- const serifFontFamily = "Georgia,'Times New Roman',Times,serif";
37879
- const monoFontFamily = "Monaco,Menlo,Consolas,'Courier New',monospace";
37880
- const baseFontSize = "14px";
37881
- const baseFontFamily = "inherit";
37882
- const baseLineHeight = "20px";
37883
- const altFontFamily = "Georgia,'Times New Roman',Times,serif";
37884
- const headingsFontFamily = "inherit";
37885
- const headingsFontWeight = "bold";
37886
- const headingsColor = "inherit";
37887
- const fontSizeLarge = "18px";
37888
- const fontSizeSmall = "12px";
37889
- const fontSizeMini = "10.5px";
37890
- const paddingLarge = "11px 19px";
37891
- const paddingSmall = "2px 10px";
37892
- const paddingMini = "0 6px";
37893
- const baseBorderRadius = "4px";
37894
- const borderRadiusLarge = "6px";
37895
- const borderRadiusSmall = "3px";
37896
- const tableBackground = "#000000";
37897
- const tableBackgroundAccent = "#F9F9F9";
37898
- const tableBackgroundHover = "#F5F5F5";
37899
- const tableBorder = "#DDDDDD";
37900
- const btnBackground = "#FFFFFF";
37901
- const btnBackgroundHighlight = "rgb(230, 230, 230)";
37902
- const btnBorder = "#CCCCCC";
37903
- const btnPrimaryBackground = "#0088CC";
37904
- const btnPrimaryBackgroundHighlight = "rgb(0, 68, 204)";
37905
- const btnInfoBackground = "#5BC0DE";
37906
- const btnInfoBackgroundHighlight = "#2F96B4";
37907
- const btnSuccessBackground = "#62C462";
37908
- const btnSuccessBackgroundHighlight = "#51A351";
37909
- const btnWarningBackground = "rgb(251, 180, 81)";
37910
- const btnWarningBackgroundHighlight = "#F89406";
37911
- const btnDangerBackground = "#EE5F5B";
37912
- const btnDangerBackgroundHighlight = "#BD362F";
37913
- const btnInverseBackground = "#444444";
37914
- const btnInverseBackgroundHighlight = "#222222";
37915
- const inputBackground = "#FFFFFF";
37916
- const inputBorder = "#CCCCCC";
37917
- const inputBorderRadius = "4px";
37918
- const inputDisabledBackground = "#EEEEEE";
37919
- const formActionsBackground = "#F5F5F5";
37920
- const inputHeight = "30px";
37921
- const dropdownBackground = "#FFFFFF";
37922
- const dropdownBorder = "rgba(0, 0, 0, 0.2)";
37923
- const dropdownDividerTop = "#E5E5E5";
37924
- const dropdownDividerBottom = "#FFFFFF";
37925
- const dropdownLinkColor = "#333333";
37926
- const dropdownLinkColorHover = "#FFFFFF";
37927
- const dropdownLinkColorActive = "#FFFFFF";
37928
- const dropdownLinkBackgroundActive = "#0088CC";
37929
- const dropdownLinkBackgroundHover = "#0088CC";
37930
- const zindexDropdown = 1000;
37931
- const zindexPopover = 1010;
37932
- const zindexTooltip = 1030;
37933
- const zindexFixedNavbar = 1030;
37934
- const zindexHeader = 1060;
37935
- const zindexModalBackdrop = 1070;
37936
- const zindexModal = 1080;
37937
- const zindexAboveModal = 1090;
37938
- const iconSpritePath = "../img/glyphicons-halflings.png";
37939
- const iconWhiteSpritePath = "../img/glyphicons-halflings-white.png";
37940
- const placeholderText = "#999999";
37941
- const hrBorder = "#EEEEEE";
37942
- const horizontalComponentOffset = "180px";
37943
- const wellBackground = "#F5F5F5";
37944
- const navbarCollapseWidth = "979px";
37945
- const navbarCollapseDesktopWidth = "980px";
37946
- const navbarHeight = "40px";
37947
- const navbarBackgroundHighlight = "#FFFFFF";
37948
- const navbarBackground = "rgb(242, 242, 242)";
37949
- const navbarBorder = "rgb(212, 212, 212)";
37950
- const navbarText = "#777777";
37951
- const navbarLinkColor = "#777777";
37952
- const navbarLinkColorHover = "#333333";
37953
- const navbarLinkColorActive = "#555555";
37954
- const navbarLinkBackgroundHover = "#000000";
37955
- const navbarLinkBackgroundActive = "rgb(230, 230, 230)";
37956
- const navbarBrandColor = "#777777";
37957
- const navbarInverseBackground = "#111111";
37958
- const navbarInverseBackgroundHighlight = "#222222";
37959
- const navbarInverseBorder = "#252525";
37960
- const navbarInverseText = "#999999";
37961
- const navbarInverseLinkColor = "#999999";
37962
- const navbarInverseLinkColorHover = "#FFFFFF";
37963
- const navbarInverseLinkColorActive = "#FFFFFF";
37964
- const navbarInverseLinkBackgroundHover = "#000000";
37965
- const navbarInverseLinkBackgroundActive = "#111111";
37966
- const navbarInverseSearchBackground = "rgb(82, 82, 82)";
37967
- const navbarInverseSearchBackgroundFocus = "#FFFFFF";
37968
- const navbarInverseSearchBorder = "#111111";
37969
- const navbarInverseSearchPlaceholderColor = "#CCCCCC";
37970
- const navbarInverseBrandColor = "#999999";
37971
- const paginationBackground = "#FFFFFF";
37972
- const paginationBorder = "#DDDDDD";
37973
- const paginationActiveBackground = "#F5F5F5";
37974
- const heroUnitBackground = "#EEEEEE";
37975
- const heroUnitHeadingColor = "inherit";
37976
- const heroUnitLeadColor = "inherit";
37977
- const warningText = "#C09853";
37978
- const warningBackground = "#FCF8E3";
37979
- const warningBorder = "rgb(251, 239, 213)";
37980
- const errorText = "#B94A48";
37981
- const errorBackground = "#F2DEDE";
37982
- const errorBorder = "rgb(238, 211, 215)";
37983
- const successText = "#468847";
37984
- const successBackground = "#DFF0D8";
37985
- const successBorder = "rgb(212, 232, 197)";
37986
- const infoText = "#4A7C17";
37987
- const infoBackground = "#EDF2DF";
37988
- const infoBorder = "rgb(228, 232, 197)";
37989
- const tooltipColor = "#FFFFFF";
37990
- const tooltipBackground = "#000000";
37991
- const tooltipArrowWidth = "5px";
37992
- const tooltipArrowColor = "#000000";
37993
- const popoverBackground = "#FFFFFF";
37994
- const popoverArrowWidth = "10px";
37995
- const popoverArrowColor = "#FFFFFF";
37996
- const popoverTitleBackground = "rgb(247, 247, 247)";
37997
- const popoverArrowOuterWidth = "11px";
37998
- const popoverArrowOuterColor = "rgba(0, 0, 0, 0.25)";
37999
- const gridColumns = "12";
38000
- const gridColumnWidth = "60px";
38001
- const gridGutterWidth = "20px";
38002
- const gridRowWidth = "940px";
38003
- const gridColumnWidth1200 = "70px";
38004
- const gridGutterWidth1200 = "30px";
38005
- const gridRowWidth1200 = "1170px";
38006
- const gridColumnWidth768 = "42px";
38007
- const gridGutterWidth768 = "20px";
38008
- const gridRowWidth768 = "724px";
38009
- const fluidGridColumnWidth = "6.382978723404255%";
38010
- const fluidGridGutterWidth = "2.127659574468085%";
38011
- const fluidGridColumnWidth1200 = "5.982905982905983%";
38012
- const fluidGridGutterWidth1200 = "2.564102564102564%";
38013
- const fluidGridColumnWidth768 = "5.801104972375691%";
38014
- const fluidGridGutterWidth768 = "2.7624309392265194%";
38015
- const kaGreen = "#71B307";
38016
- const kaGreenLight = "#BED47A";
38017
- const kaBlue = "#314453";
38018
- const kaBlueLight = "#4D6779";
38019
- const grayExtraDark = "#111111";
38020
- const grayExtraLight = "#EEEEEE";
38021
- const graySuperLight = "#F7F7F7";
38022
- const whiteDark = "#FDFDFD";
38023
- const dark = "#21242C";
38024
- const blueDarkUnsaturated = "#3B414E";
38025
- const blueDarkSaturated = "#1F3043";
38026
- const blueLight = "#A9C0D1";
38027
- const greenLight = "#C6D1AD";
38028
- const greenDark = "#356700";
38029
- const yellowGreen = "#9DB63B";
38030
- const okGreen = "#A7CF5B";
38031
- const alertRed = "#C42420";
38032
- const coral = "#EE6666";
38033
- const grayBlue = "#9AB3B9";
38034
- const lightPageBackground = "#FDFDFD";
38035
- const yclaBlue = "#49BAD5";
38036
- const yclaTeal = "#56D0B3";
38037
- const yclaTealDark = "#50C1A7";
38038
- const yclaGreenDark = "#3D9A82";
38039
- const yclaGreen = "#81C262";
38040
- const learnstormBlue = "#4898FC";
38041
- const homepageBlue = "#46A8BF";
38042
- const backgroundBlue = "#00294A";
38043
- const teacherSignupBlue = "#335d7d";
38044
- const homepageGreen = "#2B8E7B";
38045
- const streakColor = "#F79734";
38046
- const facebookColor = "#1A60A2";
38047
- const blueGoogleColor = "#2CAFE2";
38048
- const redGoogleColor = "#BF4434";
38049
- const homepageColor = "#2D585E";
38050
- const lightTextColor = "#898989";
38051
- const fontSizeTiny = "11px";
38052
- const fontSizeNormal = "14px";
38053
- const fontSizeMedium = "16px";
38054
- const fontSizeExtraLarge = "24px";
38055
- const fontSizeHuge = "36px";
38056
- const fontSizeBakana = "48px";
38057
- const fontSizeBananas = "60px";
38058
- const fixedWidthFontFamily = "'Helvetica Neue',Helvetica,Arial,sans-serif";
38059
- const contentPadding = "20px";
38060
- const primaryButtonBackground = "#89B908";
38061
- const primaryButtonBorder = "#76A005";
38062
- const buttonTextColor = "rgb(122, 122, 122)";
38063
- const buttonBorderRadius = "3px";
38064
- const basicBorderColor = "#CCCCCC";
38065
- const minContainerWidth = "1000px";
38066
- const maxContainerWidth = "1200px";
38067
- const formHintColor = "#999999";
38068
- const guidelineExampleText = "#606060";
38069
- const hoverCardWidth = "450px";
38070
- const menuHighlightColor = "#96AB4F";
38071
- const menuHighlightBorderColor = "#76A005";
38072
- const menuActiveTextColor = "#FFFFFF";
38073
- const menuInactiveTextColor = "#444444";
38074
- const menuSubheaderTextColor = "rgb(120, 120, 120)";
38075
- const menuBorderColor = "#94A170";
38076
- const unreadBackgroundColor = "#ECEEF4";
38077
- const menuLineHeight = "18px";
38078
- const menuSubheaderLineHeight = "17px";
38079
- const menuSubheaderFontSize = "9px";
38080
- const notificationBarColor = "#4858A4";
38081
- const modalBodyTopPadding = "20px";
38082
- const modalDialogPadding = "15px";
38083
- const modalSectionPadding = "9px";
38084
- const mathDomain1 = "#11ACCD";
38085
- const mathDomain2 = "#63D9EA";
38086
- const mathDomain3 = "#027d97";
38087
- const mathDomain4 = "#085566";
38088
- const partnerContentDomain1 = "#01A995";
38089
- const partnerContentDomain2 = "#01D1C1";
38090
- const partnerContentDomain3 = "#208170";
38091
- const partnerContentDomain4 = "#144F44";
38092
- const csDomain1 = "#1FAB54";
38093
- const csDomain2 = "#74CF70";
38094
- const csDomain3 = "#0D923F";
38095
- const csDomain4 = "#085E29";
38096
- const economicsDomain1 = "#E07D10";
38097
- const economicsDomain2 = "#FFBE26";
38098
- const economicsDomain3 = "#A75A05";
38099
- const economicsDomain4 = "#953C02";
38100
- const collegeCareersMoreDomain1 = "#01A995";
38101
- const collegeCareersMoreDomain2 = "#01D1C1";
38102
- const collegeCareersMoreDomain3 = "#208170";
38103
- const collegeCareersMoreDomain4 = "#144F44";
38104
- const humanitiesDomain1 = "#E84D39";
38105
- const humanitiesDomain2 = "#FF8482";
38106
- const humanitiesDomain3 = "#BE2612";
38107
- const humanitiesDomain4 = "#8C1C0D";
38108
- const gtpDomain1 = "#1865F2";
38109
- const gtpDomain2 = "#1865F2";
38110
- const gtpDomain3 = "#1865F2";
38111
- const gtpDomain4 = "#1865F2";
38112
- const scienceDomain1 = "#CA337C";
38113
- const scienceDomain2 = "#FF92C6";
38114
- const scienceDomain3 = "#9E034E";
38115
- const scienceDomain4 = "#6B0235";
38116
- const testPrepDomain1 = "#1865F2";
38117
- const testPrepDomain2 = "#1865F2";
38118
- const testPrepDomain3 = "#1865F2";
38119
- const testPrepDomain4 = "#1865F2";
38120
- const defaultDomain1 = "#1865F2";
38121
- const defaultDomain2 = "#66afe9";
38122
- const defaultDomain3 = "#1152b0";
38123
- const defaultDomain4 = "#0d3d82";
38124
- const defaultDomainColor = "#314453";
38125
- const defaultSubjectColor = "#4D6779";
38126
- const defaultTopicColor = "#6A8DA6";
38127
- const defaultHoverColor = "#314453";
38128
- const defaultLinkColor = "#314453";
38129
- const defaultLinkHoverColor = "#6A8DA6";
38130
- const scienceDomainColor = "#94424F";
38131
- const scienceSubjectColor = "#9D4A5A";
38132
- const scienceTopicColor = "#C55F73";
38133
- const scienceHoverColor = "#94424F";
38134
- const scienceLinkColor = "#94424F";
38135
- const scienceLinkHoverColor = "#C55F73";
38136
- const humanitiesDomainColor = "#AD3434";
38137
- const humanitiesSubjectColor = "#C13B31";
38138
- const humanitiesTopicColor = "#D24A45";
38139
- const humanitiesHoverColor = "#AD3434";
38140
- const humanitiesLinkColor = "#AD3434";
38141
- const humanitiesLinkHoverColor = "#D24A45";
38142
- const economicsDomainColor = "#B77033";
38143
- const economicsSubjectColor = "#BF7B34";
38144
- const economicsTopicColor = "#D1933B";
38145
- const economicsHoverColor = "#B77033";
38146
- const economicsLinkColor = "#B77033";
38147
- const economicsLinkHoverColor = "#D1933B";
38148
- const csDomainColor = "#437A39";
38149
- const csSubjectColor = "#53893E";
38150
- const csTopicColor = "#689B51";
38151
- const csHoverColor = "#437A39";
38152
- const csLinkColor = "#437A39";
38153
- const csLinkHoverColor = "#689B51";
38154
- const partnerContentDomainColor = "#218270";
38155
- const partnerContentSubjectColor = "#2C8D7B";
38156
- const partnerContentTopicColor = "#329A86";
38157
- const partnerContentHoverColor = "#218270";
38158
- const partnerContentLinkColor = "#218270";
38159
- const partnerContentLinkHoverColor = "#329A86";
38160
- const essayDomainColor = "#0071B3";
38161
- const mathDomainColor = "#1C758A";
38162
- const mathSubjectColor = "#46A8BF";
38163
- const mathTopicColor = "#4FBAD4";
38164
- const mathHoverColor = "#1C758A";
38165
- const mathLinkColor = "#1C758A";
38166
- const mathLinkHoverColor = "#4FBAD4";
38167
- const testPrepDomainColor = "#644172";
38168
- const testPrepSubjectColor = "#7E5F8E";
38169
- const testPrepTopicColor = "#9A72AC";
38170
- const testPrepHoverColor = "#644172";
38171
- const testPrepLinkColor = "#644172";
38172
- const testPrepLinkHoverColor = "#9A72AC";
38173
- const collegeCareersMoreDomainColor = "#218270";
38174
- const collegeCareersMoreSubjectColor = "#2C8D7B";
38175
- const collegeCareersMoreTopicColor = "#329A86";
38176
- const collegeCareersMoreHoverColor = "#218270";
38177
- const collegeCareersMoreLinkColor = "#218270";
38178
- const collegeCareersMoreLinkHoverColor = "#329A86";
38179
- const colorProficient = "#1C758A";
38180
- const colorPracticed = "rgb(158, 221, 235)";
38181
- const colorMastery1 = "rgb(90, 197, 221)";
38182
- const colorMastery2 = "rgb(42, 174, 203)";
38183
- const colorMastery3 = "#1C758A";
38184
- const colorSuggested = "#73982C";
38185
- const colorReview = "#E35D04";
38186
- const colorNotStarted = "#CCCCCC";
38187
- const colorStarted = "#C7E5F7";
38188
- const colorStruggling = "#C30202";
38189
- const colorPersonal = "#00809C";
38190
- const colorPractice = "#96D9FF";
38191
- const colorEnergyPoints = "#005987";
38192
- const pureSmMin = "568px";
38193
- const pureMdMin = "768px";
38194
- const pureLgMin = "1024px";
38195
- const pureXlMin = "1200px";
38196
- const pureXsMax = "567px";
38197
- const pureSmMax = "767px";
38198
- const pureMdMax = "1023px";
38199
- const pureLgMax = "1199px";
38200
-
38201
- var globalConstants = /*#__PURE__*/Object.freeze({
38202
- __proto__: null,
38203
- black: black,
38204
- grayDarker: grayDarker,
38205
- grayDark: grayDark,
38206
- gray: gray,
38207
- grayLight: grayLight,
38208
- grayLighter: grayLighter,
38209
- white: white,
38210
- blue: blue,
38211
- blueDark: blueDark,
38212
- green: green,
38213
- red: red,
38214
- yellow: yellow,
38215
- orange: orange,
38216
- pink: pink,
38217
- purple: purple,
38218
- bodyBackground: bodyBackground,
38219
- textColor: textColor,
38220
- linkColor: linkColor,
38221
- linkColorHover: linkColorHover,
38222
- sansFontFamily: sansFontFamily,
38223
- serifFontFamily: serifFontFamily,
38224
- monoFontFamily: monoFontFamily,
38225
- baseFontSize: baseFontSize,
38226
- baseFontFamily: baseFontFamily,
38227
- baseLineHeight: baseLineHeight,
38228
- altFontFamily: altFontFamily,
38229
- headingsFontFamily: headingsFontFamily,
38230
- headingsFontWeight: headingsFontWeight,
38231
- headingsColor: headingsColor,
38232
- fontSizeLarge: fontSizeLarge,
38233
- fontSizeSmall: fontSizeSmall,
38234
- fontSizeMini: fontSizeMini,
38235
- paddingLarge: paddingLarge,
38236
- paddingSmall: paddingSmall,
38237
- paddingMini: paddingMini,
38238
- baseBorderRadius: baseBorderRadius,
38239
- borderRadiusLarge: borderRadiusLarge,
38240
- borderRadiusSmall: borderRadiusSmall,
38241
- tableBackground: tableBackground,
38242
- tableBackgroundAccent: tableBackgroundAccent,
38243
- tableBackgroundHover: tableBackgroundHover,
38244
- tableBorder: tableBorder,
38245
- btnBackground: btnBackground,
38246
- btnBackgroundHighlight: btnBackgroundHighlight,
38247
- btnBorder: btnBorder,
38248
- btnPrimaryBackground: btnPrimaryBackground,
38249
- btnPrimaryBackgroundHighlight: btnPrimaryBackgroundHighlight,
38250
- btnInfoBackground: btnInfoBackground,
38251
- btnInfoBackgroundHighlight: btnInfoBackgroundHighlight,
38252
- btnSuccessBackground: btnSuccessBackground,
38253
- btnSuccessBackgroundHighlight: btnSuccessBackgroundHighlight,
38254
- btnWarningBackground: btnWarningBackground,
38255
- btnWarningBackgroundHighlight: btnWarningBackgroundHighlight,
38256
- btnDangerBackground: btnDangerBackground,
38257
- btnDangerBackgroundHighlight: btnDangerBackgroundHighlight,
38258
- btnInverseBackground: btnInverseBackground,
38259
- btnInverseBackgroundHighlight: btnInverseBackgroundHighlight,
38260
- inputBackground: inputBackground,
38261
- inputBorder: inputBorder,
38262
- inputBorderRadius: inputBorderRadius,
38263
- inputDisabledBackground: inputDisabledBackground,
38264
- formActionsBackground: formActionsBackground,
38265
- inputHeight: inputHeight,
38266
- dropdownBackground: dropdownBackground,
38267
- dropdownBorder: dropdownBorder,
38268
- dropdownDividerTop: dropdownDividerTop,
38269
- dropdownDividerBottom: dropdownDividerBottom,
38270
- dropdownLinkColor: dropdownLinkColor,
38271
- dropdownLinkColorHover: dropdownLinkColorHover,
38272
- dropdownLinkColorActive: dropdownLinkColorActive,
38273
- dropdownLinkBackgroundActive: dropdownLinkBackgroundActive,
38274
- dropdownLinkBackgroundHover: dropdownLinkBackgroundHover,
38275
- zindexDropdown: zindexDropdown,
38276
- zindexPopover: zindexPopover,
38277
- zindexTooltip: zindexTooltip,
38278
- zindexFixedNavbar: zindexFixedNavbar,
38279
- zindexHeader: zindexHeader,
38280
- zindexModalBackdrop: zindexModalBackdrop,
38281
- zindexModal: zindexModal,
38282
- zindexAboveModal: zindexAboveModal,
38283
- iconSpritePath: iconSpritePath,
38284
- iconWhiteSpritePath: iconWhiteSpritePath,
38285
- placeholderText: placeholderText,
38286
- hrBorder: hrBorder,
38287
- horizontalComponentOffset: horizontalComponentOffset,
38288
- wellBackground: wellBackground,
38289
- navbarCollapseWidth: navbarCollapseWidth,
38290
- navbarCollapseDesktopWidth: navbarCollapseDesktopWidth,
38291
- navbarHeight: navbarHeight,
38292
- navbarBackgroundHighlight: navbarBackgroundHighlight,
38293
- navbarBackground: navbarBackground,
38294
- navbarBorder: navbarBorder,
38295
- navbarText: navbarText,
38296
- navbarLinkColor: navbarLinkColor,
38297
- navbarLinkColorHover: navbarLinkColorHover,
38298
- navbarLinkColorActive: navbarLinkColorActive,
38299
- navbarLinkBackgroundHover: navbarLinkBackgroundHover,
38300
- navbarLinkBackgroundActive: navbarLinkBackgroundActive,
38301
- navbarBrandColor: navbarBrandColor,
38302
- navbarInverseBackground: navbarInverseBackground,
38303
- navbarInverseBackgroundHighlight: navbarInverseBackgroundHighlight,
38304
- navbarInverseBorder: navbarInverseBorder,
38305
- navbarInverseText: navbarInverseText,
38306
- navbarInverseLinkColor: navbarInverseLinkColor,
38307
- navbarInverseLinkColorHover: navbarInverseLinkColorHover,
38308
- navbarInverseLinkColorActive: navbarInverseLinkColorActive,
38309
- navbarInverseLinkBackgroundHover: navbarInverseLinkBackgroundHover,
38310
- navbarInverseLinkBackgroundActive: navbarInverseLinkBackgroundActive,
38311
- navbarInverseSearchBackground: navbarInverseSearchBackground,
38312
- navbarInverseSearchBackgroundFocus: navbarInverseSearchBackgroundFocus,
38313
- navbarInverseSearchBorder: navbarInverseSearchBorder,
38314
- navbarInverseSearchPlaceholderColor: navbarInverseSearchPlaceholderColor,
38315
- navbarInverseBrandColor: navbarInverseBrandColor,
38316
- paginationBackground: paginationBackground,
38317
- paginationBorder: paginationBorder,
38318
- paginationActiveBackground: paginationActiveBackground,
38319
- heroUnitBackground: heroUnitBackground,
38320
- heroUnitHeadingColor: heroUnitHeadingColor,
38321
- heroUnitLeadColor: heroUnitLeadColor,
38322
- warningText: warningText,
38323
- warningBackground: warningBackground,
38324
- warningBorder: warningBorder,
38325
- errorText: errorText,
38326
- errorBackground: errorBackground,
38327
- errorBorder: errorBorder,
38328
- successText: successText,
38329
- successBackground: successBackground,
38330
- successBorder: successBorder,
38331
- infoText: infoText,
38332
- infoBackground: infoBackground,
38333
- infoBorder: infoBorder,
38334
- tooltipColor: tooltipColor,
38335
- tooltipBackground: tooltipBackground,
38336
- tooltipArrowWidth: tooltipArrowWidth,
38337
- tooltipArrowColor: tooltipArrowColor,
38338
- popoverBackground: popoverBackground,
38339
- popoverArrowWidth: popoverArrowWidth,
38340
- popoverArrowColor: popoverArrowColor,
38341
- popoverTitleBackground: popoverTitleBackground,
38342
- popoverArrowOuterWidth: popoverArrowOuterWidth,
38343
- popoverArrowOuterColor: popoverArrowOuterColor,
38344
- gridColumns: gridColumns,
38345
- gridColumnWidth: gridColumnWidth,
38346
- gridGutterWidth: gridGutterWidth,
38347
- gridRowWidth: gridRowWidth,
38348
- gridColumnWidth1200: gridColumnWidth1200,
38349
- gridGutterWidth1200: gridGutterWidth1200,
38350
- gridRowWidth1200: gridRowWidth1200,
38351
- gridColumnWidth768: gridColumnWidth768,
38352
- gridGutterWidth768: gridGutterWidth768,
38353
- gridRowWidth768: gridRowWidth768,
38354
- fluidGridColumnWidth: fluidGridColumnWidth,
38355
- fluidGridGutterWidth: fluidGridGutterWidth,
38356
- fluidGridColumnWidth1200: fluidGridColumnWidth1200,
38357
- fluidGridGutterWidth1200: fluidGridGutterWidth1200,
38358
- fluidGridColumnWidth768: fluidGridColumnWidth768,
38359
- fluidGridGutterWidth768: fluidGridGutterWidth768,
38360
- kaGreen: kaGreen,
38361
- kaGreenLight: kaGreenLight,
38362
- kaBlue: kaBlue,
38363
- kaBlueLight: kaBlueLight,
38364
- grayExtraDark: grayExtraDark,
38365
- grayExtraLight: grayExtraLight,
38366
- graySuperLight: graySuperLight,
38367
- whiteDark: whiteDark,
38368
- dark: dark,
38369
- blueDarkUnsaturated: blueDarkUnsaturated,
38370
- blueDarkSaturated: blueDarkSaturated,
38371
- blueLight: blueLight,
38372
- greenLight: greenLight,
38373
- greenDark: greenDark,
38374
- yellowGreen: yellowGreen,
38375
- okGreen: okGreen,
38376
- alertRed: alertRed,
38377
- coral: coral,
38378
- grayBlue: grayBlue,
38379
- lightPageBackground: lightPageBackground,
38380
- yclaBlue: yclaBlue,
38381
- yclaTeal: yclaTeal,
38382
- yclaTealDark: yclaTealDark,
38383
- yclaGreenDark: yclaGreenDark,
38384
- yclaGreen: yclaGreen,
38385
- learnstormBlue: learnstormBlue,
38386
- homepageBlue: homepageBlue,
38387
- backgroundBlue: backgroundBlue,
38388
- teacherSignupBlue: teacherSignupBlue,
38389
- homepageGreen: homepageGreen,
38390
- streakColor: streakColor,
38391
- facebookColor: facebookColor,
38392
- blueGoogleColor: blueGoogleColor,
38393
- redGoogleColor: redGoogleColor,
38394
- homepageColor: homepageColor,
38395
- lightTextColor: lightTextColor,
38396
- fontSizeTiny: fontSizeTiny,
38397
- fontSizeNormal: fontSizeNormal,
38398
- fontSizeMedium: fontSizeMedium,
38399
- fontSizeExtraLarge: fontSizeExtraLarge,
38400
- fontSizeHuge: fontSizeHuge,
38401
- fontSizeBakana: fontSizeBakana,
38402
- fontSizeBananas: fontSizeBananas,
38403
- fixedWidthFontFamily: fixedWidthFontFamily,
38404
- contentPadding: contentPadding,
38405
- primaryButtonBackground: primaryButtonBackground,
38406
- primaryButtonBorder: primaryButtonBorder,
38407
- buttonTextColor: buttonTextColor,
38408
- buttonBorderRadius: buttonBorderRadius,
38409
- basicBorderColor: basicBorderColor,
38410
- minContainerWidth: minContainerWidth,
38411
- maxContainerWidth: maxContainerWidth,
38412
- formHintColor: formHintColor,
38413
- guidelineExampleText: guidelineExampleText,
38414
- hoverCardWidth: hoverCardWidth,
38415
- menuHighlightColor: menuHighlightColor,
38416
- menuHighlightBorderColor: menuHighlightBorderColor,
38417
- menuActiveTextColor: menuActiveTextColor,
38418
- menuInactiveTextColor: menuInactiveTextColor,
38419
- menuSubheaderTextColor: menuSubheaderTextColor,
38420
- menuBorderColor: menuBorderColor,
38421
- unreadBackgroundColor: unreadBackgroundColor,
38422
- menuLineHeight: menuLineHeight,
38423
- menuSubheaderLineHeight: menuSubheaderLineHeight,
38424
- menuSubheaderFontSize: menuSubheaderFontSize,
38425
- notificationBarColor: notificationBarColor,
38426
- modalBodyTopPadding: modalBodyTopPadding,
38427
- modalDialogPadding: modalDialogPadding,
38428
- modalSectionPadding: modalSectionPadding,
38429
- mathDomain1: mathDomain1,
38430
- mathDomain2: mathDomain2,
38431
- mathDomain3: mathDomain3,
38432
- mathDomain4: mathDomain4,
38433
- partnerContentDomain1: partnerContentDomain1,
38434
- partnerContentDomain2: partnerContentDomain2,
38435
- partnerContentDomain3: partnerContentDomain3,
38436
- partnerContentDomain4: partnerContentDomain4,
38437
- csDomain1: csDomain1,
38438
- csDomain2: csDomain2,
38439
- csDomain3: csDomain3,
38440
- csDomain4: csDomain4,
38441
- economicsDomain1: economicsDomain1,
38442
- economicsDomain2: economicsDomain2,
38443
- economicsDomain3: economicsDomain3,
38444
- economicsDomain4: economicsDomain4,
38445
- collegeCareersMoreDomain1: collegeCareersMoreDomain1,
38446
- collegeCareersMoreDomain2: collegeCareersMoreDomain2,
38447
- collegeCareersMoreDomain3: collegeCareersMoreDomain3,
38448
- collegeCareersMoreDomain4: collegeCareersMoreDomain4,
38449
- humanitiesDomain1: humanitiesDomain1,
38450
- humanitiesDomain2: humanitiesDomain2,
38451
- humanitiesDomain3: humanitiesDomain3,
38452
- humanitiesDomain4: humanitiesDomain4,
38453
- gtpDomain1: gtpDomain1,
38454
- gtpDomain2: gtpDomain2,
38455
- gtpDomain3: gtpDomain3,
38456
- gtpDomain4: gtpDomain4,
38457
- scienceDomain1: scienceDomain1,
38458
- scienceDomain2: scienceDomain2,
38459
- scienceDomain3: scienceDomain3,
38460
- scienceDomain4: scienceDomain4,
38461
- testPrepDomain1: testPrepDomain1,
38462
- testPrepDomain2: testPrepDomain2,
38463
- testPrepDomain3: testPrepDomain3,
38464
- testPrepDomain4: testPrepDomain4,
38465
- defaultDomain1: defaultDomain1,
38466
- defaultDomain2: defaultDomain2,
38467
- defaultDomain3: defaultDomain3,
38468
- defaultDomain4: defaultDomain4,
38469
- defaultDomainColor: defaultDomainColor,
38470
- defaultSubjectColor: defaultSubjectColor,
38471
- defaultTopicColor: defaultTopicColor,
38472
- defaultHoverColor: defaultHoverColor,
38473
- defaultLinkColor: defaultLinkColor,
38474
- defaultLinkHoverColor: defaultLinkHoverColor,
38475
- scienceDomainColor: scienceDomainColor,
38476
- scienceSubjectColor: scienceSubjectColor,
38477
- scienceTopicColor: scienceTopicColor,
38478
- scienceHoverColor: scienceHoverColor,
38479
- scienceLinkColor: scienceLinkColor,
38480
- scienceLinkHoverColor: scienceLinkHoverColor,
38481
- humanitiesDomainColor: humanitiesDomainColor,
38482
- humanitiesSubjectColor: humanitiesSubjectColor,
38483
- humanitiesTopicColor: humanitiesTopicColor,
38484
- humanitiesHoverColor: humanitiesHoverColor,
38485
- humanitiesLinkColor: humanitiesLinkColor,
38486
- humanitiesLinkHoverColor: humanitiesLinkHoverColor,
38487
- economicsDomainColor: economicsDomainColor,
38488
- economicsSubjectColor: economicsSubjectColor,
38489
- economicsTopicColor: economicsTopicColor,
38490
- economicsHoverColor: economicsHoverColor,
38491
- economicsLinkColor: economicsLinkColor,
38492
- economicsLinkHoverColor: economicsLinkHoverColor,
38493
- csDomainColor: csDomainColor,
38494
- csSubjectColor: csSubjectColor,
38495
- csTopicColor: csTopicColor,
38496
- csHoverColor: csHoverColor,
38497
- csLinkColor: csLinkColor,
38498
- csLinkHoverColor: csLinkHoverColor,
38499
- partnerContentDomainColor: partnerContentDomainColor,
38500
- partnerContentSubjectColor: partnerContentSubjectColor,
38501
- partnerContentTopicColor: partnerContentTopicColor,
38502
- partnerContentHoverColor: partnerContentHoverColor,
38503
- partnerContentLinkColor: partnerContentLinkColor,
38504
- partnerContentLinkHoverColor: partnerContentLinkHoverColor,
38505
- essayDomainColor: essayDomainColor,
38506
- mathDomainColor: mathDomainColor,
38507
- mathSubjectColor: mathSubjectColor,
38508
- mathTopicColor: mathTopicColor,
38509
- mathHoverColor: mathHoverColor,
38510
- mathLinkColor: mathLinkColor,
38511
- mathLinkHoverColor: mathLinkHoverColor,
38512
- testPrepDomainColor: testPrepDomainColor,
38513
- testPrepSubjectColor: testPrepSubjectColor,
38514
- testPrepTopicColor: testPrepTopicColor,
38515
- testPrepHoverColor: testPrepHoverColor,
38516
- testPrepLinkColor: testPrepLinkColor,
38517
- testPrepLinkHoverColor: testPrepLinkHoverColor,
38518
- collegeCareersMoreDomainColor: collegeCareersMoreDomainColor,
38519
- collegeCareersMoreSubjectColor: collegeCareersMoreSubjectColor,
38520
- collegeCareersMoreTopicColor: collegeCareersMoreTopicColor,
38521
- collegeCareersMoreHoverColor: collegeCareersMoreHoverColor,
38522
- collegeCareersMoreLinkColor: collegeCareersMoreLinkColor,
38523
- collegeCareersMoreLinkHoverColor: collegeCareersMoreLinkHoverColor,
38524
- colorProficient: colorProficient,
38525
- colorPracticed: colorPracticed,
38526
- colorMastery1: colorMastery1,
38527
- colorMastery2: colorMastery2,
38528
- colorMastery3: colorMastery3,
38529
- colorSuggested: colorSuggested,
38530
- colorReview: colorReview,
38531
- colorNotStarted: colorNotStarted,
38532
- colorStarted: colorStarted,
38533
- colorStruggling: colorStruggling,
38534
- colorPersonal: colorPersonal,
38535
- colorPractice: colorPractice,
38536
- colorEnergyPoints: colorEnergyPoints,
38537
- pureSmMin: pureSmMin,
38538
- pureMdMin: pureMdMin,
38539
- pureLgMin: pureLgMin,
38540
- pureXlMin: pureXlMin,
38541
- pureXsMax: pureXsMax,
38542
- pureSmMax: pureSmMax,
38543
- pureMdMax: pureMdMax,
38544
- pureLgMax: pureLgMax
38545
- });
38546
-
38547
- const colors = {
38548
- white: "#FFFFFF",
38549
- gray98: "#FAFAFA",
38550
- gray97: "#F6F7F7",
38551
- gray95: "#F0F1F2",
38552
- gray90: "#E3E5E6",
38553
- gray85: "#D6D8DA",
38554
- gray76: "#BABEC2",
38555
- gray72: "#999A9D",
38556
- gray68: "#888D93",
38557
- gray55: "#707378",
38558
- gray41: "#626569",
38559
- gray25: "#3B3E40",
38560
- gray17: "#21242C",
38561
- gray10: "#1499af",
38562
- black: "#000000",
38563
- // These colors are pulled out from constants.js, which is itself generated
38564
- // from variables.less. We should probably avoid duplicating these here.
38565
- alertRed,
38566
- homepageBlue,
38567
- kaBlue,
38568
- kaBlueLight,
38569
- kaGreen,
38570
- kaGreenLight,
38571
- learnstormBlue,
38572
- backgroundBlue,
38573
- adminGreen: "#78C008",
38574
- kaBlueExtraLight: "#39c2db",
38575
- kaBlueHover: "#0c7f99",
38576
- kaGreenHover: "#518005",
38577
- // Domain-agnostic colors related to the study guide experience in the
38578
- // content library.
38579
- skyDark: "#001f4d",
38580
- recommendationGold: "#ffbe26",
38581
- starAced: "#ff9c39",
38582
- // We interploate the domain colors into this colors object for
38583
- // historical reasons, but everyone using domain-specific colors
38584
- // should switch to using the domainColors() function to look up
38585
- // colors instead.
38586
- //
38587
- // TODO(davidflanagan): remove this line once everything is
38588
- // working correctly with the domainColors() accessor.
38589
- // ...domainToColorsMap,
38590
- // mission: missionColors,
38591
-
38592
- // Colors for various OAuth providers
38593
- google: "#C1432f",
38594
- modernGoogle: "#be2612",
38595
- modernGoogleHover: "#8c1c0d",
38596
- facebook: "#4267B2",
38597
- modernFacebook: "#4267B2",
38598
- modernFacebookHover: "#00294a",
38599
- wonderBlocksGold: "#FFB100",
38600
- wonderBlocksLightBlue: "#37C5FD",
38601
- // We're using rgba instead of opacity to avoid affecting the focus outline
38602
- // when we just want to make the color transparent.
38603
- breadcrumbs: "rgba(255, 255, 255, 0.7)"
38604
- };
38605
- const borderRadius = 4;
38606
- const breadcrumbStyles = {
38607
- color: colors.breadcrumbs,
38163
+ // TODO: can we use WB typography?
38164
+ const bodyXsmallBold = {
38165
+ // For emphasized metadata, descriptions, etc
38608
38166
  fontFamily: "inherit",
38609
38167
  fontSize: 15,
38610
38168
  fontWeight: "bold",
38611
- letterSpacing: 0.8,
38612
- lineHeight: "19px",
38613
- textTransform: "uppercase",
38614
- // Disable subpixel antialiasing for OSX (improves light on dark text)
38615
- MozOsxFontSmoothing: "grayscale",
38616
- WebkitFontSmoothing: "antialiased"
38617
- };
38618
- const fonts = {
38619
- // The body of the page is setup to use 'Helvetica' by default and
38620
- // 'Lato' if/once it downloads. For your elements to follow this
38621
- // pattern, all you need is to make the text in your element
38622
- // inherit from the body using the "inherit" value.
38623
- regular: "inherit"
38624
- };
38625
- const typography = {
38626
- // For subject names
38627
- subjectHeadingDesktop: {
38628
- fontFamily: "inherit",
38629
- fontSize: 50,
38630
- fontWeight: "bold",
38631
- lineHeight: "61px"
38632
- },
38633
- subjectHeadingMobile: {
38634
- fontFamily: "inherit",
38635
- fontSize: 35,
38636
- fontWeight: "bold",
38637
- lineHeight: "37px"
38638
- },
38639
- // For the most important thing on the page
38640
- conceptHeadingDesktop: {
38641
- fontFamily: "inherit",
38642
- fontSize: 35,
38643
- fontWeight: "bold",
38644
- lineHeight: "37px"
38645
- },
38646
- conceptHeadingMobile: {
38647
- fontFamily: "inherit",
38648
- fontSize: 23,
38649
- fontWeight: "bold",
38650
- lineHeight: "27px"
38651
- },
38652
- // For sections or modules
38653
- subheadingDesktop: {
38654
- fontFamily: "inherit",
38655
- fontSize: 23,
38656
- fontWeight: "bold",
38657
- lineHeight: "27px"
38658
- },
38659
- subheadingMobile: {
38660
- fontFamily: "inherit",
38661
- fontSize: 20,
38662
- fontWeight: "bold",
38663
- lineHeight: "24px"
38664
- },
38665
- smallSubheadingDesktop: {
38666
- fontFamily: "inherit",
38667
- fontSize: 20,
38668
- lineHeight: 1.25
38669
- },
38670
- smallSubheadingMobile: {
38671
- fontFamily: "inherit",
38672
- fontSize: 15,
38673
- lineHeight: 1.25
38674
- },
38675
- // For callouts
38676
- smallHeading: {
38677
- color: colors.gray25,
38678
- fontFamily: "inherit",
38679
- fontSize: 17,
38680
- fontWeight: "bold",
38681
- lineHeight: "23px"
38682
- },
38683
- smallHeadingMobile: {
38684
- color: colors.gray25,
38685
- fontFamily: "inherit",
38686
- fontSize: 15,
38687
- fontWeight: "bold",
38688
- lineHeight: "18px"
38689
- },
38690
- // Accent headings
38691
- accentHeading: {
38692
- color: colors.gray68,
38693
- fontFamily: "inherit",
38694
- fontSize: 15,
38695
- fontWeight: "bold",
38696
- letterSpacing: 0.8,
38697
- lineHeight: "19px",
38698
- textTransform: "uppercase"
38699
- },
38700
- bodyLarge: {
38701
- // For articles with multiple paragraphs
38702
- fontFamily: fonts.regular,
38703
- fontSize: 20,
38704
- lineHeight: "30px"
38705
- },
38706
- bodySmall: {
38707
- // For small blocks of copy
38708
- fontFamily: fonts.regular,
38709
- fontSize: 17,
38710
- fontWeight: "normal",
38711
- lineHeight: "23px"
38712
- },
38713
- bodySmallBold: {
38714
- // For small blocks of copy
38715
- fontFamily: fonts.regular,
38716
- fontSize: 17,
38717
- fontWeight: "bold",
38718
- lineHeight: "23px"
38719
- },
38720
- bodyXsmall: {
38721
- // For metadata, descriptions, etc
38722
- fontFamily: fonts.regular,
38723
- fontSize: 15,
38724
- lineHeight: "22px"
38725
- },
38726
- bodyXsmallBold: {
38727
- // For emphasized metadata, descriptions, etc
38728
- fontFamily: "inherit",
38729
- fontSize: 15,
38730
- fontWeight: "bold",
38731
- lineHeight: "22px"
38732
- },
38733
- breadcrumb: breadcrumbStyles,
38734
- // For in-page tabs, breadcrumbs
38735
- breadcrumbSmall: {
38736
- // For in-page tabs and leaf page breadcrumbs
38737
- ...breadcrumbStyles,
38738
- fontSize: 12,
38739
- lineHeight: "13px"
38740
- },
38741
- labelLarge: {
38742
- // For chiclets and thumbnail titles
38743
- fontFamily: "inherit",
38744
- fontSize: 23,
38745
- fontWeight: "bold",
38746
- lineHeight: "27px"
38747
- },
38748
- labelMedium: {
38749
- // For chiclets and thumbnail titles
38750
- fontFamily: "inherit",
38751
- fontSize: 15,
38752
- fontWeight: "bold",
38753
- lineHeight: "18px"
38754
- },
38755
- labelSmall: {
38756
- // Generally used with labelLarge, for breadcrumbs in thumbnails
38757
- fontFamily: fonts.regular,
38758
- fontSize: 12,
38759
- lineHeight: "14px"
38760
- },
38761
- caption: {
38762
- // For image captions
38763
- fontFamily: fonts.regular,
38764
- fontSize: 14,
38765
- fontStyle: "italic",
38766
- lineHeight: "17px"
38767
- },
38768
- desktopScalingOnMobile: {
38769
- // For when you need Mobile Safari and friends to stick to desktop
38770
- // sizes
38771
- MozTextSizeAdjust: "100%",
38772
- MsTextSizeAdjust: "100%",
38773
- WebkitTextSizeAdjust: "100%"
38774
- },
38775
- // TODO (josh): This should move to within <ModalHeader>
38776
- modalSubtitle: {
38777
- fontSize: 12,
38778
- lineHeight: 1.65,
38779
- // NOTE (josh): Font smoothing used here to create a 'medium' weight
38780
- // font, between normal and bold, on webkit browsers.
38781
- // This prop is an a11y risk when used on non-bold text, since it
38782
- // makes text very thin, but it's fine on bold text.
38783
- WebkitFontSmoothing: "antialiased",
38784
- fontWeight: "bold"
38785
- },
38786
- // HACK(kevinb): temporary wonder blocks styles for header only
38787
- // TODO(kevinb): use components from wonder-blocks-typography instead
38788
- wonderBlocksHeader: {
38789
- paddingLeft: 16,
38790
- paddingRight: 16,
38791
- paddingBottom: 24,
38792
- paddingTop: 34,
38793
- [mediaQueries.mdOrLarger]: {
38794
- paddingBottom: 32
38795
- }
38796
- },
38797
- // HACK(kevinb): temporary wonder blocks styles for header only
38798
- // TODO(kevinb): use components from wonder-blocks-typography instead
38799
- wonderBlocksTitle: {
38800
- color: colors.white,
38801
- fontWeight: 900,
38802
- // "Black" weight
38803
- fontSize: 28,
38804
- lineHeight: "32px",
38805
- margin: 0,
38806
- [mediaQueries.mdOrLarger]: {
38807
- fontSize: 36,
38808
- lineHeight: "40px"
38809
- }
38810
- },
38811
- // HACK(kevinb): temporary wonder blocks styles for header only
38812
- // TODO(kevinb): use components from wonder-blocks-typography instead
38813
- wonderBlocksLabelLarge: {
38814
- fontSize: 16,
38815
- lineHeight: "20px",
38816
- fontWeight: 700 // Bold
38817
- }
38169
+ lineHeight: "22px"
38818
38170
  };
38819
38171
 
38820
- var globalStyles = /*#__PURE__*/Object.freeze({
38821
- __proto__: null,
38822
- colors: colors,
38823
- borderRadius: borderRadius,
38824
- fonts: fonts,
38825
- typography: typography
38826
- });
38827
-
38828
38172
  const AnswerChoices = props => {
38829
38173
  const {
38830
38174
  strings
@@ -39742,7 +39086,7 @@ const styles$a = aphrodite.StyleSheet.create({
39742
39086
  paddingBottom: 16
39743
39087
  },
39744
39088
  instructionsCaption: {
39745
- ...typography.bodyXsmallBold,
39089
+ ...bodyXsmallBold,
39746
39090
  paddingBottom: 16
39747
39091
  },
39748
39092
  instructionsChoices: {
@@ -43577,9 +42921,9 @@ const makeSafeUrl = (urlString, locale) => {
43577
42921
  };
43578
42922
  const styles$6 = aphrodite.StyleSheet.create({
43579
42923
  widgetContainer: {
43580
- borderRadius: borderRadiusLarge,
42924
+ borderRadius: 6,
43581
42925
  borderWidth: 1,
43582
- borderColor: basicBorderColor,
42926
+ borderColor: "#CCC",
43583
42927
  padding: wonderBlocksTokens.spacing.medium_16,
43584
42928
  paddingBottom: 0
43585
42929
  },
@@ -44892,7 +44236,7 @@ const VideoTranscriptLink = props => {
44892
44236
  } = props;
44893
44237
  const {
44894
44238
  useVideo
44895
- } = getDependencies();
44239
+ } = useDependencies();
44896
44240
  const [id, kind] = IS_URL$1.test(location) ? [getYoutubeId(location), "YOUTUBE_ID"] : [location, "READABLE_ID"];
44897
44241
  // The result value conforms to the wonder-blocks-data `Result` type
44898
44242
  // which is used by our GraphQL framework.
@@ -45068,7 +44412,7 @@ const init = function (options) {
45068
44412
 
45069
44413
  // This file is processed by a Rollup plugin (replace) to inject the production
45070
44414
  const libName = "@khanacademy/perseus";
45071
- const libVersion = "39.0.1";
44415
+ const libVersion = "41.0.0";
45072
44416
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
45073
44417
 
45074
44418
  const apiVersion = {
@@ -45349,7 +44693,7 @@ const styles$3 = aphrodite.StyleSheet.create({
45349
44693
  },
45350
44694
  lastRenderedNewHint: {
45351
44695
  marginBottom: 0,
45352
- borderLeftColor: kaGreen$1
44696
+ borderLeftColor: kaGreen
45353
44697
  }
45354
44698
  });
45355
44699
 
@@ -45540,7 +44884,7 @@ const styles$2 = aphrodite.StyleSheet.create({
45540
44884
  fontSize: "100%",
45541
44885
  fontFamily: "inherit",
45542
44886
  fontWeight: "bold",
45543
- color: kaGreen$1,
44887
+ color: kaGreen,
45544
44888
  padding: 0,
45545
44889
  position: "relative"
45546
44890
  },
@@ -45942,28 +45286,6 @@ const ref = /*#__PURE__*/React__namespace.forwardRef((props, ref) => /*#__PURE__
45942
45286
  }));
45943
45287
  }));
45944
45288
 
45945
- const devices = {
45946
- PHONE: "phone",
45947
- TABLET: "tablet",
45948
- DESKTOP: "desktop"
45949
- };
45950
-
45951
- // How many pixels do we reserve on the right-hand side of a preview
45952
- // for displaying lint indicators? This space needs to be reserved
45953
- // in DeviceFramer, but it is actually allocated in PerseusFrame
45954
- const lintGutterWidth = 36;
45955
-
45956
- // How wide a border does PerseusFrame draw? We need to allocate enough
45957
- // space for it in DeviceFramer.
45958
- const perseusFrameBorderWidth = 1;
45959
-
45960
- var constants = /*#__PURE__*/Object.freeze({
45961
- __proto__: null,
45962
- devices: devices,
45963
- lintGutterWidth: lintGutterWidth,
45964
- perseusFrameBorderWidth: perseusFrameBorderWidth
45965
- });
45966
-
45967
45289
  /**
45968
45290
  * Traverses a {content, widgets, images} renderer props object,
45969
45291
  * such as `itemData.question`
@@ -48656,6 +47978,18 @@ const EditorJsonify = {
48656
47978
  }
48657
47979
  };
48658
47980
 
47981
+ /**
47982
+ * Main entry point
47983
+ */
47984
+ const GrapherUtil = {
47985
+ allTypes,
47986
+ DEFAULT_GRAPHER_PROPS,
47987
+ chooseType,
47988
+ defaultPlotProps,
47989
+ getEquationString,
47990
+ typeToButton
47991
+ };
47992
+
48659
47993
  exports.ApiOptions = ApiOptions;
48660
47994
  exports.ArticleRenderer = ArticleRenderer;
48661
47995
  exports.BaseRadio = BaseRadio;
@@ -48665,7 +47999,7 @@ exports.ClassNames = ClassNames;
48665
47999
  exports.Dependencies = dependencies;
48666
48000
  exports.EditorJsonify = EditorJsonify;
48667
48001
  exports.Expression = Expression;
48668
- exports.GrapherUtil = util;
48002
+ exports.GrapherUtil = GrapherUtil;
48669
48003
  exports.GrapherWidget = Grapher$1;
48670
48004
  exports.HintRenderer = HintRenderer;
48671
48005
  exports.HintsRenderer = HintsRenderer;
@@ -48689,17 +48023,17 @@ exports.QUESTION_WIDGETS = QUESTION_WIDGETS;
48689
48023
  exports.Radio = Radio;
48690
48024
  exports.Renderer = Renderer;
48691
48025
  exports.ServerItemRenderer = ref;
48692
- exports.SizingUtils = sizingUtils;
48693
48026
  exports.TableWidget = Table$1;
48694
48027
  exports.Util = Util;
48695
48028
  exports.WIDGET_PROP_DENYLIST = denylist;
48696
48029
  exports.Widgets = widgets$1;
48697
48030
  exports.addWidget = addWidget;
48698
48031
  exports.apiVersion = apiVersion;
48032
+ exports.bodyXsmallBold = bodyXsmallBold;
48699
48033
  exports.buildEmptyItemTreeForShape = buildEmptyItemTreeForShape;
48700
48034
  exports.buildMapper = buildMapper;
48701
48035
  exports.components = components;
48702
- exports.constants = constants;
48036
+ exports.containerSizeClass = containerSizeClass;
48703
48037
  exports.contentHasWidgetType = contentHasWidgetType;
48704
48038
  exports.convertWidgetNameToEnum = convertWidgetNameToEnum;
48705
48039
  exports.displaySigFigs = displaySigFigs;
@@ -48717,6 +48051,7 @@ exports.getCircleCoords = getCircleCoords;
48717
48051
  exports.getClockwiseAngle = getClockwiseAngle;
48718
48052
  exports.getCorrectAnswerForWidgetId = getCorrectAnswerForWidgetId;
48719
48053
  exports.getImagesWithoutAltData = getImagesWithoutAltData;
48054
+ exports.getInteractiveBoxFromSizeClass = getInteractiveBoxFromSizeClass;
48720
48055
  exports.getLineCoords = getLineCoords;
48721
48056
  exports.getLinearSystemCoords = getLinearSystemCoords;
48722
48057
  exports.getPointCoords = getPointCoords;
@@ -48731,9 +48066,8 @@ exports.getWidgetIdsFromContentByType = getWidgetIdsFromContentByType;
48731
48066
  exports.getWidgetTypeByWidgetId = getWidgetTypeByWidgetId;
48732
48067
  exports.getWidgetsFromWidgetMap = getWidgetsFromWidgetMap;
48733
48068
  exports.getWidgetsMapFromItemData = getWidgetsMapFromItemData;
48734
- exports.globalConstants = globalConstants;
48735
- exports.globalStyles = globalStyles;
48736
- exports.icons = iconPaths;
48069
+ exports.iconChevronDown = iconChevronDown;
48070
+ exports.iconTrash = iconTrash;
48737
48071
  exports.init = init;
48738
48072
  exports.injectWidgets = injectWidgets;
48739
48073
  exports.interactiveSizes = interactiveSizes$1;