@khanacademy/perseus 30.0.1 → 31.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/es/index.js CHANGED
@@ -15,7 +15,7 @@ import ReactDOM__default from 'react-dom';
15
15
  import _, { debounce as debounce$1 } from 'underscore';
16
16
  import Clickable from '@khanacademy/wonder-blocks-clickable';
17
17
  import { Popover, PopoverContentCore, PopoverContent } from '@khanacademy/wonder-blocks-popover';
18
- import { color, spacing } from '@khanacademy/wonder-blocks-tokens';
18
+ import { color, spacing, font } from '@khanacademy/wonder-blocks-tokens';
19
19
  import $ from 'jquery';
20
20
  import { v4 } from 'uuid';
21
21
  import PropTypes from 'prop-types';
@@ -40,6 +40,8 @@ import Switch from '@khanacademy/wonder-blocks-switch';
40
40
  import Pill from '@khanacademy/wonder-blocks-pill';
41
41
  import { Popper } from 'react-popper';
42
42
  import createReactClass from 'create-react-class';
43
+ import IconButton from '@khanacademy/wonder-blocks-icon-button';
44
+ import cornersOutIcon from '@phosphor-icons/core/regular/corners-out.svg';
43
45
  import Link from '@khanacademy/wonder-blocks-link';
44
46
 
45
47
  function _extends() {
@@ -250,7 +252,9 @@ const mockStrings = {
250
252
  mathInputDescription: "Use keyboard/mouse to interact with math-based input fields",
251
253
  sin: "sin",
252
254
  cos: "cos",
253
- tan: "tan"
255
+ tan: "tan",
256
+ simulationLoadFail: "Sorry, this simulation cannot load.",
257
+ simulationLocaleWarning: "Sorry, this simulation isn't available in your language."
254
258
  };
255
259
 
256
260
  /**
@@ -503,7 +507,7 @@ class InnerMathInput extends React.Component {
503
507
  className = className + " " + this.props.className;
504
508
  }
505
509
  return /*#__PURE__*/React.createElement(View, {
506
- style: [styles$D.outerWrapper, this.state.focused && styles$D.wrapperFocused, this.props.hasError && styles$D.wrapperError]
510
+ style: [styles$E.outerWrapper, this.state.focused && styles$E.wrapperFocused, this.props.hasError && styles$E.wrapperError]
507
511
  }, /*#__PURE__*/React.createElement("div", {
508
512
  style: {
509
513
  display: "flex",
@@ -540,7 +544,7 @@ class InnerMathInput extends React.Component {
540
544
  style: a11y.srOnly
541
545
  }, this.context.strings.mathInputDescription), /*#__PURE__*/React.createElement(PopoverContentCore, {
542
546
  closeButtonVisible: true,
543
- style: styles$D.popoverContent
547
+ style: styles$E.popoverContent
544
548
  }, /*#__PURE__*/React.createElement(DesktopKeypad, _extends({
545
549
  onAnalyticsEvent: this.props.analytics.onAnalyticsEvent,
546
550
  extraKeys: this.props.extraKeys,
@@ -616,9 +620,9 @@ const MathInputIcon = ({
616
620
  fillColor = color.offBlack;
617
621
  break;
618
622
  }
619
- const dynamicClass = active || focused ? styles$D.iconActive : styles$D.iconInactive;
623
+ const dynamicClass = active || focused ? styles$E.iconActive : styles$E.iconInactive;
620
624
  return /*#__PURE__*/React.createElement(View, {
621
- style: [styles$D.iconContainer, dynamicClass]
625
+ style: [styles$E.iconContainer, dynamicClass]
622
626
  }, /*#__PURE__*/React.createElement("svg", {
623
627
  xmlns: "http://www.w3.org/2000/svg",
624
628
  width: "16",
@@ -663,7 +667,7 @@ const inputFocused = {
663
667
  borderColor: color.blue,
664
668
  margin: -1
665
669
  };
666
- const styles$D = StyleSheet.create({
670
+ const styles$E = StyleSheet.create({
667
671
  iconContainer: {
668
672
  display: "flex",
669
673
  justifyContent: "center",
@@ -2489,7 +2493,7 @@ class Expression extends React.Component {
2489
2493
  var _this$props$keypadCon, _this$props$analytics3;
2490
2494
  if (this.props.apiOptions.customKeypad) {
2491
2495
  return /*#__PURE__*/React.createElement(View, {
2492
- className: css(styles$C.mobileLabelInputWrapper)
2496
+ className: css(styles$D.mobileLabelInputWrapper)
2493
2497
  }, !!this.props.visibleLabel && /*#__PURE__*/React.createElement(LabelSmall, {
2494
2498
  htmlFor: this._textareaId,
2495
2499
  tag: "label"
@@ -2524,7 +2528,7 @@ class Expression extends React.Component {
2524
2528
  ERROR_TITLE
2525
2529
  } = this.context.strings;
2526
2530
  return /*#__PURE__*/React.createElement(View, {
2527
- className: css(styles$C.desktopLabelInputWrapper)
2531
+ className: css(styles$D.desktopLabelInputWrapper)
2528
2532
  }, !!this.props.visibleLabel && /*#__PURE__*/React.createElement(LabelSmall, {
2529
2533
  htmlFor: this._textareaId,
2530
2534
  tag: "label"
@@ -2576,7 +2580,7 @@ Expression.defaultProps = {
2576
2580
  apiOptions: ApiOptions.defaults,
2577
2581
  linterContext: linterContextDefault
2578
2582
  };
2579
- const styles$C = StyleSheet.create({
2583
+ const styles$D = StyleSheet.create({
2580
2584
  mobileLabelInputWrapper: {
2581
2585
  padding: "15px 4px 0"
2582
2586
  },
@@ -18199,33 +18203,33 @@ class Lint extends React.Component {
18199
18203
  let warningText;
18200
18204
  let warningTextStyle;
18201
18205
  if (this.props.severity === Severity.Error) {
18202
- severityStyle = styles$B.indicatorError;
18206
+ severityStyle = styles$C.indicatorError;
18203
18207
  warningText = "Error";
18204
- warningTextStyle = styles$B.publishBlockingError;
18208
+ warningTextStyle = styles$C.publishBlockingError;
18205
18209
  } else if (this.props.severity === Severity.Warning) {
18206
- severityStyle = styles$B.indicatorWarning;
18210
+ severityStyle = styles$C.indicatorWarning;
18207
18211
  warningText = "Warning";
18208
- warningTextStyle = styles$B.warning;
18212
+ warningTextStyle = styles$C.warning;
18209
18213
  } else {
18210
- severityStyle = styles$B.indicatorGuideline;
18214
+ severityStyle = styles$C.indicatorGuideline;
18211
18215
  warningText = "Recommendation";
18212
- warningTextStyle = styles$B.warning;
18216
+ warningTextStyle = styles$C.warning;
18213
18217
  }
18214
18218
  return /*#__PURE__*/React.createElement("a", {
18215
18219
  href: `https://khanacademy.org/r/linter-rules#${this.props.ruleName}`,
18216
18220
  target: "lint-help-window",
18217
18221
  className: css(style)
18218
18222
  }, /*#__PURE__*/React.createElement("span", {
18219
- className: css(styles$B.indicator, severityStyle)
18223
+ className: css(styles$C.indicator, severityStyle)
18220
18224
  }, this.props.severity === 1 && /*#__PURE__*/React.createElement(InlineIcon, exclamationIcon)), /*#__PURE__*/React.createElement("div", {
18221
- className: css(styles$B.tooltip, tooltipAbove && styles$B.tooltipAbove)
18225
+ className: css(styles$C.tooltip, tooltipAbove && styles$C.tooltipAbove)
18222
18226
  }, this.props.message.split("\n\n").map((m, i) => /*#__PURE__*/React.createElement("p", {
18223
18227
  key: i,
18224
- className: css(styles$B.tooltipParagraph)
18228
+ className: css(styles$C.tooltipParagraph)
18225
18229
  }, /*#__PURE__*/React.createElement("span", {
18226
18230
  className: css(warningTextStyle)
18227
18231
  }, warningText, ":", " "), m)), /*#__PURE__*/React.createElement("div", {
18228
- className: css(styles$B.tail, tooltipAbove && styles$B.tailAbove)
18232
+ className: css(styles$C.tail, tooltipAbove && styles$C.tailAbove)
18229
18233
  })));
18230
18234
  };
18231
18235
  }
@@ -18263,20 +18267,20 @@ class Lint extends React.Component {
18263
18267
  }
18264
18268
  if (blockHighlight) {
18265
18269
  return /*#__PURE__*/React.createElement("span", {
18266
- className: css(styles$B.lintContainer, styles$B.lintContainerBlock)
18267
- }, this.renderLink(styles$B.radioWidgetHoverTarget), /*#__PURE__*/React.createElement("span", null, children));
18270
+ className: css(styles$C.lintContainer, styles$C.lintContainerBlock)
18271
+ }, this.renderLink(styles$C.radioWidgetHoverTarget), /*#__PURE__*/React.createElement("span", null, children));
18268
18272
  }
18269
18273
  if (inline) {
18270
18274
  return /*#__PURE__*/React.createElement("span", {
18271
- className: css(styles$B.lintContainer)
18272
- }, this.renderLink(styles$B.inlineHoverTarget), /*#__PURE__*/React.createElement("span", null, children));
18275
+ className: css(styles$C.lintContainer)
18276
+ }, this.renderLink(styles$C.inlineHoverTarget), /*#__PURE__*/React.createElement("span", null, children));
18273
18277
  }
18274
18278
  return /*#__PURE__*/React.createElement("div", {
18275
- className: css(styles$B.lintContainer)
18276
- }, this.renderLink(styles$B.hoverTarget), /*#__PURE__*/React.createElement("div", null, children));
18279
+ className: css(styles$C.lintContainer)
18280
+ }, this.renderLink(styles$C.hoverTarget), /*#__PURE__*/React.createElement("div", null, children));
18277
18281
  }
18278
18282
  }
18279
- const styles$B = StyleSheet.create({
18283
+ const styles$C = StyleSheet.create({
18280
18284
  // This is the class of the outermost element.
18281
18285
  // We use relative positioning so that the lint indicator can be
18282
18286
  // positioned absolutely relative to the position of the linty container.
@@ -20210,7 +20214,7 @@ class Renderer extends React.Component {
20210
20214
  paddingLeft: margin,
20211
20215
  paddingRight: margin
20212
20216
  };
20213
- const mobileInnerStyle = _extends({}, innerStyle, styles$A.mobileZoomableParentFix);
20217
+ const mobileInnerStyle = _extends({}, innerStyle, styles$B.mobileZoomableParentFix);
20214
20218
  return /*#__PURE__*/React.createElement("div", {
20215
20219
  key: state.key,
20216
20220
  className: "perseus-block-math",
@@ -20334,7 +20338,7 @@ class Renderer extends React.Component {
20334
20338
  paddingLeft: 0,
20335
20339
  paddingRight: 0
20336
20340
  };
20337
- const mobileInnerStyle = _extends({}, innerStyle, styles$A.mobileZoomableParentFix);
20341
+ const mobileInnerStyle = _extends({}, innerStyle, styles$B.mobileZoomableParentFix);
20338
20342
  const wrappedOutput = /*#__PURE__*/React.createElement("div", {
20339
20343
  style: _extends({}, mobileInnerStyle, {
20340
20344
  overflowX: "auto"
@@ -20982,7 +20986,7 @@ Renderer.defaultProps = {
20982
20986
  onSerializedStateUpdated: () => {},
20983
20987
  linterContext: PerseusLinter.linterContextDefault
20984
20988
  };
20985
- const styles$A = {
20989
+ const styles$B = {
20986
20990
  mobileZoomableParentFix: {
20987
20991
  // NOTE(abdul): There is an issue where transform animations will
20988
20992
  // cause the Zoomable component to disappear when running on the
@@ -21852,10 +21856,10 @@ class InputNumber extends React.Component {
21852
21856
  // Note: This is _very_ similar to what `numeric-input.jsx` does. If
21853
21857
  // you modify this, double-check if you also need to modify that
21854
21858
  // component.
21855
- const inputStyles = [styles$z.default, this.props.size === "small" ? styles$z.small : null, this.props.rightAlign ? styles$z.rightAlign : styles$z.leftAlign];
21859
+ const inputStyles = [styles$A.default, this.props.size === "small" ? styles$A.small : null, this.props.rightAlign ? styles$A.rightAlign : styles$A.leftAlign];
21856
21860
  // Unanswered
21857
21861
  if (rubric && !this.props.currentValue) {
21858
- inputStyles.push(styles$z.answerStateUnanswered);
21862
+ inputStyles.push(styles$A.answerStateUnanswered);
21859
21863
  }
21860
21864
  return /*#__PURE__*/React.createElement(InputWithExamples
21861
21865
  // eslint-disable-next-line react/no-string-refs
@@ -21940,7 +21944,7 @@ InputNumber.defaultProps = {
21940
21944
  apiOptions: ApiOptions.defaults,
21941
21945
  linterContext: linterContextDefault
21942
21946
  };
21943
- const styles$z = StyleSheet.create({
21947
+ const styles$A = StyleSheet.create({
21944
21948
  default: {
21945
21949
  width: 80,
21946
21950
  height: "auto"
@@ -22935,7 +22939,7 @@ const FocusRing = function FocusRing(props) {
22935
22939
  };
22936
22940
  return /*#__PURE__*/React.createElement("span", {
22937
22941
  "data-testid": "focus-ring",
22938
- className: css(styles$y.ring),
22942
+ className: css(styles$z.ring),
22939
22943
  style: style
22940
22944
  }, children);
22941
22945
  };
@@ -22944,7 +22948,7 @@ FocusRing.defaultProps = {
22944
22948
  color: kaGreen$1,
22945
22949
  multipleSelect: false
22946
22950
  };
22947
- const styles$y = StyleSheet.create({
22951
+ const styles$z = StyleSheet.create({
22948
22952
  ring: {
22949
22953
  margin: "auto",
22950
22954
  display: "inline-block",
@@ -22971,7 +22975,7 @@ function getChoiceLetter(pos, strings) {
22971
22975
  }
22972
22976
 
22973
22977
  const CHOICE_ICON_SIZE = 24;
22974
- const styles$x = StyleSheet.create({
22978
+ const styles$y = StyleSheet.create({
22975
22979
  iconWrapper: {
22976
22980
  display: "inline-block",
22977
22981
  position: "relative"
@@ -22994,7 +22998,7 @@ const CrossOutLine = function CrossOutLine(props) {
22994
22998
  width: CROSS_OUT_LINE_SIZE,
22995
22999
  height: CROSS_OUT_LINE_SIZE,
22996
23000
  viewBox: `0 0 ${CROSS_OUT_LINE_SIZE} ${CROSS_OUT_LINE_SIZE}`,
22997
- className: css(styles$w.crossOutLine),
23001
+ className: css(styles$x.crossOutLine),
22998
23002
  "data-testid": "choice-icon__cross-out-line"
22999
23003
  }, /*#__PURE__*/React.createElement("line", {
23000
23004
  // The line stretches from the bottom-left to top-right.
@@ -23009,7 +23013,7 @@ const CrossOutLine = function CrossOutLine(props) {
23009
23013
  strokeLinecap: "round"
23010
23014
  }));
23011
23015
  };
23012
- const styles$w = StyleSheet.create({
23016
+ const styles$x = StyleSheet.create({
23013
23017
  crossOutLine: {
23014
23018
  // Center the icon within the container.
23015
23019
  position: "absolute",
@@ -23093,7 +23097,7 @@ const ChoiceIcon = function ChoiceIcon(props) {
23093
23097
  } = props;
23094
23098
  const dynamicStyles = getDynamicStyles(checked, showCorrectness, pressed, multipleSelect, correct);
23095
23099
  return /*#__PURE__*/React.createElement("div", {
23096
- className: css(styles$x.iconWrapper)
23100
+ className: css(styles$y.iconWrapper)
23097
23101
  }, /*#__PURE__*/React.createElement(FocusRing, {
23098
23102
  color: color.blue,
23099
23103
  visible: focused || hovered,
@@ -23102,13 +23106,13 @@ const ChoiceIcon = function ChoiceIcon(props) {
23102
23106
  // @ts-expect-error - TS2322 - Type '{ backgroundColor: string | null | undefined; borderColor: string; color: string; borderRadius: number; }' is not assignable to type 'Properties<string | number, string & {}>'.
23103
23107
  style: dynamicStyles,
23104
23108
  "data-testid": "choice-icon__library-choice-icon",
23105
- className: css(styles$v.circle, showCorrectness && correct && styles$v.circleCorrect, showCorrectness && !correct && styles$v.circleIncorrect, showCorrectness && !correct && (checked || previouslyAnswered) && styles$v.circleIncorrectAnswered)
23109
+ className: css(styles$w.circle, showCorrectness && correct && styles$w.circleCorrect, showCorrectness && !correct && styles$w.circleIncorrect, showCorrectness && !correct && (checked || previouslyAnswered) && styles$w.circleIncorrectAnswered)
23106
23110
  // used in BaseRadio editMode to check
23107
23111
  // if we actually clicked on the radio icon
23108
23112
  ,
23109
23113
  "data-is-radio-icon": true
23110
23114
  }, /*#__PURE__*/React.createElement("div", {
23111
- className: css(styles$v.innerWrapper)
23115
+ className: css(styles$w.innerWrapper)
23112
23116
  }, /*#__PURE__*/React.createElement(ChoiceInner, {
23113
23117
  pos: pos,
23114
23118
  showCorrectness: showCorrectness,
@@ -23117,7 +23121,7 @@ const ChoiceIcon = function ChoiceIcon(props) {
23117
23121
  color: dynamicStyles.borderColor
23118
23122
  }));
23119
23123
  };
23120
- const styles$v = StyleSheet.create({
23124
+ const styles$w = StyleSheet.create({
23121
23125
  innerWrapper: {
23122
23126
  width: CHOICE_ICON_SIZE,
23123
23127
  height: CHOICE_ICON_SIZE,
@@ -23212,19 +23216,19 @@ const OptionStatus = function OptionStatus(props) {
23212
23216
  }
23213
23217
  let textStyle;
23214
23218
  if (correct) {
23215
- textStyle = styles$u.correct;
23219
+ textStyle = styles$v.correct;
23216
23220
  } else {
23217
23221
  if (checked || previouslyAnswered) {
23218
- textStyle = styles$u.incorrectAnswered;
23222
+ textStyle = styles$v.incorrectAnswered;
23219
23223
  } else {
23220
- textStyle = styles$u.incorrect;
23224
+ textStyle = styles$v.incorrect;
23221
23225
  }
23222
23226
  }
23223
23227
  return /*#__PURE__*/React.createElement("div", {
23224
- className: css(styles$u.text, textStyle)
23228
+ className: css(styles$v.text, textStyle)
23225
23229
  }, renderText(checked, correct, crossedOut, strings));
23226
23230
  };
23227
- const styles$u = StyleSheet.create({
23231
+ const styles$v = StyleSheet.create({
23228
23232
  text: {
23229
23233
  alignItems: "center",
23230
23234
  display: "flex",
@@ -23294,8 +23298,8 @@ const Choice = function Choice(props) {
23294
23298
  crossedOut: updatedCrossedOut
23295
23299
  });
23296
23300
  }
23297
- const descriptionClassName = classNames("description", css(styles$t.description));
23298
- const rationaleClassName = classNames("perseus-radio-rationale-content", css(styles$t.rationale));
23301
+ const descriptionClassName = classNames("description", css(styles$u.description));
23302
+ const rationaleClassName = classNames("perseus-radio-rationale-content", css(styles$u.rationale));
23299
23303
 
23300
23304
  // We want to show the choices as dimmed out when the choices are disabled.
23301
23305
  // However, we don't want to do this when we're in review mode in the
@@ -23455,7 +23459,7 @@ const Choice = function Choice(props) {
23455
23459
  "data-testid": `perseus-radio-rationale-content-${pos}`
23456
23460
  }, rationale));
23457
23461
  };
23458
- const styles$t = StyleSheet.create({
23462
+ const styles$u = StyleSheet.create({
23459
23463
  description: {
23460
23464
  display: "inline-block",
23461
23465
  width: "100%"
@@ -23654,10 +23658,10 @@ const BaseRadio = function BaseRadio(props) {
23654
23658
  const isMobile = apiOptions.isMobile;
23655
23659
  const firstChoiceHighlighted = choices[0].highlighted;
23656
23660
  const lastChoiceHighlighted = choices[choices.length - 1].highlighted;
23657
- const className = classNames("perseus-widget-radio", !editMode && "perseus-rendered-radio", css(styles$s.radio, styles$s.responsiveRadioContainer, firstChoiceHighlighted && isMobile && styles$s.radioContainerFirstHighlighted, lastChoiceHighlighted && isMobile && styles$s.radioContainerLastHighlighted));
23658
- const instructionsClassName = classNames("instructions", css(styles$s.instructions, isMobile && styles$s.instructionsMobile));
23661
+ const className = classNames("perseus-widget-radio", !editMode && "perseus-rendered-radio", css(styles$t.radio, styles$t.responsiveRadioContainer, firstChoiceHighlighted && isMobile && styles$t.radioContainerFirstHighlighted, lastChoiceHighlighted && isMobile && styles$t.radioContainerLastHighlighted));
23662
+ const instructionsClassName = classNames("instructions", css(styles$t.instructions, isMobile && styles$t.instructionsMobile));
23659
23663
  const instructions = getInstructionsText(multipleSelect, countChoices, numCorrect, strings);
23660
- const responsiveClassName = css(styles$s.responsiveFieldset);
23664
+ const responsiveClassName = css(styles$t.responsiveFieldset);
23661
23665
  const fieldset = /*#__PURE__*/React.createElement("fieldset", {
23662
23666
  className: `perseus-widget-radio-fieldset ${responsiveClassName}`
23663
23667
  }, /*#__PURE__*/React.createElement("legend", {
@@ -23708,7 +23712,7 @@ const BaseRadio = function BaseRadio(props) {
23708
23712
  const aphroditeClassName = checked => {
23709
23713
  // Whether or not to show correctness borders
23710
23714
  // for this choice and the next choice.
23711
- return css(sharedStyles.aboveScratchpad, styles$s.item, styles$s.responsiveItem, checked && styles$s.selectedItem, checked && choice.highlighted && styles$s.aboveBackdrop, checked && choice.highlighted && apiOptions.isMobile && styles$s.aboveBackdropMobile, nextChoiceHighlighted && apiOptions.isMobile && styles$s.nextHighlighted);
23715
+ return css(sharedStyles.aboveScratchpad, styles$t.item, styles$t.responsiveItem, checked && styles$t.selectedItem, checked && choice.highlighted && styles$t.aboveBackdrop, checked && choice.highlighted && apiOptions.isMobile && styles$t.aboveBackdropMobile, nextChoiceHighlighted && apiOptions.isMobile && styles$t.nextHighlighted);
23712
23716
  };
23713
23717
 
23714
23718
  // HACK(abdulrahman): Preloads the selection-state
@@ -23782,14 +23786,14 @@ const BaseRadio = function BaseRadio(props) {
23782
23786
  // Allow for horizontal scrolling if content is too wide, which may be
23783
23787
  // an issue especially on phones.
23784
23788
  return /*#__PURE__*/React.createElement("div", {
23785
- className: css(styles$s.responsiveContainer)
23789
+ className: css(styles$t.responsiveContainer)
23786
23790
  }, fieldset);
23787
23791
  };
23788
23792
  BaseRadio.defaultProps = {
23789
23793
  editMode: false,
23790
23794
  multipleSelect: false
23791
23795
  };
23792
- const styles$s = StyleSheet.create({
23796
+ const styles$t = StyleSheet.create({
23793
23797
  instructions: {
23794
23798
  display: "block",
23795
23799
  color: gray17,
@@ -24451,9 +24455,9 @@ class Categorizer extends React.Component {
24451
24455
  indexedItems = Util.shuffle(indexedItems, this.props.problemNum);
24452
24456
  }
24453
24457
  const table = /*#__PURE__*/React.createElement("table", {
24454
- className: "categorizer-table " + css(styles$r.mobileTable)
24458
+ className: "categorizer-table " + css(styles$s.mobileTable)
24455
24459
  }, /*#__PURE__*/React.createElement("thead", null, /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("td", {
24456
- className: css(styles$r.emptyHeaderCell)
24460
+ className: css(styles$s.emptyHeaderCell)
24457
24461
  }), this.props.categories.map((category, i) => {
24458
24462
  // Array index is the correct key here, as that's
24459
24463
  // how category grading actually works -- no way
@@ -24461,7 +24465,7 @@ class Categorizer extends React.Component {
24461
24465
  // middle. (If we later add that, this should be
24462
24466
  // fixed.)
24463
24467
  return /*#__PURE__*/React.createElement("th", {
24464
- className: css(styles$r.header),
24468
+ className: css(styles$s.header),
24465
24469
  key: i
24466
24470
  }, /*#__PURE__*/React.createElement(Renderer, {
24467
24471
  content: category,
@@ -24483,7 +24487,7 @@ class Categorizer extends React.Component {
24483
24487
  })), self.props.categories.map((catName, catNum) => {
24484
24488
  const selected = self.props.values[itemNum] === catNum;
24485
24489
  return /*#__PURE__*/React.createElement("td", {
24486
- className: "category " + css(styles$r.cell),
24490
+ className: "category " + css(styles$s.cell),
24487
24491
  key: catNum
24488
24492
  }, /*#__PURE__*/React.createElement("div", {
24489
24493
  className: ClassNames.INTERACTIVE,
@@ -24498,7 +24502,7 @@ class Categorizer extends React.Component {
24498
24502
  onChange: () => this.onChange(itemNum, catNum),
24499
24503
  onClick: e => e.stopPropagation()
24500
24504
  }), !isMobile && /*#__PURE__*/React.createElement("span", {
24501
- className: css(styles$r.radioSpan, selected && styles$r.checkedRadioSpan, this.props.static && selected && styles$r.staticCheckedRadioSpan)
24505
+ className: css(styles$s.radioSpan, selected && styles$s.checkedRadioSpan, this.props.static && selected && styles$s.staticCheckedRadioSpan)
24502
24506
  }, selected ? /*#__PURE__*/React.createElement(InlineIcon, iconCircle) : /*#__PURE__*/React.createElement(InlineIcon, iconCircleThin))));
24503
24507
  }));
24504
24508
  /* eslint-enable max-len */
@@ -24510,7 +24514,7 @@ class Categorizer extends React.Component {
24510
24514
  "categorizer-container": true,
24511
24515
  "static-mode": this.props.static
24512
24516
  });
24513
- const inlineStyles = this.props.apiOptions.isMobile ? [styles$r.fullBleedContainer] : [];
24517
+ const inlineStyles = this.props.apiOptions.isMobile ? [styles$s.fullBleedContainer] : [];
24514
24518
  return /*#__PURE__*/React.createElement("div", {
24515
24519
  className: extraClassNames + " " + css(...inlineStyles)
24516
24520
  }, table);
@@ -24563,7 +24567,7 @@ const pageMargin = 16;
24563
24567
 
24564
24568
  // Stylesheets aren't directly testable
24565
24569
  /* c8 ignore next */
24566
- const styles$r = StyleSheet.create({
24570
+ const styles$s = StyleSheet.create({
24567
24571
  mobileTable: {
24568
24572
  [mediaQueries.smOrSmaller]: {
24569
24573
  minWidth: "auto"
@@ -24799,7 +24803,7 @@ class CSProgram extends React.Component {
24799
24803
  // creator "went wild".
24800
24804
  // http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
24801
24805
  return /*#__PURE__*/React.createElement("div", {
24802
- className: css(styles$q.widthOverride, styleContainer && styles$q.container)
24806
+ className: css(styles$r.widthOverride, styleContainer && styles$r.container)
24803
24807
  }, /*#__PURE__*/React.createElement("iframe", {
24804
24808
  sandbox: sandboxOptions,
24805
24809
  src: url,
@@ -24828,7 +24832,7 @@ CSProgram.defaultProps = {
24828
24832
  // optional message
24829
24833
  message: null
24830
24834
  };
24831
- const styles$q = StyleSheet.create({
24835
+ const styles$r = StyleSheet.create({
24832
24836
  // Override the inherited width from the perseus paragraph class
24833
24837
  // 820 is minimum width for 420px editor and 400px canvas
24834
24838
  widthOverride: {
@@ -24871,7 +24875,7 @@ class Definition extends React.Component {
24871
24875
  }) => /*#__PURE__*/React.createElement(Popover, {
24872
24876
  content: /*#__PURE__*/React.createElement(PopoverContentCore, {
24873
24877
  color: "white",
24874
- style: styles$p.tooltipBody,
24878
+ style: styles$q.tooltipBody,
24875
24879
  closeButtonVisible: true
24876
24880
  }, /*#__PURE__*/React.createElement(Renderer, {
24877
24881
  apiOptions: this.props.apiOptions,
@@ -24904,7 +24908,7 @@ Definition.defaultProps = {
24904
24908
  togglePrompt: "define me",
24905
24909
  definition: "definition goes here"
24906
24910
  };
24907
- const styles$p = {
24911
+ const styles$q = {
24908
24912
  tooltipBody: {
24909
24913
  color: color.offBlack,
24910
24914
  fontSize: 20,
@@ -25132,7 +25136,7 @@ class Explanation extends React.Component {
25132
25136
  marginRight: "-6px",
25133
25137
  "white-space": "normal"
25134
25138
  };
25135
- const contentStyling = [styles$o.content, this.state.expanded ? styles$o.contentExpanded : styles$o.contentCollapsed, allowTransition && (this.state.expanded ? styles$o.transitionExpanded : styles$o.transitionCollapsed)];
25139
+ const contentStyling = [styles$p.content, this.state.expanded ? styles$p.contentExpanded : styles$p.contentCollapsed, allowTransition && (this.state.expanded ? styles$p.transitionExpanded : styles$p.transitionCollapsed)];
25136
25140
  return /*#__PURE__*/React.createElement(UniqueIDProvider, {
25137
25141
  mockOnFirstRender: true,
25138
25142
  scope: "explanation-widget"
@@ -25151,7 +25155,7 @@ class Explanation extends React.Component {
25151
25155
  "aria-hidden": !this.state.expanded,
25152
25156
  testId: "content-container"
25153
25157
  }, /*#__PURE__*/React.createElement(View, {
25154
- style: styles$o.contentWrapper
25158
+ style: styles$p.contentWrapper
25155
25159
  }, /*#__PURE__*/React.createElement(Renderer, {
25156
25160
  apiOptions: this.props.apiOptions,
25157
25161
  content: this.props.explanation,
@@ -25172,7 +25176,7 @@ Explanation.defaultProps = {
25172
25176
  const leftBorderSpacing = 23;
25173
25177
  const verticalContentPadding = 10;
25174
25178
  const arrowHeight = 14;
25175
- const styles$o = StyleSheet.create({
25179
+ const styles$p = StyleSheet.create({
25176
25180
  content: {
25177
25181
  borderLeft: "0px solid #ccc",
25178
25182
  display: "inline-grid",
@@ -25236,15 +25240,15 @@ class GradedGroupAnswerBar extends React.Component {
25236
25240
  correctExcited,
25237
25241
  nextQuestion
25238
25242
  } = this.context.strings;
25239
- const answerBarStyle = _extends({}, styles$n.answerBar, {
25243
+ const answerBarStyle = _extends({}, styles$o.answerBar, {
25240
25244
  backgroundColor: answerBarState === "CORRECT" ? color.offWhite : "white",
25241
25245
  // Center the "Correct!" message only when there's no next question
25242
25246
  justifyContent: answerBarState === "CORRECT" && !onNextQuestion ? "center" : "space-between"
25243
25247
  });
25244
25248
  const message = answerBarState === "INCORRECT" ? /*#__PURE__*/React.createElement("span", {
25245
- style: styles$n.text
25249
+ style: styles$o.text
25246
25250
  }, /*#__PURE__*/React.createElement("span", {
25247
- style: styles$n.tryAgainIcon
25251
+ style: styles$o.tryAgainIcon
25248
25252
  }, /*#__PURE__*/React.createElement(InlineIcon, iconTryAgain)), /*#__PURE__*/React.createElement("span", {
25249
25253
  style: {
25250
25254
  marginLeft: 8
@@ -25263,7 +25267,7 @@ class GradedGroupAnswerBar extends React.Component {
25263
25267
  return /*#__PURE__*/React.createElement("div", {
25264
25268
  style: answerBarStyle
25265
25269
  }, /*#__PURE__*/React.createElement("span", {
25266
- style: styles$n.text
25270
+ style: styles$o.text
25267
25271
  }, /*#__PURE__*/React.createElement("span", {
25268
25272
  style: {
25269
25273
  fontSize: 28,
@@ -25286,7 +25290,7 @@ class GradedGroupAnswerBar extends React.Component {
25286
25290
  }
25287
25291
  GradedGroupAnswerBar.contextType = PerseusI18nContext;
25288
25292
  const fontSize = 17;
25289
- const styles$n = {
25293
+ const styles$o = {
25290
25294
  answerBar: {
25291
25295
  display: "flex",
25292
25296
  alignItems: "center",
@@ -25471,7 +25475,7 @@ class GradedGroup extends React.Component {
25471
25475
  }));
25472
25476
  gradeStatus = this.context.strings.incorrect;
25473
25477
  }
25474
- const mobileClass = this.props.inGradedGroupSet ? css(styles$m.gradedGroupInSet) : css(styles$m.gradedGroup);
25478
+ const mobileClass = this.props.inGradedGroupSet ? css(styles$n.gradedGroupInSet) : css(styles$n.gradedGroup);
25475
25479
  const classes = classNames({
25476
25480
  [mobileClass]: apiOptions.isMobile,
25477
25481
  "perseus-graded-group": true,
@@ -25490,7 +25494,7 @@ class GradedGroup extends React.Component {
25490
25494
  return /*#__PURE__*/React.createElement("div", {
25491
25495
  className: classes
25492
25496
  }, !!this.props.title && /*#__PURE__*/React.createElement("div", {
25493
- className: css(styles$m.title)
25497
+ className: css(styles$n.title)
25494
25498
  }, this.props.title), /*#__PURE__*/React.createElement(Renderer, _extends({}, this.props, {
25495
25499
  // eslint-disable-next-line react/no-string-refs
25496
25500
  ref: "renderer",
@@ -25523,7 +25527,7 @@ class GradedGroup extends React.Component {
25523
25527
  }, this.context.strings.nextQuestion), this.props.hint && this.props.hint.content && (this.state.showHint ? /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("button", {
25524
25528
  // @ts-expect-error - TS2322 - Type 'string' is not assignable to type 'number | undefined'.
25525
25529
  tabIndex: "0",
25526
- className: css(styles$m.explanationTitle),
25530
+ className: css(styles$n.explanationTitle),
25527
25531
  onClick: () => this.setState({
25528
25532
  showHint: false
25529
25533
  }),
@@ -25556,7 +25560,7 @@ class GradedGroup extends React.Component {
25556
25560
  showHint: true
25557
25561
  });
25558
25562
  },
25559
- className: css(styles$m.showHintLink)
25563
+ className: css(styles$n.showHintLink)
25560
25564
  }, this.context.strings.explain)), apiOptions.isMobile && answerBarState !== "HIDDEN" && /*#__PURE__*/React.createElement(GradedGroupAnswerBar, {
25561
25565
  apiOptions: apiOptions,
25562
25566
  answerBarState: answerBarState,
@@ -25578,7 +25582,7 @@ GradedGroup.defaultProps = {
25578
25582
  const traverseChildWidgets$2 = function traverseChildWidgets(props, traverseRenderer) {
25579
25583
  return _.extend({}, props, traverseRenderer(props));
25580
25584
  };
25581
- const styles$m = StyleSheet.create({
25585
+ const styles$n = StyleSheet.create({
25582
25586
  gradedGroupInSet: {
25583
25587
  // Reset a few desktop-only styles that come from graded-group.less
25584
25588
  marginLeft: 0,
@@ -25651,18 +25655,18 @@ class Indicators extends React.Component {
25651
25655
  return /*#__PURE__*/React.createElement("ul", {
25652
25656
  // reduntantly add class name for use in .less files--
25653
25657
  // the styles object key gets hashed
25654
- className: classNames(css(styles$l.indicatorContainer), "indicatorContainer")
25658
+ className: classNames(css(styles$m.indicatorContainer), "indicatorContainer")
25655
25659
  }, this.props.gradedGroups.map(({
25656
25660
  title
25657
25661
  }, i) => /*#__PURE__*/React.createElement("li", {
25658
- className: css(styles$l.indicator),
25662
+ className: css(styles$m.indicator),
25659
25663
  key: title
25660
25664
  }, /*#__PURE__*/React.createElement(Clickable, {
25661
25665
  role: "button",
25662
25666
  "aria-label": this.context.strings.skipToTitle({
25663
25667
  title
25664
25668
  }),
25665
- style: styles$l.indicatorButton,
25669
+ style: styles$m.indicatorButton,
25666
25670
  onClick: () => this.props.onChangeCurrentGroup(i),
25667
25671
  onKeyDown: e => this.handleKeyDown(e, i)
25668
25672
  }, ({
@@ -25670,9 +25674,9 @@ class Indicators extends React.Component {
25670
25674
  focused,
25671
25675
  pressed
25672
25676
  }) => /*#__PURE__*/React.createElement(View, {
25673
- style: [styles$l.indicatorDot, (hovered || focused || pressed) && styles$l.indicatorDotFocused]
25677
+ style: [styles$m.indicatorDot, (hovered || focused || pressed) && styles$m.indicatorDotFocused]
25674
25678
  }, i === this.props.currentGroup && /*#__PURE__*/React.createElement(View, {
25675
- style: styles$l.indicatorDotActive
25679
+ style: styles$m.indicatorDotActive
25676
25680
  }, /*#__PURE__*/React.createElement("span", {
25677
25681
  className: css(a11y.srOnly)
25678
25682
  }, this.context.strings.current)))))));
@@ -25736,7 +25740,7 @@ class GradedGroupSet extends React.Component {
25736
25740
  } = getDependencies();
25737
25741
  if (JIPT.useJIPT && this.props.gradedGroups.length > 1) {
25738
25742
  return /*#__PURE__*/React.createElement("div", {
25739
- className: css(styles$l.container)
25743
+ className: css(styles$m.container)
25740
25744
  }, this.props.gradedGroups.map((gradedGroup, i) => {
25741
25745
  return (
25742
25746
  /*#__PURE__*/
@@ -25759,13 +25763,13 @@ class GradedGroupSet extends React.Component {
25759
25763
  const numGroups = this.props.gradedGroups.length;
25760
25764
  const handleNextQuestion = this.state.currentGroup < numGroups - 1 ? this.handleNextQuestion : null;
25761
25765
  return /*#__PURE__*/React.createElement("div", {
25762
- className: css(styles$l.container)
25766
+ className: css(styles$m.container)
25763
25767
  }, /*#__PURE__*/React.createElement("div", {
25764
- className: css(styles$l.top)
25768
+ className: css(styles$m.top)
25765
25769
  }, /*#__PURE__*/React.createElement("div", {
25766
- className: css(styles$l.title)
25770
+ className: css(styles$m.title)
25767
25771
  }, currentGroup.title), /*#__PURE__*/React.createElement("div", {
25768
- className: css(styles$l.spacer)
25772
+ className: css(styles$m.spacer)
25769
25773
  }), /*#__PURE__*/React.createElement(Indicators, {
25770
25774
  currentGroup: this.state.currentGroup,
25771
25775
  gradedGroups: this.props.gradedGroups,
@@ -25807,7 +25811,7 @@ var GradedGroupSet$1 = {
25807
25811
  tracking: "all",
25808
25812
  isLintable: true
25809
25813
  };
25810
- const styles$l = StyleSheet.create({
25814
+ const styles$m = StyleSheet.create({
25811
25815
  top: {
25812
25816
  display: "flex",
25813
25817
  flexDirection: "row"
@@ -25912,7 +25916,7 @@ class ButtonGroup extends React.Component {
25912
25916
  id: "" + i,
25913
25917
  ref: "button" + i,
25914
25918
  key: "" + i,
25915
- className: css(styles$k.buttonStyle, button.value === value && styles$k.selectedStyle),
25919
+ className: css(styles$l.buttonStyle, button.value === value && styles$l.selectedStyle),
25916
25920
  onClick: () => this.toggleSelect(button.value)
25917
25921
  }, button.content || "" + button.value);
25918
25922
  });
@@ -25929,7 +25933,7 @@ ButtonGroup.defaultProps = {
25929
25933
  value: null,
25930
25934
  allowEmpty: true
25931
25935
  };
25932
- const styles$k = StyleSheet.create({
25936
+ const styles$l = StyleSheet.create({
25933
25937
  buttonStyle: {
25934
25938
  backgroundColor: "white",
25935
25939
  border: "1px solid #ccc",
@@ -28339,65 +28343,6 @@ Graph.defaultProps = {
28339
28343
  isMobile: false
28340
28344
  };
28341
28345
 
28342
- const defaultGraphConfig = {
28343
- range: [[0, 1], [0, 1]],
28344
- tickStep: [1, 1],
28345
- gridStep: [1, 1],
28346
- snapStep: [1, 1],
28347
- markings: "none",
28348
- showTooltips: false,
28349
- graphDimensionsInPixels: [1, 1],
28350
- width: 0,
28351
- height: 0,
28352
- labels: [],
28353
- disableKeyboardInteraction: false
28354
- };
28355
- const GraphConfigContext = /*#__PURE__*/createContext(defaultGraphConfig);
28356
- function useGraphConfig() {
28357
- return React__default.useContext(GraphConfigContext);
28358
- }
28359
-
28360
- const matrixBuilder = vec.matrixBuilder;
28361
-
28362
- // When converting vectors, we don't need to translate because a vector
28363
- // is like a pair of x and y distances, which are not bound to specific graph locations.
28364
- function vectorsToPixels(vectors, graphState, translation = [0, 0]) {
28365
- const {
28366
- range,
28367
- width,
28368
- height
28369
- } = graphState;
28370
- const [[xMin, xMax], [yMin, yMax]] = range;
28371
- const transformToPx = matrixBuilder().translate(...translation).scale(width / (xMax - xMin), -height / (yMax - yMin)).get();
28372
- return vectors.map(p => vec.transform(p, transformToPx));
28373
- }
28374
- function dimensionsToPixels(dimens, graphState) {
28375
- const {
28376
- range,
28377
- width,
28378
- height
28379
- } = graphState;
28380
- const [[xMin, xMax], [yMin, yMax]] = range;
28381
- const transformToPx = matrixBuilder().scale(width / (xMax - xMin), height / (yMax - yMin)).get();
28382
- return dimens.map(d => vec.transform(d, transformToPx));
28383
- }
28384
-
28385
- // When converting points, we translate so the pixel point ends up in the right location.
28386
- // This is because points are specific locations on graphs, unlike vectors.
28387
- function pointToPixel(point, graphState) {
28388
- const [[xMin], [, yMax]] = graphState.range;
28389
- const [a, b] = [-xMin, -yMax];
28390
- return vectorsToPixels([point], graphState, [a, b])[0];
28391
- }
28392
- const useTransformVectorsToPixels = (...vectors) => {
28393
- const graphState = useGraphConfig();
28394
- return vectorsToPixels(vectors, graphState);
28395
- };
28396
- const useTransformDimensionsToPixels = (...dimens) => {
28397
- const graphState = useGraphConfig();
28398
- return dimensionsToPixels(dimens, graphState);
28399
- };
28400
-
28401
28346
  // Restricts `value` to be between `min` and `max`.
28402
28347
  function clamp(value, min, max) {
28403
28348
  if (value < min) {
@@ -28541,6 +28486,79 @@ const findAngle = (point1, point2, vertex) => {
28541
28486
  return findAngle(point1, vertex) - findAngle(point2, vertex);
28542
28487
  };
28543
28488
 
28489
+ const defaultGraphConfig = {
28490
+ range: [[0, 1], [0, 1]],
28491
+ tickStep: [1, 1],
28492
+ gridStep: [1, 1],
28493
+ snapStep: [1, 1],
28494
+ markings: "none",
28495
+ showTooltips: false,
28496
+ graphDimensionsInPixels: [1, 1],
28497
+ width: 0,
28498
+ height: 0,
28499
+ labels: [],
28500
+ disableKeyboardInteraction: false
28501
+ };
28502
+ const GraphConfigContext = /*#__PURE__*/createContext(defaultGraphConfig);
28503
+ function useGraphConfig() {
28504
+ return React__default.useContext(GraphConfigContext);
28505
+ }
28506
+
28507
+ const matrixBuilder = vec.matrixBuilder;
28508
+
28509
+ // When converting vectors, we don't need to translate because a vector
28510
+ // is like a pair of x and y distances, which are not bound to specific graph locations.
28511
+ function vectorsToPixels(vectors, graphState, translation = [0, 0]) {
28512
+ const {
28513
+ range,
28514
+ width,
28515
+ height
28516
+ } = graphState;
28517
+ const [[xMin, xMax], [yMin, yMax]] = range;
28518
+ const transformToPx = matrixBuilder().translate(...translation).scale(width / (xMax - xMin), -height / (yMax - yMin)).get();
28519
+ return vectors.map(p => vec.transform(p, transformToPx));
28520
+ }
28521
+ function dimensionsToPixels(dimens, graphState) {
28522
+ const {
28523
+ range,
28524
+ width,
28525
+ height
28526
+ } = graphState;
28527
+ const [[xMin, xMax], [yMin, yMax]] = range;
28528
+ const transformToPx = matrixBuilder().scale(width / (xMax - xMin), height / (yMax - yMin)).get();
28529
+ return dimens.map(d => vec.transform(d, transformToPx));
28530
+ }
28531
+
28532
+ // When converting points, we translate so the pixel point ends up in the right location.
28533
+ // This is because points are specific locations on graphs, unlike vectors.
28534
+ function pointToPixel(point, graphState) {
28535
+ const [[xMin], [, yMax]] = graphState.range;
28536
+ const [a, b] = [-xMin, -yMax];
28537
+ return vectorsToPixels([point], graphState, [a, b])[0];
28538
+ }
28539
+ const useTransformVectorsToPixels = (...vectors) => {
28540
+ const graphState = useGraphConfig();
28541
+ return vectorsToPixels(vectors, graphState);
28542
+ };
28543
+ const useTransformDimensionsToPixels = (...dimens) => {
28544
+ const graphState = useGraphConfig();
28545
+ return dimensionsToPixels(dimens, graphState);
28546
+ };
28547
+ function pixelsToVectors(pixels, graphState) {
28548
+ const [[xMin, xMax], [yMin, yMax]] = graphState.range;
28549
+ const {
28550
+ width,
28551
+ height
28552
+ } = graphState;
28553
+ const xSpan = xMax - xMin;
28554
+ const ySpan = yMax - yMin;
28555
+ return pixels.map(pixel => {
28556
+ const x = pixel[X] / width * xSpan + xMin;
28557
+ const y = yMax - pixel[Y] / height * ySpan;
28558
+ return [x, y];
28559
+ });
28560
+ }
28561
+
28544
28562
  function AxisLabels() {
28545
28563
  const {
28546
28564
  range,
@@ -29127,7 +29145,7 @@ const VisibleIcon = () => /*#__PURE__*/React.createElement("svg", {
29127
29145
  width: "24",
29128
29146
  height: "24",
29129
29147
  viewBox: "0 0 24 24",
29130
- className: css(styles$j.icon)
29148
+ className: css(styles$k.icon)
29131
29149
  }, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("path", {
29132
29150
  id: "a",
29133
29151
  d: "M7.401 10.035c-1.424.748-2.599 1.905-3.544 " + "3.48a1 1 0 0 1-1.714-1.03C4.325 8.849 7.652 7 " + "12 7c4.348 0 7.675 1.848 9.857 5.486a1 1 0 0 " + "1-1.714 1.028c-.945-1.574-2.12-2.73-3.544-" + "3.48a5 5 0 1 1-9.198 0zM12 15a3 3 0 1 0 0-6 3 3 " + "0 0 0 0 6z"
@@ -29158,7 +29176,7 @@ const HiddenIcon = () => /*#__PURE__*/React.createElement("svg", {
29158
29176
  width: "24",
29159
29177
  height: "24",
29160
29178
  viewBox: "0 0 24 24",
29161
- className: css(styles$j.icon)
29179
+ className: css(styles$k.icon)
29162
29180
  }, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("path", {
29163
29181
  id: "a",
29164
29182
  d: "M8.794 7.38C9.791 7.127 10.86 7 12 7c4.348 0 " + "7.675 1.848 9.857 5.486a1 1 0 0 1-1.714 " + "1.028c-.945-1.574-2.12-2.73-3.544-3.48.258." + "604.401 1.268.401 1.966 0 1.02-.305 " + "1.967-.828 2.757l2.535 2.536a1 1 0 0 " + "1-1.414 1.414l-12-12a1 1 0 0 1 " + "1.414-1.414L8.794 7.38zm5.914 5.913a3 3 0 0 " + "0-4.001-4.001l4 4.001zM6.072 8.486l2.976 " + "2.976a3 3 0 0 0 3.49 3.49l1.579 1.58A5 5 0 " + "0 1 7.4 10.035c-1.424.747-2.599 1.904-3.544 " + "3.478a1 1 0 0 1-1.714-1.028c1.049-1.75 " + "2.363-3.085 3.929-4z"
@@ -29192,20 +29210,20 @@ const HUD = ({
29192
29210
  let state;
29193
29211
  let icon;
29194
29212
  if (enabled) {
29195
- state = styles$j.enabled;
29213
+ state = styles$k.enabled;
29196
29214
  icon = /*#__PURE__*/React.createElement(VisibleIcon, null);
29197
29215
  } else {
29198
- state = styles$j.disabled;
29216
+ state = styles$k.disabled;
29199
29217
  icon = /*#__PURE__*/React.createElement(HiddenIcon, null);
29200
29218
  }
29201
29219
  return /*#__PURE__*/React.createElement("button", {
29202
- className: css(styles$j.hud, _fixedPosition && styles$j.hudFixedPosition, state),
29220
+ className: css(styles$k.hud, _fixedPosition && styles$k.hudFixedPosition, state),
29203
29221
  onClick: e => {
29204
29222
  onClick();
29205
29223
  }
29206
29224
  }, icon, message);
29207
29225
  };
29208
- const styles$j = StyleSheet.create({
29226
+ const styles$k = StyleSheet.create({
29209
29227
  hud: {
29210
29228
  boxSizing: "border-box",
29211
29229
  height: 36,
@@ -29258,16 +29276,16 @@ const styles$j = StyleSheet.create({
29258
29276
  const InfoTipBase = props => {
29259
29277
  return /*#__PURE__*/React.createElement(Tooltip$1, {
29260
29278
  content: /*#__PURE__*/React.createElement(Body, {
29261
- style: styles$i.body
29279
+ style: styles$j.body
29262
29280
  }, props.children),
29263
29281
  placement: "right"
29264
29282
  }, /*#__PURE__*/React.createElement(PhosphorIcon, {
29265
29283
  size: "small",
29266
29284
  icon: questionIcon,
29267
- style: styles$i.spacingLeft
29285
+ style: styles$j.spacingLeft
29268
29286
  }));
29269
29287
  };
29270
- const styles$i = StyleSheet.create({
29288
+ const styles$j = StyleSheet.create({
29271
29289
  spacingLeft: {
29272
29290
  marginInlineStart: spacing.xxxSmall_4
29273
29291
  },
@@ -29346,7 +29364,7 @@ class MultiButtonGroup extends React.Component {
29346
29364
  id: "" + i,
29347
29365
  key: "" + i,
29348
29366
  ref: "button" + i,
29349
- className: css(styles$h.buttonStyle, selected && styles$h.selectedStyle),
29367
+ className: css(styles$i.buttonStyle, selected && styles$i.selectedStyle),
29350
29368
  onClick: () => this.toggleSelect(button.value)
29351
29369
  }, button.content || "" + button.value);
29352
29370
  });
@@ -29362,7 +29380,7 @@ MultiButtonGroup.defaultProps = {
29362
29380
  values: [],
29363
29381
  allowEmpty: true
29364
29382
  };
29365
- const styles$h = StyleSheet.create({
29383
+ const styles$i = StyleSheet.create({
29366
29384
  buttonStyle: {
29367
29385
  backgroundColor: "white",
29368
29386
  border: "1px solid #ccc",
@@ -29797,6 +29815,8 @@ const ItemExtras = [
29797
29815
 
29798
29816
  // prettier-ignore
29799
29817
 
29818
+ // prettier-ignore
29819
+
29800
29820
  //prettier-ignore
29801
29821
 
29802
29822
  // A background image applied to various widgets.
@@ -29817,8 +29837,53 @@ const lockedFigureFillStyles = {
29817
29837
  translucent: 0.4,
29818
29838
  solid: 1
29819
29839
  };
29840
+
29841
+ // Not associated with a specific figure
29842
+
29820
29843
  const plotterPlotTypes = ["bar", "line", "pic", "histogram", "dotplot"];
29821
29844
 
29845
+ function LockedLabel(props) {
29846
+ const {
29847
+ coord,
29848
+ text,
29849
+ color,
29850
+ size
29851
+ } = props;
29852
+ const [x, y] = pointToPixel(coord, useGraphConfig());
29853
+ const {
29854
+ TeX
29855
+ } = getDependencies();
29856
+
29857
+ // Move this all outside the SVG element
29858
+ return /*#__PURE__*/React.createElement("span", {
29859
+ className: "locked-label",
29860
+ style: {
29861
+ position: "absolute",
29862
+ left: x,
29863
+ top: y,
29864
+ color: lockedFigureColors[color],
29865
+ fontSize: font.size[size]
29866
+ }
29867
+ }, /*#__PURE__*/React.createElement(TeX, null, text));
29868
+ }
29869
+
29870
+ function GraphLockedLabelsLayer(props) {
29871
+ const {
29872
+ lockedFigures
29873
+ } = props;
29874
+ return lockedFigures.map((figure, i) => {
29875
+ if (figure.type === "label") {
29876
+ return /*#__PURE__*/React.createElement(LockedLabel, _extends({
29877
+ key: `label-${i}`
29878
+ }, figure));
29879
+ }
29880
+
29881
+ // TODO(LEMS-2271): Add support for labels within
29882
+ // other locked figure types
29883
+ return null;
29884
+ });
29885
+ }
29886
+
29822
29887
  const LockedEllipse = props => {
29823
29888
  const {
29824
29889
  center,
@@ -30133,6 +30198,12 @@ const GraphLockedLayer = props => {
30133
30198
  return /*#__PURE__*/React.createElement(LockedFunction, _extends({
30134
30199
  key: `function-${index}`
30135
30200
  }, figure));
30201
+ case "label":
30202
+ // This is rendered outside the SVG element, since
30203
+ // TeX cannot be rendered inside an SVG.
30204
+ // See graph-locked-labels-layer.tsx for
30205
+ // the component that renders these.
30206
+ return null;
30136
30207
  default:
30137
30208
  /**
30138
30209
  * Devlopment-time future-proofing: This should
@@ -30162,7 +30233,8 @@ const actions = {
30162
30233
  movePointInFigure
30163
30234
  },
30164
30235
  pointGraph: {
30165
- movePoint
30236
+ movePoint,
30237
+ addPoint
30166
30238
  },
30167
30239
  polygon: {
30168
30240
  movePoint,
@@ -30191,6 +30263,13 @@ function moveLine(itemIndex, delta) {
30191
30263
  delta
30192
30264
  };
30193
30265
  }
30266
+ const ADD_POINT = "add-point";
30267
+ function addPoint(location) {
30268
+ return {
30269
+ type: ADD_POINT,
30270
+ location
30271
+ };
30272
+ }
30194
30273
  const MOVE_ALL = "move-all";
30195
30274
  function moveAll(delta) {
30196
30275
  return {
@@ -31727,7 +31806,7 @@ function getAngleSideConstraint(sidePoint, vertex, snapDegrees) {
31727
31806
  };
31728
31807
  }
31729
31808
 
31730
- function PointGraph(props) {
31809
+ function LimitedPointGraph(props) {
31731
31810
  const {
31732
31811
  dispatch
31733
31812
  } = props;
@@ -31737,6 +31816,46 @@ function PointGraph(props) {
31737
31816
  onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination))
31738
31817
  })));
31739
31818
  }
31819
+ function UnlimitedPointGraph(props) {
31820
+ const {
31821
+ dispatch
31822
+ } = props;
31823
+ const graphState = useGraphConfig();
31824
+ const {
31825
+ range: [[minX, maxX], [minY, maxY]]
31826
+ } = graphState;
31827
+ const width = maxX - minX;
31828
+ const height = maxY - minY;
31829
+ const [[widthPx, heightPx]] = useTransformDimensionsToPixels([width, height]);
31830
+ const [[left, top]] = useTransformVectorsToPixels([minX, maxY]);
31831
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("rect", {
31832
+ style: {
31833
+ fill: "rgba(0,0,0,0)"
31834
+ },
31835
+ width: widthPx,
31836
+ height: heightPx,
31837
+ x: left,
31838
+ y: top,
31839
+ onClick: event => {
31840
+ const elementRect = event.currentTarget.getBoundingClientRect();
31841
+ const x = event.clientX - elementRect.x;
31842
+ const y = event.clientY - elementRect.y;
31843
+ const graphCoordinates = pixelsToVectors([[x, y]], graphState);
31844
+ dispatch(actions.pointGraph.addPoint(graphCoordinates[0]));
31845
+ }
31846
+ }), props.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
31847
+ key: i,
31848
+ point: point,
31849
+ onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination))
31850
+ })));
31851
+ }
31852
+ function PointGraph(props) {
31853
+ const numPoints = props.graphState.numPoints;
31854
+ if (numPoints === "unlimited") {
31855
+ return UnlimitedPointGraph(props);
31856
+ }
31857
+ return LimitedPointGraph(props);
31858
+ }
31740
31859
 
31741
31860
  const protractorImage = "https://ka-perseus-graphie.s3.amazonaws.com/e9d032f2ab8b95979f674fbfa67056442ba1ff6a.png";
31742
31861
 
@@ -31897,11 +32016,11 @@ const MafsGraph = props => {
31897
32016
  labels,
31898
32017
  disableKeyboardInteraction: readOnly || !!props.static
31899
32018
  }
31900
- }, /*#__PURE__*/React.createElement(View, {
32019
+ }, /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(View, {
31901
32020
  className: "mafs-graph",
31902
32021
  style: {
31903
- width,
31904
32022
  height,
32023
+ width: "intrinsic",
31905
32024
  position: "relative",
31906
32025
  padding: "25px 25px 0 0",
31907
32026
  boxSizing: "content-box",
@@ -31913,6 +32032,8 @@ const MafsGraph = props => {
31913
32032
  }, /*#__PURE__*/React.createElement(LegacyGrid, {
31914
32033
  box: props.box,
31915
32034
  backgroundImage: props.backgroundImage
32035
+ }), props.showLabelsFlag && props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLabelsLayer, {
32036
+ lockedFigures: props.lockedFigures
31916
32037
  }), /*#__PURE__*/React.createElement(View, {
31917
32038
  style: {
31918
32039
  position: "absolute",
@@ -31942,7 +32063,41 @@ const MafsGraph = props => {
31942
32063
  }), props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), renderGraph({
31943
32064
  state,
31944
32065
  dispatch
31945
- })))));
32066
+ })))), renderGraphControls({
32067
+ state,
32068
+ dispatch
32069
+ })));
32070
+ };
32071
+ const renderPointGraphControls = props => /*#__PURE__*/React.createElement(Button, {
32072
+ kind: "secondary",
32073
+ style: {
32074
+ width: "100%",
32075
+ marginLeft: "20px"
32076
+ },
32077
+ onClick: () => {
32078
+ props.dispatch(actions.pointGraph.addPoint([0, 0]));
32079
+ }
32080
+ }, "Add Point");
32081
+ const renderGraphControls = props => {
32082
+ const {
32083
+ state,
32084
+ dispatch
32085
+ } = props;
32086
+ const {
32087
+ type
32088
+ } = state;
32089
+ switch (type) {
32090
+ case "point":
32091
+ if (state.numPoints === "unlimited") {
32092
+ return renderPointGraphControls({
32093
+ state,
32094
+ dispatch
32095
+ });
32096
+ }
32097
+ return null;
32098
+ default:
32099
+ return null;
32100
+ }
31946
32101
  };
31947
32102
  const renderGraph = props => {
31948
32103
  const {
@@ -32053,7 +32208,8 @@ function initializeGraphState(params) {
32053
32208
  case "point":
32054
32209
  return _extends({}, shared, {
32055
32210
  type: graph.type,
32056
- coords: getPointCoords(graph, range, step)
32211
+ coords: getPointCoords(graph, range, step),
32212
+ numPoints: graph.numPoints || 0
32057
32213
  });
32058
32214
  case "circle":
32059
32215
  return _extends({}, shared, {
@@ -32307,6 +32463,8 @@ function interactiveGraphReducer(state, action) {
32307
32463
  return doChangeSnapStep(state, action);
32308
32464
  case CHANGE_RANGE:
32309
32465
  return doChangeRange(state, action);
32466
+ case ADD_POINT:
32467
+ return doAddPoint(state, action);
32310
32468
  default:
32311
32469
  throw new UnreachableCaseError(action);
32312
32470
  }
@@ -32632,6 +32790,28 @@ function doChangeRange(state, action) {
32632
32790
  range: action.range
32633
32791
  });
32634
32792
  }
32793
+ function doAddPoint(state, action) {
32794
+ if (state.type !== "point") {
32795
+ return state;
32796
+ }
32797
+ const {
32798
+ snapStep
32799
+ } = state;
32800
+ const snappedPoint = snap(snapStep, action.location);
32801
+
32802
+ // Check if there's already a point in that spot
32803
+ for (const point of state.coords) {
32804
+ if (point[X] === snappedPoint[X] && point[Y] === snappedPoint[Y]) {
32805
+ return state;
32806
+ }
32807
+ }
32808
+
32809
+ // If there's no point in spot where we want the new point to go we add it there
32810
+ return _extends({}, state, {
32811
+ hasBeenInteractedWith: true,
32812
+ coords: [...state.coords, snappedPoint]
32813
+ });
32814
+ }
32635
32815
  const getDeltaVertex = (maxMoves, minMoves, delta) => {
32636
32816
  const [deltaX, deltaY] = delta;
32637
32817
  const maxXMove = Math.min(...maxMoves.map(move => move[X]));
@@ -34344,17 +34524,18 @@ class InteractiveGraph extends React.Component {
34344
34524
  // Mafs shim
34345
34525
  const mafsFlags = (_this$props$apiOption = this.props.apiOptions) == null || (_this$props$apiOption = _this$props$apiOption.flags) == null ? void 0 : _this$props$apiOption["mafs"];
34346
34526
  if (shouldUseMafs(mafsFlags, this.props.graph)) {
34347
- var _this$props$apiOption2;
34527
+ var _this$props$apiOption2, _this$props$apiOption3;
34348
34528
  const box = getInteractiveBoxFromSizeClass(this.props.containerSizeClass);
34349
34529
  const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, box[0]);
34350
34530
  const snapStep = this.props.snapStep || Util.snapStepFromGridStep(gridStep);
34351
34531
  return /*#__PURE__*/React.createElement(StatefulMafsGraph, _extends({}, this.props, {
34532
+ showLabelsFlag: (_this$props$apiOption2 = this.props.apiOptions) == null || (_this$props$apiOption2 = _this$props$apiOption2.flags) == null || (_this$props$apiOption2 = _this$props$apiOption2["mafs"]) == null ? void 0 : _this$props$apiOption2["interactive-graph-locked-features-labels"],
34352
34533
  ref: this.mafsRef,
34353
34534
  gridStep: gridStep,
34354
34535
  snapStep: snapStep,
34355
34536
  box: box,
34356
34537
  showTooltips: !!this.props.showTooltips,
34357
- readOnly: (_this$props$apiOption2 = this.props.apiOptions) == null ? void 0 : _this$props$apiOption2.readOnly
34538
+ readOnly: (_this$props$apiOption3 = this.props.apiOptions) == null ? void 0 : _this$props$apiOption3.readOnly
34358
34539
  }));
34359
34540
  }
34360
34541
  return /*#__PURE__*/React.createElement(LegacyInteractiveGraph, _extends({
@@ -34925,10 +35106,7 @@ function shouldUseMafs(mafsFlags, graph) {
34925
35106
  switch (graph.type) {
34926
35107
  case "point":
34927
35108
  if (graph.numPoints === UNLIMITED) {
34928
- // TODO(benchristel): add a feature flag for the "unlimited"
34929
- // case once we've implemented point graphs with unlimited
34930
- // points
34931
- return false;
35109
+ return Boolean(mafsFlags["unlimited-point"]);
34932
35110
  }
34933
35111
  return Boolean(mafsFlags["point"]);
34934
35112
  case "polygon":
@@ -35979,7 +36157,7 @@ const HideAnswersToggle = props => {
35979
36157
  strings
35980
36158
  } = usePerseusI18n();
35981
36159
  return /*#__PURE__*/React.createElement(View, {
35982
- style: styles$g.switchWrapper
36160
+ style: styles$h.switchWrapper
35983
36161
  }, /*#__PURE__*/React.createElement(Switch, {
35984
36162
  id: switchId,
35985
36163
  checked: props.areAnswersHidden,
@@ -35991,7 +36169,7 @@ const HideAnswersToggle = props => {
35991
36169
  tag: "label"
35992
36170
  }, strings.hideAnswersToggleLabel));
35993
36171
  };
35994
- const styles$g = StyleSheet.create({
36172
+ const styles$h = StyleSheet.create({
35995
36173
  switchWrapper: {
35996
36174
  display: "flex",
35997
36175
  flexDirection: "row",
@@ -36047,14 +36225,14 @@ const AnswerPill = props => {
36047
36225
  id: idFactory.get("perseus-label-image-widget-answer-pill"),
36048
36226
  onClick: correct ? undefined : onClick,
36049
36227
  ref: ref,
36050
- style: [style, popperStyle, styles$f.pill, correct && styles$f.correct, incorrect && styles$f.incorrect, (focused || hovered) && BringToFront]
36228
+ style: [style, popperStyle, styles$g.pill, correct && styles$g.correct, incorrect && styles$g.incorrect, (focused || hovered) && BringToFront]
36051
36229
  }, /*#__PURE__*/React.createElement(Renderer, {
36052
36230
  content: answerString,
36053
36231
  strings: strings,
36054
36232
  inline: true
36055
36233
  })));
36056
36234
  };
36057
- const styles$f = StyleSheet.create({
36235
+ const styles$g = StyleSheet.create({
36058
36236
  correct: {
36059
36237
  // WB green darkened by 18%
36060
36238
  backgroundColor: "#00880b"
@@ -36118,23 +36296,23 @@ class Marker extends React.Component {
36118
36296
  icon: iconNull
36119
36297
  };
36120
36298
  if (showCorrectness) {
36121
- iconStyles = [styles$e.markerGraded, showCorrectness === "correct" ? styles$e.markerCorrect : styles$e.markerIncorrect, isOpen && styles$e.markerSelected];
36299
+ iconStyles = [styles$f.markerGraded, showCorrectness === "correct" ? styles$f.markerCorrect : styles$f.markerIncorrect, isOpen && styles$f.markerSelected];
36122
36300
  args = _extends({}, args, {
36123
36301
  icon: showCorrectness === "correct" ? iconCheck : iconMinus
36124
36302
  });
36125
36303
  } else if (selectedAnswers && selectedAnswers.length > 0) {
36126
- iconStyles = [styles$e.markerFilled, isOpen && styles$e.markerSelected];
36304
+ iconStyles = [styles$f.markerFilled, isOpen && styles$f.markerSelected];
36127
36305
  } else if (isOpen) {
36128
- iconStyles = [styles$e.markerSelected];
36306
+ iconStyles = [styles$f.markerSelected];
36129
36307
  args = _extends({}, args, {
36130
36308
  icon: iconChevronDown,
36131
36309
  size: 8
36132
36310
  });
36133
36311
  } else if (showPulsate) {
36134
- iconStyles = [styles$e.markerPulsateBase, this._mounted && shouldReduceMotion() ? showPulsate && styles$e.markerUnfilledPulsateOnce : showPulsate && styles$e.markerUnfilledPulsateInfinite];
36312
+ iconStyles = [styles$f.markerPulsateBase, this._mounted && shouldReduceMotion() ? showPulsate && styles$f.markerUnfilledPulsateOnce : showPulsate && styles$f.markerUnfilledPulsateInfinite];
36135
36313
  }
36136
36314
  return /*#__PURE__*/React.createElement(View, {
36137
- style: [styles$e.markerIcon, iconStyles],
36315
+ style: [styles$f.markerIcon, iconStyles],
36138
36316
  ref: node => this._icon = node
36139
36317
  }, /*#__PURE__*/React.createElement(Icon, args));
36140
36318
  }
@@ -36156,7 +36334,7 @@ class Marker extends React.Component {
36156
36334
  const markerDisabled = showCorrectness === "correct";
36157
36335
  const active = hovered || focused;
36158
36336
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(View, {
36159
- style: [styles$e.marker, active && !markerDisabled && styles$e.markerActive],
36337
+ style: [styles$f.marker, active && !markerDisabled && styles$f.markerActive],
36160
36338
  "aria-label": markerDisabled ? this.context.strings.correctExcited : label
36161
36339
  }, this.renderIcon()), !!selected && showAnswer && /*#__PURE__*/React.createElement(AnswerPill, {
36162
36340
  selectedAnswers: selected,
@@ -36173,7 +36351,7 @@ Marker.contextType = PerseusI18nContext;
36173
36351
  Marker.defaultProps = {
36174
36352
  selected: []
36175
36353
  };
36176
- const styles$e = StyleSheet.create({
36354
+ const styles$f = StyleSheet.create({
36177
36355
  marker: {
36178
36356
  position: "absolute",
36179
36357
  backgroundColor: color.white,
@@ -36755,13 +36933,13 @@ class LabelImage extends React.Component {
36755
36933
  const promptString = isMobile ? multipleAnswers ? strings.tapMultiple : strings.tapSingle : multipleAnswers ? strings.clickMultiple : strings.clickSingle;
36756
36934
  const choicesString = strings.choices;
36757
36935
  return /*#__PURE__*/React.createElement("div", {
36758
- className: classNames("perseus-label-image-widget-instructions", css(styles$d.instructions))
36936
+ className: classNames("perseus-label-image-widget-instructions", css(styles$e.instructions))
36759
36937
  }, /*#__PURE__*/React.createElement("div", {
36760
- className: css(styles$d.instructionsCaption)
36938
+ className: css(styles$e.instructionsCaption)
36761
36939
  }, promptString, " ", !hideChoices && choicesString), !hideChoices && /*#__PURE__*/React.createElement("div", {
36762
- className: css(styles$d.instructionsChoices)
36940
+ className: css(styles$e.instructionsChoices)
36763
36941
  }, choices.map((choice, index) => /*#__PURE__*/React.createElement("div", {
36764
- className: css(styles$d.instructionsChoice),
36942
+ className: css(styles$e.instructionsChoice),
36765
36943
  key: index
36766
36944
  }, /*#__PURE__*/React.createElement(Renderer, {
36767
36945
  content: choice,
@@ -36779,20 +36957,20 @@ class LabelImage extends React.Component {
36779
36957
  activeMarkerIndex
36780
36958
  } = this.state;
36781
36959
  return /*#__PURE__*/React.createElement("div", null, this.renderInstructions(), /*#__PURE__*/React.createElement("div", {
36782
- className: css(styles$d.markersCanvas),
36960
+ className: css(styles$e.markersCanvas),
36783
36961
  style: {
36784
36962
  maxWidth: imageWidth,
36785
36963
  maxHeight: imageHeight
36786
36964
  }
36787
36965
  }, /*#__PURE__*/React.createElement("div", {
36788
- className: css(styles$d.imageContainer,
36966
+ className: css(styles$e.imageContainer,
36789
36967
  // Ensure image interaction is disabled while answer
36790
36968
  // choices popup is visible. This addresses specific
36791
36969
  // case where user may click on question image to
36792
36970
  // dismiss the popup. If the image is larger in size
36793
36971
  // than its rendered in the widget, this would
36794
36972
  // result in a zoom of the image.
36795
- activeMarkerIndex !== -1 && styles$d.imageInteractionDisabled)
36973
+ activeMarkerIndex !== -1 && styles$e.imageInteractionDisabled)
36796
36974
  }, /*#__PURE__*/React.createElement(context$1.Consumer, null, ({
36797
36975
  setAssetStatus
36798
36976
  }) => /*#__PURE__*/React.createElement(SvgImage, {
@@ -36817,7 +36995,7 @@ class LabelImage extends React.Component {
36817
36995
  }
36818
36996
  }
36819
36997
  LabelImage.contextType = PerseusI18nContext;
36820
- const styles$d = StyleSheet.create({
36998
+ const styles$e = StyleSheet.create({
36821
36999
  instructions: {
36822
37000
  paddingBottom: 16
36823
37001
  },
@@ -36914,7 +37092,7 @@ class Placeholder extends React.Component {
36914
37092
  const {
36915
37093
  layout
36916
37094
  } = this.props;
36917
- const className = css(styles$c.card, styles$c.placeholder, layout === Layout.HORIZONTAL && styles$c.horizontalCard);
37095
+ const className = css(styles$d.card, styles$d.placeholder, layout === Layout.HORIZONTAL && styles$d.horizontalCard);
36918
37096
  const style = {
36919
37097
  width: this.props.width,
36920
37098
  height: this.props.height
@@ -37097,7 +37275,7 @@ class Draggable extends React.Component {
37097
37275
  // We need to keep backwards compatbility with rules specified directly
37098
37276
  // in CSS. Hence the hacky tacking on of manual classNames.
37099
37277
  // See sortable.less for details.
37100
- let className = css(styles$c.card, styles$c.draggable, layout === Layout.HORIZONTAL && styles$c.horizontalCard, layout === Layout.VERTICAL && styles$c.verticalCard, type === ItemState.DRAGGING && styles$c.dragging, type === ItemState.DISABLED && styles$c.disabled, !includePadding && styles$c.unpaddedCard) + " " + ClassNames.INTERACTIVE + " perseus-sortable-draggable";
37278
+ let className = css(styles$d.card, styles$d.draggable, layout === Layout.HORIZONTAL && styles$d.horizontalCard, layout === Layout.VERTICAL && styles$d.verticalCard, type === ItemState.DRAGGING && styles$d.dragging, type === ItemState.DISABLED && styles$d.disabled, !includePadding && styles$d.unpaddedCard) + " " + ClassNames.INTERACTIVE + " perseus-sortable-draggable";
37101
37279
  if (!includePadding) {
37102
37280
  className += " perseus-sortable-draggable-unpadded";
37103
37281
  }
@@ -37306,7 +37484,7 @@ class Sortable extends React.Component {
37306
37484
  } = this.props;
37307
37485
  // We need to keep backwards compatbility with rules specified directly
37308
37486
  // in CSS. See sortable.less for details.
37309
- const className = css(styles$c.sortable) + " perseus-sortable";
37487
+ const className = css(styles$d.sortable) + " perseus-sortable";
37310
37488
  const syncWidth = ((_this$props$constrain = this.props.constraints) == null ? void 0 : _this$props$constrain.width) || layout === Layout.VERTICAL;
37311
37489
  const syncHeight = ((_this$props$constrain2 = this.props.constraints) == null ? void 0 : _this$props$constrain2.height) || layout === Layout.HORIZONTAL;
37312
37490
  _.each(this.state.items, function (item, i, items) {
@@ -37548,7 +37726,7 @@ Sortable.defaultProps = {
37548
37726
  linterContext: PerseusLinter.linterContextDefault,
37549
37727
  waitForTexRendererToLoad: true
37550
37728
  };
37551
- const styles$c = StyleSheet.create({
37729
+ const styles$d = StyleSheet.create({
37552
37730
  sortable: {
37553
37731
  boxSizing: "border-box",
37554
37732
  float: "left",
@@ -37710,25 +37888,25 @@ class Matcher extends React.Component {
37710
37888
  };
37711
37889
  const cellMarginPx = this.props.apiOptions.isMobile ? 8 : 5;
37712
37890
  return /*#__PURE__*/React.createElement("table", {
37713
- className: css(styles$b.widget) + " " + HACKY_CSS_CLASSNAME
37891
+ className: css(styles$c.widget) + " " + HACKY_CSS_CLASSNAME
37714
37892
  }, /*#__PURE__*/React.createElement("tbody", null, showLabels && /*#__PURE__*/React.createElement("tr", {
37715
- className: css(styles$b.row)
37893
+ className: css(styles$c.row)
37716
37894
  }, /*#__PURE__*/React.createElement("th", {
37717
- className: css(styles$b.column, styles$b.columnLabel)
37895
+ className: css(styles$c.column, styles$c.columnLabel)
37718
37896
  }, /*#__PURE__*/React.createElement(Renderer, {
37719
37897
  content: this.props.labels[0] || "...",
37720
37898
  linterContext: this.props.linterContext,
37721
37899
  strings: this.context.strings
37722
37900
  })), /*#__PURE__*/React.createElement("th", {
37723
- className: css(styles$b.column, styles$b.columnRight, styles$b.columnLabel)
37901
+ className: css(styles$c.column, styles$c.columnRight, styles$c.columnLabel)
37724
37902
  }, /*#__PURE__*/React.createElement(Renderer, {
37725
37903
  content: this.props.labels[1] || "...",
37726
37904
  linterContext: this.props.linterContext,
37727
37905
  strings: this.context.strings
37728
37906
  }))), /*#__PURE__*/React.createElement("tr", {
37729
- className: css(styles$b.row)
37907
+ className: css(styles$c.row)
37730
37908
  }, /*#__PURE__*/React.createElement("td", {
37731
- className: css(styles$b.column)
37909
+ className: css(styles$c.column)
37732
37910
  }, /*#__PURE__*/React.createElement(Sortable, {
37733
37911
  options: left,
37734
37912
  layout: Layout.VERTICAL,
@@ -37743,7 +37921,7 @@ class Matcher extends React.Component {
37743
37921
  ,
37744
37922
  ref: "left"
37745
37923
  })), /*#__PURE__*/React.createElement("td", {
37746
- className: css(styles$b.column, styles$b.columnRight)
37924
+ className: css(styles$c.column, styles$c.columnRight)
37747
37925
  }, /*#__PURE__*/React.createElement(Sortable
37748
37926
  // @ts-expect-error - TS2322 - Type 'readonly unknown[]' is not assignable to type 'readonly string[]'.
37749
37927
  , {
@@ -37797,7 +37975,7 @@ _.extend(Matcher, {
37797
37975
  });
37798
37976
  const padding = 5;
37799
37977
  const border = "1px solid #444";
37800
- const styles$b = StyleSheet.create({
37978
+ const styles$c = StyleSheet.create({
37801
37979
  widget: {
37802
37980
  paddingTop: padding,
37803
37981
  maxWidth: "100%",
@@ -38473,9 +38651,9 @@ function drawText(ctx, item) {
38473
38651
  if (item.value === null) {
38474
38652
  return;
38475
38653
  }
38476
- ctx.fillStyle = styles$a.fgColor;
38477
- ctx.font = styles$a.font;
38478
- ctx.fillText(item.value, item.pos[0] - styles$a.fontSizePx / 2 + 1, item.pos[1] + styles$a.fontSizePx / 2);
38654
+ ctx.fillStyle = styles$b.fgColor;
38655
+ ctx.font = styles$b.font;
38656
+ ctx.fillText(item.value, item.pos[0] - styles$b.fontSizePx / 2 + 1, item.pos[1] + styles$b.fontSizePx / 2);
38479
38657
  }
38480
38658
 
38481
38659
  /**
@@ -38496,16 +38674,16 @@ function drawText(ctx, item) {
38496
38674
  function drawDoubleLine(ctx, item) {
38497
38675
  // Outer line that forms both of the bond lines.
38498
38676
  let path = new Path2D();
38499
- ctx.lineWidth = 5 * styles$a.lineWidth;
38500
- ctx.strokeStyle = styles$a.fgColor;
38677
+ ctx.lineWidth = 5 * styles$b.lineWidth;
38678
+ ctx.strokeStyle = styles$b.fgColor;
38501
38679
  path.moveTo(item.startPos[0], item.startPos[1]);
38502
38680
  path.lineTo(item.endPos[0], item.endPos[1]);
38503
38681
  ctx.stroke(path);
38504
38682
 
38505
38683
  // Inner white line that separates the two bond lines.
38506
38684
  path = new Path2D();
38507
- ctx.lineWidth = 3 * styles$a.lineWidth;
38508
- ctx.strokeStyle = styles$a.bgColor;
38685
+ ctx.lineWidth = 3 * styles$b.lineWidth;
38686
+ ctx.strokeStyle = styles$b.bgColor;
38509
38687
  path.moveTo(item.startPos[0], item.startPos[1]);
38510
38688
  path.lineTo(item.endPos[0], item.endPos[1]);
38511
38689
  ctx.stroke(path);
@@ -38532,16 +38710,16 @@ function drawTripleLine(ctx, item) {
38532
38710
 
38533
38711
  // Outer line that will form the two outer bond lines.
38534
38712
  let path = new Path2D();
38535
- ctx.lineWidth = 7 * styles$a.lineWidth;
38536
- ctx.strokeStyle = styles$a.fgColor;
38713
+ ctx.lineWidth = 7 * styles$b.lineWidth;
38714
+ ctx.strokeStyle = styles$b.fgColor;
38537
38715
  path.moveTo(item.startPos[0], item.startPos[1]);
38538
38716
  path.lineTo(item.endPos[0], item.endPos[1]);
38539
38717
  ctx.stroke(path);
38540
38718
 
38541
38719
  // Middle white line that separates the bonds
38542
38720
  path = new Path2D();
38543
- ctx.lineWidth = 5 * styles$a.lineWidth;
38544
- ctx.strokeStyle = styles$a.bgColor;
38721
+ ctx.lineWidth = 5 * styles$b.lineWidth;
38722
+ ctx.strokeStyle = styles$b.bgColor;
38545
38723
  path.moveTo(item.startPos[0], item.startPos[1]);
38546
38724
  path.lineTo(item.endPos[0], item.endPos[1]);
38547
38725
  ctx.stroke(path);
@@ -38563,8 +38741,8 @@ function drawTripleLine(ctx, item) {
38563
38741
  */
38564
38742
  function drawLine(ctx, item) {
38565
38743
  const path = new Path2D();
38566
- ctx.lineWidth = styles$a.lineWidth;
38567
- ctx.strokeStyle = styles$a.fgColor;
38744
+ ctx.lineWidth = styles$b.lineWidth;
38745
+ ctx.strokeStyle = styles$b.fgColor;
38568
38746
  path.moveTo(item.startPos[0], item.startPos[1]);
38569
38747
  path.lineTo(item.endPos[0], item.endPos[1]);
38570
38748
  ctx.stroke(path);
@@ -38616,7 +38794,7 @@ function compareElements(item0, item1) {
38616
38794
  function draw(ctx, items) {
38617
38795
  items.sort(compareElements).forEach(drawItem(ctx));
38618
38796
  }
38619
- const styles$a = {
38797
+ const styles$b = {
38620
38798
  bgColor: "rgb(255, 255, 255)",
38621
38799
  fgColor: "rgb(0, 0, 0)",
38622
38800
  fontSizePx: 12,
@@ -41175,7 +41353,7 @@ class HighlightRenderer extends React.PureComponent {
41175
41353
  const rects = this.state.cachedHighlightRects;
41176
41354
  return /*#__PURE__*/React.createElement("div", null, rects.map((rect, index) => /*#__PURE__*/React.createElement("div", {
41177
41355
  key: index,
41178
- className: css(styles$9.highlightRect),
41356
+ className: css(styles$a.highlightRect),
41179
41357
  style: {
41180
41358
  // NOTE(mdr): We apply `position: absolute` here
41181
41359
  // rather than in Aphrodite styles, because
@@ -41194,7 +41372,7 @@ class HighlightRenderer extends React.PureComponent {
41194
41372
  })));
41195
41373
  }
41196
41374
  }
41197
- const styles$9 = StyleSheet.create({
41375
+ const styles$a = StyleSheet.create({
41198
41376
  highlightRect: {
41199
41377
  background: "#fffabe" // highlighter yellow :)
41200
41378
  }
@@ -41275,7 +41453,7 @@ class HighlightTooltip extends React.PureComponent {
41275
41453
  // TooltipContent wouldn't let me overwrite
41276
41454
  // user-select and onClick
41277
41455
  const content = /*#__PURE__*/React.createElement("div", {
41278
- className: css(styles$8.tooltipLabel),
41456
+ className: css(styles$9.tooltipLabel),
41279
41457
  onClick: this.props.onClick
41280
41458
  }, this.props.label);
41281
41459
  const style = {
@@ -41295,7 +41473,7 @@ class HighlightTooltip extends React.PureComponent {
41295
41473
  }, /*#__PURE__*/React.createElement("div", null)));
41296
41474
  }
41297
41475
  }
41298
- const styles$8 = StyleSheet.create({
41476
+ const styles$9 = StyleSheet.create({
41299
41477
  tooltipLabel: {
41300
41478
  // Unlike most tooltip contents, we don't want this label to be
41301
41479
  // selectable. Selecting it is not only unhelpful, but dangerous: if
@@ -41849,7 +42027,7 @@ class HighlightableContent extends React.PureComponent {
41849
42027
  // starting point: https://phabricator.khanacademy.org/D35623?id=170698
41850
42028
  const buildHighlight = r => this._buildHighlight(r);
41851
42029
  return /*#__PURE__*/React.createElement("div", {
41852
- className: css(styles$7.container),
42030
+ className: css(styles$8.container),
41853
42031
  ref: container => this._container = container
41854
42032
  }, /*#__PURE__*/React.createElement("div", null, this.props.enabled && this._container && this._content && /*#__PURE__*/React.createElement(HighlightingUI, {
41855
42033
  buildHighlight: buildHighlight,
@@ -41870,14 +42048,14 @@ class HighlightableContent extends React.PureComponent {
41870
42048
  onAddHighlight: this._handleAddHighlight,
41871
42049
  onRemoveHighlight: this._handleRemoveHighlight
41872
42050
  })), /*#__PURE__*/React.createElement("div", {
41873
- className: css(styles$7.content),
42051
+ className: css(styles$8.content),
41874
42052
  ref: content => this._content = content
41875
42053
  }, /*#__PURE__*/React.createElement(WordIndexer, {
41876
42054
  onWordsUpdate: this._handleWordsUpdate
41877
42055
  }, this.props.children)));
41878
42056
  }
41879
42057
  }
41880
- const styles$7 = StyleSheet.create({
42058
+ const styles$8 = StyleSheet.create({
41881
42059
  // We render content with z-index 1, and highlights with z-index 0, so that
41882
42060
  // content will receive the browser's native pointer events. (Highlights
41883
42061
  // have their own magic code to manage hover detection, despite being
@@ -42289,7 +42467,7 @@ class LineHeightMeasurer extends React.Component {
42289
42467
  }
42290
42468
  render() {
42291
42469
  return /*#__PURE__*/React.createElement("div", {
42292
- className: css(styles$6.measurer),
42470
+ className: css(styles$7.measurer),
42293
42471
  "aria-hidden": "true"
42294
42472
  }, /*#__PURE__*/React.createElement("div", {
42295
42473
  ref: ref => this._line = ref,
@@ -42297,7 +42475,7 @@ class LineHeightMeasurer extends React.Component {
42297
42475
  }, "Line Height Measurement"));
42298
42476
  }
42299
42477
  }
42300
- const styles$6 = StyleSheet.create({
42478
+ const styles$7 = StyleSheet.create({
42301
42479
  measurer: {
42302
42480
  position: "absolute",
42303
42481
  top: 0,
@@ -42772,6 +42950,207 @@ var PassageRefTarget$1 = {
42772
42950
  isLintable: true
42773
42951
  };
42774
42952
 
42953
+ /* eslint-disable react/forbid-prop-types, react/sort-comp */
42954
+
42955
+ // For returning user input, but currently the PhET widget
42956
+ // does not support accessing user input
42957
+ // This renders the PhET sim
42958
+ class PhetSimulation extends React.Component {
42959
+ constructor(props) {
42960
+ super(props);
42961
+ this.iframeRef = /*#__PURE__*/React.createRef();
42962
+ this.locale = void 0;
42963
+ this.state = {
42964
+ url: null,
42965
+ banner: null
42966
+ };
42967
+ this.change = (...args) => {
42968
+ // @ts-expect-error - TS2345 - Argument of type 'readonly unknown[]' is not assignable to parameter of type 'any[]'.
42969
+ return change.apply(this, args);
42970
+ };
42971
+ this.simpleValidate = rubric => {
42972
+ // @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof PhetSimulation'.
42973
+ return PhetSimulation.validate(this.getUserInput(), rubric);
42974
+ };
42975
+ // kaLocales and PhET locales use different formats and abbreviations.
42976
+ // PhET accepts different formats, i.e. kaLocale's hyphens, but it does not accept
42977
+ // different abbreviations, so in points of divergence of abbreviations, we need to
42978
+ // convert kaLocale abbreviations into abbreviations recognized by PhET.
42979
+ this.getPhetCompatibleLocale = kaLocale => {
42980
+ switch (kaLocale) {
42981
+ case "pt-pt":
42982
+ return "pt";
42983
+ case "zh-hans":
42984
+ return "zh_CN";
42985
+ case "zh-hant":
42986
+ return "zh_TW";
42987
+ case "fa-af":
42988
+ return "fa_DA";
42989
+ default:
42990
+ return kaLocale;
42991
+ }
42992
+ };
42993
+ this.displayLoadFailure = () => {
42994
+ this.setState({
42995
+ url: null,
42996
+ banner: {
42997
+ message: this.context.strings.simulationLoadFail,
42998
+ kind: "critical"
42999
+ }
43000
+ });
43001
+ };
43002
+ this.locale = this.getPhetCompatibleLocale(getDependencies().kaLocale);
43003
+ }
43004
+ getUserInput() {
43005
+ return null;
43006
+ }
43007
+ async componentDidMount() {
43008
+ await this.updateSimState(this.props.url);
43009
+ }
43010
+ async componentDidUpdate(prevProps) {
43011
+ // If the URL has changed, update our state
43012
+ if (prevProps.url !== this.props.url) {
43013
+ await this.updateSimState(this.props.url);
43014
+ }
43015
+ }
43016
+ render() {
43017
+ var _this$state$url;
43018
+ // We sandbox the iframe so that we allowlist only the functionality
43019
+ // that we need. This makes it safer to present third-party content
43020
+ // from the PhET website.
43021
+ // http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
43022
+ const sandboxProperties = "allow-same-origin allow-scripts";
43023
+ return /*#__PURE__*/React.createElement(View, {
43024
+ style: styles$6.container
43025
+ }, this.state.banner !== null &&
43026
+ /*#__PURE__*/
43027
+ // TODO(anna): Make this banner focusable
43028
+ React.createElement(View, {
43029
+ style: {
43030
+ marginBottom: phoneMargin
43031
+ }
43032
+ }, /*#__PURE__*/React.createElement(Banner, {
43033
+ layout: "floating",
43034
+ kind: this.state.banner.kind,
43035
+ text: this.state.banner.message
43036
+ })), /*#__PURE__*/React.createElement("iframe", {
43037
+ ref: this.iframeRef,
43038
+ title: this.props.description,
43039
+ sandbox: sandboxProperties,
43040
+ style: {
43041
+ minWidth: 400,
43042
+ height: 360,
43043
+ width: "100%",
43044
+ borderWidth: 0
43045
+ },
43046
+ src: (_this$state$url = this.state.url) == null ? void 0 : _this$state$url.toString(),
43047
+ allow: "fullscreen"
43048
+ }), /*#__PURE__*/React.createElement(IconButton, {
43049
+ icon: cornersOutIcon,
43050
+ onClick: () => {
43051
+ var _this$iframeRef$curre;
43052
+ (_this$iframeRef$curre = this.iframeRef.current) == null || _this$iframeRef$curre.requestFullscreen();
43053
+ },
43054
+ kind: "secondary",
43055
+ "aria-label": "Fullscreen",
43056
+ style: {
43057
+ marginTop: 5,
43058
+ marginBottom: 5,
43059
+ alignSelf: "flex-end"
43060
+ },
43061
+ disabled: this.state.url === null
43062
+ }));
43063
+ }
43064
+ async updateSimState(urlString) {
43065
+ const url = makeSafeUrl(urlString, this.locale);
43066
+ if (url === null) {
43067
+ this.displayLoadFailure();
43068
+ return;
43069
+ }
43070
+ const response = await fetch(url);
43071
+ if (!response.ok) {
43072
+ this.displayLoadFailure();
43073
+ return;
43074
+ }
43075
+ const showLocaleWarning = await this.showLocaleWarning(url);
43076
+ this.setState({
43077
+ url: url,
43078
+ banner: showLocaleWarning ? {
43079
+ message: this.context.strings.simulationLocaleWarning,
43080
+ kind: "warning"
43081
+ } : null
43082
+ });
43083
+ }
43084
+ async showLocaleWarning(url) {
43085
+ // Do not show a locale warning on an invalid URL
43086
+ if (!url) {
43087
+ return false;
43088
+ }
43089
+ // Grab the simulation name
43090
+ const phetRegex = /https:\/\/phet\.colorado\.edu\/sims\/html\/([a-zA-Z0-9-]+)\/.*/g;
43091
+ const match = phetRegex.exec(url.toString());
43092
+ // Do not show a locale warning on a non-simulation URL
43093
+ if (match === null) {
43094
+ return false;
43095
+ }
43096
+ const simName = match[1];
43097
+ const response = await fetch(`https://phet.colorado.edu/sims/html/${simName}/latest/string-map.json`);
43098
+ if (!response.ok) {
43099
+ return false;
43100
+ }
43101
+ let responseJson;
43102
+ try {
43103
+ responseJson = await response.json();
43104
+ } catch (_unused) {
43105
+ // If the file exists but there is no content to parse into a JSON,
43106
+ // response.json() will throw an error that we want to catch.
43107
+ return false;
43108
+ }
43109
+ const locales = Object.keys(responseJson);
43110
+
43111
+ // Only display a locale warning if there is no fallback language
43112
+ const baseLocale = this.locale.split("_")[0];
43113
+ for (const l of locales) {
43114
+ if (baseLocale === l.split("_")[0]) {
43115
+ return false;
43116
+ }
43117
+ }
43118
+ return true;
43119
+ }
43120
+ }
43121
+
43122
+ // Setting URL to null will display an error message in the iframe
43123
+ PhetSimulation.contextType = PerseusI18nContext;
43124
+ const makeSafeUrl = (urlString, locale) => {
43125
+ if (!URL.canParse(urlString)) {
43126
+ return null;
43127
+ }
43128
+ const url = new URL(urlString);
43129
+ if (url.origin !== "https://phet.colorado.edu") {
43130
+ return null;
43131
+ }
43132
+ url.searchParams.set("locale", locale);
43133
+ return url;
43134
+ };
43135
+ var PhetSimulation$1 = {
43136
+ name: "phet-simulation",
43137
+ displayName: "PhET Simulation",
43138
+ widget: PhetSimulation,
43139
+ // Hides widget from content creators until full release
43140
+ hidden: true,
43141
+ isLintable: true
43142
+ };
43143
+ const styles$6 = StyleSheet.create({
43144
+ container: {
43145
+ borderRadius: borderRadiusLarge,
43146
+ borderWidth: 1,
43147
+ borderColor: basicBorderColor,
43148
+ padding: phoneMargin,
43149
+ paddingBottom: 0,
43150
+ width: 650
43151
+ }
43152
+ });
43153
+
42775
43154
  /* eslint-disable react/no-unsafe */
42776
43155
  const {
42777
43156
  deepEq
@@ -44236,7 +44615,7 @@ var Video$1 = {
44236
44615
  };
44237
44616
 
44238
44617
  /* eslint-disable import/no-named-as-default */
44239
- var extraWidgets = [Categorizer$1, CSProgram$1, Dropdown$1, Explanation$1, Definition$1, Grapher$1, GradedGroup$1, GradedGroupSet$1, Group$1, Iframe$1, Image$1, Interactive, InteractiveGraph$1, LabelImage$1, Matrix$1, Matcher$1, Measurer$1, Molecule$1, NumberLine$1, Orderer$1, Passage$1, PassageRef$1, PassageRefTarget$1, Plotter$1, PythonProgram$1, Sorter$1, Table$1, Video$1, DeprecatedStandin$1];
44618
+ var extraWidgets = [Categorizer$1, CSProgram$1, Dropdown$1, Explanation$1, Definition$1, Grapher$1, GradedGroup$1, GradedGroupSet$1, Group$1, Iframe$1, Image$1, Interactive, InteractiveGraph$1, LabelImage$1, Matrix$1, Matcher$1, Measurer$1, Molecule$1, NumberLine$1, Orderer$1, Passage$1, PassageRef$1, PassageRefTarget$1, PhetSimulation$1, Plotter$1, PythonProgram$1, Sorter$1, Table$1, Video$1, DeprecatedStandin$1];
44240
44619
 
44241
44620
  /**
44242
44621
  * This should be called by all clients, specifying whether extra widgets are
@@ -44276,7 +44655,7 @@ const init = function init(options) {
44276
44655
 
44277
44656
  // This file is processed by a Rollup plugin (replace) to inject the production
44278
44657
  const libName = "@khanacademy/perseus";
44279
- const libVersion = "30.0.1";
44658
+ const libVersion = "31.0.1";
44280
44659
  addLibraryVersionToPerseusDebug(libName, libVersion);
44281
44660
 
44282
44661
  const apiVersion = {
@@ -47836,6 +48215,7 @@ const widgetNameToEnum = {
47836
48215
  passage: "PASSAGE",
47837
48216
  "passage-ref": "PASSAGE_REF",
47838
48217
  "passage-ref-target": "PASSAGE_REF_TARGET",
48218
+ "phet-simulation": "PHET_SIMULATION",
47839
48219
  plotter: "PLOTTER",
47840
48220
  "python-program": "PYTHON_PROGRAM",
47841
48221
  "reaction-diagram": "REACTION_DIAGRAM",