@khanacademy/perseus 46.0.1 → 47.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.
package/dist/es/index.js CHANGED
@@ -508,7 +508,7 @@ class InnerMathInput extends React.Component {
508
508
  className = className + " " + this.props.className;
509
509
  }
510
510
  return /*#__PURE__*/React.createElement(View, {
511
- style: [styles$E.outerWrapper, this.state.focused && styles$E.wrapperFocused, this.props.hasError && styles$E.wrapperError]
511
+ style: [styles$D.outerWrapper, this.state.focused && styles$D.wrapperFocused, this.props.hasError && styles$D.wrapperError]
512
512
  }, /*#__PURE__*/React.createElement("div", {
513
513
  style: {
514
514
  display: "flex",
@@ -546,7 +546,7 @@ class InnerMathInput extends React.Component {
546
546
  style: a11y.srOnly
547
547
  }, this.context.strings.mathInputDescription), /*#__PURE__*/React.createElement(PopoverContentCore, {
548
548
  closeButtonVisible: true,
549
- style: styles$E.popoverContent
549
+ style: styles$D.popoverContent
550
550
  }, /*#__PURE__*/React.createElement(DesktopKeypad, _extends({
551
551
  onAnalyticsEvent: this.props.onAnalyticsEvent,
552
552
  extraKeys: this.props.extraKeys,
@@ -622,9 +622,9 @@ const MathInputIcon = ({
622
622
  fillColor = color.offBlack;
623
623
  break;
624
624
  }
625
- const dynamicClass = active || focused ? styles$E.iconActive : styles$E.iconInactive;
625
+ const dynamicClass = active || focused ? styles$D.iconActive : styles$D.iconInactive;
626
626
  return /*#__PURE__*/React.createElement(View, {
627
- style: [styles$E.iconContainer, dynamicClass]
627
+ style: [styles$D.iconContainer, dynamicClass]
628
628
  }, /*#__PURE__*/React.createElement("svg", {
629
629
  xmlns: "http://www.w3.org/2000/svg",
630
630
  width: "16",
@@ -669,7 +669,7 @@ const inputFocused = {
669
669
  borderColor: color.blue,
670
670
  margin: -1
671
671
  };
672
- const styles$E = StyleSheet.create({
672
+ const styles$D = StyleSheet.create({
673
673
  iconContainer: {
674
674
  display: "flex",
675
675
  justifyContent: "center",
@@ -2469,7 +2469,7 @@ class Expression extends React.Component {
2469
2469
  var _this$props$keypadCon, _this$props$analytics2, _this$props$analytics3;
2470
2470
  if (this.props.apiOptions.customKeypad) {
2471
2471
  return /*#__PURE__*/React.createElement(View, {
2472
- className: css(styles$D.mobileLabelInputWrapper)
2472
+ className: css(styles$C.mobileLabelInputWrapper)
2473
2473
  }, !!this.props.visibleLabel && /*#__PURE__*/React.createElement(LabelSmall, {
2474
2474
  htmlFor: this._textareaId,
2475
2475
  tag: "label"
@@ -2504,7 +2504,7 @@ class Expression extends React.Component {
2504
2504
  ERROR_TITLE
2505
2505
  } = this.context.strings;
2506
2506
  return /*#__PURE__*/React.createElement(View, {
2507
- className: css(styles$D.desktopLabelInputWrapper)
2507
+ className: css(styles$C.desktopLabelInputWrapper)
2508
2508
  }, !!this.props.visibleLabel && /*#__PURE__*/React.createElement(LabelSmall, {
2509
2509
  htmlFor: this._textareaId,
2510
2510
  tag: "label"
@@ -2554,7 +2554,7 @@ Expression.defaultProps = {
2554
2554
  apiOptions: ApiOptions.defaults,
2555
2555
  linterContext: linterContextDefault
2556
2556
  };
2557
- const styles$D = StyleSheet.create({
2557
+ const styles$C = StyleSheet.create({
2558
2558
  mobileLabelInputWrapper: {
2559
2559
  padding: "15px 4px 0"
2560
2560
  },
@@ -17930,33 +17930,33 @@ class Lint extends React.Component {
17930
17930
  let warningText;
17931
17931
  let warningTextStyle;
17932
17932
  if (this.props.severity === Severity.Error) {
17933
- severityStyle = styles$C.indicatorError;
17933
+ severityStyle = styles$B.indicatorError;
17934
17934
  warningText = "Error";
17935
- warningTextStyle = styles$C.publishBlockingError;
17935
+ warningTextStyle = styles$B.publishBlockingError;
17936
17936
  } else if (this.props.severity === Severity.Warning) {
17937
- severityStyle = styles$C.indicatorWarning;
17937
+ severityStyle = styles$B.indicatorWarning;
17938
17938
  warningText = "Warning";
17939
- warningTextStyle = styles$C.warning;
17939
+ warningTextStyle = styles$B.warning;
17940
17940
  } else {
17941
- severityStyle = styles$C.indicatorGuideline;
17941
+ severityStyle = styles$B.indicatorGuideline;
17942
17942
  warningText = "Recommendation";
17943
- warningTextStyle = styles$C.warning;
17943
+ warningTextStyle = styles$B.warning;
17944
17944
  }
17945
17945
  return /*#__PURE__*/React.createElement("a", {
17946
17946
  href: `https://khanacademy.org/r/linter-rules#${this.props.ruleName}`,
17947
17947
  target: "lint-help-window",
17948
17948
  className: css(style)
17949
17949
  }, /*#__PURE__*/React.createElement("span", {
17950
- className: css(styles$C.indicator, severityStyle)
17950
+ className: css(styles$B.indicator, severityStyle)
17951
17951
  }, this.props.severity === 1 && /*#__PURE__*/React.createElement(InlineIcon, exclamationIcon)), /*#__PURE__*/React.createElement("div", {
17952
- className: css(styles$C.tooltip, tooltipAbove && styles$C.tooltipAbove)
17952
+ className: css(styles$B.tooltip, tooltipAbove && styles$B.tooltipAbove)
17953
17953
  }, this.props.message.split("\n\n").map((m, i) => /*#__PURE__*/React.createElement("p", {
17954
17954
  key: i,
17955
- className: css(styles$C.tooltipParagraph)
17955
+ className: css(styles$B.tooltipParagraph)
17956
17956
  }, /*#__PURE__*/React.createElement("span", {
17957
17957
  className: css(warningTextStyle)
17958
17958
  }, warningText, ":", " "), m)), /*#__PURE__*/React.createElement("div", {
17959
- className: css(styles$C.tail, tooltipAbove && styles$C.tailAbove)
17959
+ className: css(styles$B.tail, tooltipAbove && styles$B.tailAbove)
17960
17960
  })));
17961
17961
  };
17962
17962
  }
@@ -17994,20 +17994,20 @@ class Lint extends React.Component {
17994
17994
  }
17995
17995
  if (blockHighlight) {
17996
17996
  return /*#__PURE__*/React.createElement("span", {
17997
- className: css(styles$C.lintContainer, styles$C.lintContainerBlock)
17998
- }, this.renderLink(styles$C.radioWidgetHoverTarget), /*#__PURE__*/React.createElement("span", null, children));
17997
+ className: css(styles$B.lintContainer, styles$B.lintContainerBlock)
17998
+ }, this.renderLink(styles$B.radioWidgetHoverTarget), /*#__PURE__*/React.createElement("span", null, children));
17999
17999
  }
18000
18000
  if (inline) {
18001
18001
  return /*#__PURE__*/React.createElement("span", {
18002
- className: css(styles$C.lintContainer)
18003
- }, this.renderLink(styles$C.inlineHoverTarget), /*#__PURE__*/React.createElement("span", null, children));
18002
+ className: css(styles$B.lintContainer)
18003
+ }, this.renderLink(styles$B.inlineHoverTarget), /*#__PURE__*/React.createElement("span", null, children));
18004
18004
  }
18005
18005
  return /*#__PURE__*/React.createElement("div", {
18006
- className: css(styles$C.lintContainer)
18007
- }, this.renderLink(styles$C.hoverTarget), /*#__PURE__*/React.createElement("div", null, children));
18006
+ className: css(styles$B.lintContainer)
18007
+ }, this.renderLink(styles$B.hoverTarget), /*#__PURE__*/React.createElement("div", null, children));
18008
18008
  }
18009
18009
  }
18010
- const styles$C = StyleSheet.create({
18010
+ const styles$B = StyleSheet.create({
18011
18011
  // This is the class of the outermost element.
18012
18012
  // We use relative positioning so that the lint indicator can be
18013
18013
  // positioned absolutely relative to the position of the linty container.
@@ -18814,8 +18814,6 @@ function getWidgetFromWidgetMap(widgetId, widgetMap) {
18814
18814
 
18815
18815
  /**
18816
18816
  * Get select widgets from a widget map.
18817
- * Useful for multi-items that needs to split
18818
- * one PerseusItem into several
18819
18817
  *
18820
18818
  * @param {ReadonlyArray<string>} widgetIds to extract
18821
18819
  * @param {PerseusWidgetsMap} widgetMap to extract from
@@ -20200,7 +20198,7 @@ class Renderer extends React.Component {
20200
20198
  paddingLeft: margin,
20201
20199
  paddingRight: margin
20202
20200
  };
20203
- const mobileInnerStyle = _extends({}, innerStyle, styles$B.mobileZoomableParentFix);
20201
+ const mobileInnerStyle = _extends({}, innerStyle, styles$A.mobileZoomableParentFix);
20204
20202
  return /*#__PURE__*/React.createElement("div", {
20205
20203
  key: state.key,
20206
20204
  className: "perseus-block-math",
@@ -20324,7 +20322,7 @@ class Renderer extends React.Component {
20324
20322
  paddingLeft: 0,
20325
20323
  paddingRight: 0
20326
20324
  };
20327
- const mobileInnerStyle = _extends({}, innerStyle, styles$B.mobileZoomableParentFix);
20325
+ const mobileInnerStyle = _extends({}, innerStyle, styles$A.mobileZoomableParentFix);
20328
20326
  const wrappedOutput = /*#__PURE__*/React.createElement("div", {
20329
20327
  style: _extends({}, mobileInnerStyle, {
20330
20328
  overflowX: "auto"
@@ -20965,7 +20963,7 @@ Renderer.defaultProps = {
20965
20963
  onSerializedStateUpdated: () => {},
20966
20964
  linterContext: PerseusLinter.linterContextDefault
20967
20965
  };
20968
- const styles$B = {
20966
+ const styles$A = {
20969
20967
  mobileZoomableParentFix: {
20970
20968
  // NOTE(abdul): There is an issue where transform animations will
20971
20969
  // cause the Zoomable component to disappear when running on the
@@ -21073,7 +21071,7 @@ function _objectWithoutPropertiesLoose(r, e) {
21073
21071
  return t;
21074
21072
  }
21075
21073
 
21076
- const _excluded$8 = ["position", "visibility", "left", "top"];
21074
+ const _excluded$7 = ["position", "visibility", "left", "top"];
21077
21075
  const zIndex = 10;
21078
21076
  let HorizontalDirection = /*#__PURE__*/function (HorizontalDirection) {
21079
21077
  HorizontalDirection["Left"] = "left";
@@ -21124,7 +21122,7 @@ const TooltipArrow = _ref => {
21124
21122
  left = 100,
21125
21123
  top = 0
21126
21124
  } = _ref,
21127
- props = _objectWithoutPropertiesLoose(_ref, _excluded$8);
21125
+ props = _objectWithoutPropertiesLoose(_ref, _excluded$7);
21128
21126
  // TODO(aria): Think about adding a box-shadow to the triangle here
21129
21127
  // See http://css-tricks.com/triangle-with-shadow/
21130
21128
 
@@ -21498,7 +21496,7 @@ InputWithExamples.defaultProps = {
21498
21496
  className: ""
21499
21497
  };
21500
21498
 
21501
- const _excluded$7 = ["keypadElement", "onFocus", "value"];
21499
+ const _excluded$6 = ["keypadElement", "onFocus", "value"];
21502
21500
  class SimpleKeypadInput extends React.Component {
21503
21501
  constructor(...args) {
21504
21502
  super(...args);
@@ -21543,7 +21541,7 @@ class SimpleKeypadInput extends React.Component {
21543
21541
  onFocus,
21544
21542
  value
21545
21543
  } = _this$props,
21546
- rest = _objectWithoutPropertiesLoose(_this$props, _excluded$7);
21544
+ rest = _objectWithoutPropertiesLoose(_this$props, _excluded$6);
21547
21545
  return (
21548
21546
  /*#__PURE__*/
21549
21547
  // @ts-expect-error - TS2769 - No overload matches this call.
@@ -21948,10 +21946,10 @@ class InputNumber extends React.Component {
21948
21946
  // Note: This is _very_ similar to what `numeric-input.jsx` does. If
21949
21947
  // you modify this, double-check if you also need to modify that
21950
21948
  // component.
21951
- const inputStyles = [styles$A.default, this.props.size === "small" ? styles$A.small : null, this.props.rightAlign ? styles$A.rightAlign : styles$A.leftAlign];
21949
+ const inputStyles = [styles$z.default, this.props.size === "small" ? styles$z.small : null, this.props.rightAlign ? styles$z.rightAlign : styles$z.leftAlign];
21952
21950
  // Unanswered
21953
21951
  if (this.props.reviewMode && !this.props.currentValue) {
21954
- inputStyles.push(styles$A.answerStateUnanswered);
21952
+ inputStyles.push(styles$z.answerStateUnanswered);
21955
21953
  }
21956
21954
  return /*#__PURE__*/React.createElement(InputWithExamples
21957
21955
  // eslint-disable-next-line react/no-string-refs
@@ -21981,7 +21979,7 @@ InputNumber.defaultProps = {
21981
21979
  apiOptions: ApiOptions.defaults,
21982
21980
  linterContext: linterContextDefault
21983
21981
  };
21984
- const styles$A = StyleSheet.create({
21982
+ const styles$z = StyleSheet.create({
21985
21983
  default: {
21986
21984
  width: 80,
21987
21985
  height: "auto"
@@ -23152,7 +23150,7 @@ class HighlightRenderer extends React.PureComponent {
23152
23150
  const rects = this.state.cachedHighlightRects;
23153
23151
  return /*#__PURE__*/React.createElement("div", null, rects.map((rect, index) => /*#__PURE__*/React.createElement("div", {
23154
23152
  key: index,
23155
- className: css(styles$z.highlightRect),
23153
+ className: css(styles$y.highlightRect),
23156
23154
  style: {
23157
23155
  // NOTE(mdr): We apply `position: absolute` here
23158
23156
  // rather than in Aphrodite styles, because
@@ -23171,7 +23169,7 @@ class HighlightRenderer extends React.PureComponent {
23171
23169
  })));
23172
23170
  }
23173
23171
  }
23174
- const styles$z = StyleSheet.create({
23172
+ const styles$y = StyleSheet.create({
23175
23173
  highlightRect: {
23176
23174
  background: "#fffabe" // highlighter yellow :)
23177
23175
  }
@@ -23252,7 +23250,7 @@ class HighlightTooltip extends React.PureComponent {
23252
23250
  // TooltipContent wouldn't let me overwrite
23253
23251
  // user-select and onClick
23254
23252
  const content = /*#__PURE__*/React.createElement("div", {
23255
- className: css(styles$y.tooltipLabel),
23253
+ className: css(styles$x.tooltipLabel),
23256
23254
  onClick: this.props.onClick
23257
23255
  }, this.props.label);
23258
23256
  const style = {
@@ -23272,7 +23270,7 @@ class HighlightTooltip extends React.PureComponent {
23272
23270
  }, /*#__PURE__*/React.createElement("div", null)));
23273
23271
  }
23274
23272
  }
23275
- const styles$y = StyleSheet.create({
23273
+ const styles$x = StyleSheet.create({
23276
23274
  tooltipLabel: {
23277
23275
  // Unlike most tooltip contents, we don't want this label to be
23278
23276
  // selectable. Selecting it is not only unhelpful, but dangerous: if
@@ -23823,7 +23821,7 @@ class HighlightableContent extends React.PureComponent {
23823
23821
  // starting point: https://phabricator.khanacademy.org/D35623?id=170698
23824
23822
  const buildHighlight = r => this._buildHighlight(r);
23825
23823
  return /*#__PURE__*/React.createElement("div", {
23826
- className: css(styles$x.container),
23824
+ className: css(styles$w.container),
23827
23825
  ref: container => this._container = container
23828
23826
  }, /*#__PURE__*/React.createElement("div", null, this.props.enabled && this._container && this._content && /*#__PURE__*/React.createElement(HighlightingUI, {
23829
23827
  buildHighlight: buildHighlight,
@@ -23844,14 +23842,14 @@ class HighlightableContent extends React.PureComponent {
23844
23842
  onAddHighlight: this._handleAddHighlight,
23845
23843
  onRemoveHighlight: this._handleRemoveHighlight
23846
23844
  })), /*#__PURE__*/React.createElement("div", {
23847
- className: css(styles$x.content),
23845
+ className: css(styles$w.content),
23848
23846
  ref: content => this._content = content
23849
23847
  }, /*#__PURE__*/React.createElement(WordIndexer, {
23850
23848
  onWordsUpdate: this._handleWordsUpdate
23851
23849
  }, this.props.children)));
23852
23850
  }
23853
23851
  }
23854
- const styles$x = StyleSheet.create({
23852
+ const styles$w = StyleSheet.create({
23855
23853
  // We render content with z-index 1, and highlights with z-index 0, so that
23856
23854
  // content will receive the browser's native pointer events. (Highlights
23857
23855
  // have their own magic code to manage hover detection, despite being
@@ -24277,7 +24275,7 @@ class LineHeightMeasurer extends React.Component {
24277
24275
  }
24278
24276
  render() {
24279
24277
  return /*#__PURE__*/React.createElement("div", {
24280
- className: css(styles$w.measurer),
24278
+ className: css(styles$v.measurer),
24281
24279
  "aria-hidden": "true"
24282
24280
  }, /*#__PURE__*/React.createElement("div", {
24283
24281
  ref: ref => this._line = ref,
@@ -24285,7 +24283,7 @@ class LineHeightMeasurer extends React.Component {
24285
24283
  }, "Line Height Measurement"));
24286
24284
  }
24287
24285
  }
24288
- const styles$w = StyleSheet.create({
24286
+ const styles$v = StyleSheet.create({
24289
24287
  measurer: {
24290
24288
  position: "absolute",
24291
24289
  top: 0,
@@ -25262,11 +25260,11 @@ const FocusRing = function FocusRing({
25262
25260
  };
25263
25261
  return /*#__PURE__*/React.createElement("span", {
25264
25262
  "data-testid": "focus-ring",
25265
- className: css(styles$v.ring),
25263
+ className: css(styles$u.ring),
25266
25264
  style: style
25267
25265
  }, children);
25268
25266
  };
25269
- const styles$v = StyleSheet.create({
25267
+ const styles$u = StyleSheet.create({
25270
25268
  ring: {
25271
25269
  margin: "auto",
25272
25270
  display: "inline-block",
@@ -25293,7 +25291,7 @@ function getChoiceLetter(pos, strings) {
25293
25291
  }
25294
25292
 
25295
25293
  const CHOICE_ICON_SIZE = 24;
25296
- const styles$u = StyleSheet.create({
25294
+ const styles$t = StyleSheet.create({
25297
25295
  iconWrapper: {
25298
25296
  display: "inline-block",
25299
25297
  position: "relative"
@@ -25316,7 +25314,7 @@ const CrossOutLine = function CrossOutLine(props) {
25316
25314
  width: CROSS_OUT_LINE_SIZE,
25317
25315
  height: CROSS_OUT_LINE_SIZE,
25318
25316
  viewBox: `0 0 ${CROSS_OUT_LINE_SIZE} ${CROSS_OUT_LINE_SIZE}`,
25319
- className: css(styles$t.crossOutLine),
25317
+ className: css(styles$s.crossOutLine),
25320
25318
  "data-testid": "choice-icon__cross-out-line"
25321
25319
  }, /*#__PURE__*/React.createElement("line", {
25322
25320
  // The line stretches from the bottom-left to top-right.
@@ -25331,7 +25329,7 @@ const CrossOutLine = function CrossOutLine(props) {
25331
25329
  strokeLinecap: "round"
25332
25330
  }));
25333
25331
  };
25334
- const styles$t = StyleSheet.create({
25332
+ const styles$s = StyleSheet.create({
25335
25333
  crossOutLine: {
25336
25334
  // Center the icon within the container.
25337
25335
  position: "absolute",
@@ -25415,7 +25413,7 @@ const ChoiceIcon = function ChoiceIcon(props) {
25415
25413
  } = props;
25416
25414
  const dynamicStyles = getDynamicStyles(checked, showCorrectness, pressed, multipleSelect, correct);
25417
25415
  return /*#__PURE__*/React.createElement("div", {
25418
- className: css(styles$u.iconWrapper)
25416
+ className: css(styles$t.iconWrapper)
25419
25417
  }, /*#__PURE__*/React.createElement(FocusRing, {
25420
25418
  color: color.blue,
25421
25419
  visible: focused || hovered,
@@ -25423,13 +25421,13 @@ const ChoiceIcon = function ChoiceIcon(props) {
25423
25421
  }, /*#__PURE__*/React.createElement("div", {
25424
25422
  style: dynamicStyles,
25425
25423
  "data-testid": "choice-icon__library-choice-icon",
25426
- className: css(styles$s.circle, showCorrectness && correct && styles$s.circleCorrect, showCorrectness && !correct && styles$s.circleIncorrect, showCorrectness && !correct && (checked || previouslyAnswered) && styles$s.circleIncorrectAnswered)
25424
+ className: css(styles$r.circle, showCorrectness && correct && styles$r.circleCorrect, showCorrectness && !correct && styles$r.circleIncorrect, showCorrectness && !correct && (checked || previouslyAnswered) && styles$r.circleIncorrectAnswered)
25427
25425
  // used in BaseRadio editMode to check
25428
25426
  // if we actually clicked on the radio icon
25429
25427
  ,
25430
25428
  "data-is-radio-icon": true
25431
25429
  }, /*#__PURE__*/React.createElement("div", {
25432
- className: css(styles$s.innerWrapper)
25430
+ className: css(styles$r.innerWrapper)
25433
25431
  }, /*#__PURE__*/React.createElement(ChoiceInner, {
25434
25432
  pos: pos,
25435
25433
  showCorrectness: showCorrectness,
@@ -25438,7 +25436,7 @@ const ChoiceIcon = function ChoiceIcon(props) {
25438
25436
  color: dynamicStyles.borderColor
25439
25437
  }));
25440
25438
  };
25441
- const styles$s = StyleSheet.create({
25439
+ const styles$r = StyleSheet.create({
25442
25440
  innerWrapper: {
25443
25441
  width: CHOICE_ICON_SIZE,
25444
25442
  height: CHOICE_ICON_SIZE,
@@ -25533,19 +25531,19 @@ const OptionStatus = function OptionStatus(props) {
25533
25531
  }
25534
25532
  let textStyle;
25535
25533
  if (correct) {
25536
- textStyle = styles$r.correct;
25534
+ textStyle = styles$q.correct;
25537
25535
  } else {
25538
25536
  if (checked || previouslyAnswered) {
25539
- textStyle = styles$r.incorrectAnswered;
25537
+ textStyle = styles$q.incorrectAnswered;
25540
25538
  } else {
25541
- textStyle = styles$r.incorrect;
25539
+ textStyle = styles$q.incorrect;
25542
25540
  }
25543
25541
  }
25544
25542
  return /*#__PURE__*/React.createElement("div", {
25545
- className: css(styles$r.text, textStyle)
25543
+ className: css(styles$q.text, textStyle)
25546
25544
  }, renderText(checked, correct, crossedOut, strings));
25547
25545
  };
25548
- const styles$r = StyleSheet.create({
25546
+ const styles$q = StyleSheet.create({
25549
25547
  text: {
25550
25548
  alignItems: "center",
25551
25549
  display: "flex",
@@ -25615,8 +25613,8 @@ const Choice = function Choice(props) {
25615
25613
  crossedOut: updatedCrossedOut
25616
25614
  });
25617
25615
  }
25618
- const descriptionClassName = classNames$1("description", css(styles$q.description));
25619
- const rationaleClassName = classNames$1("perseus-radio-rationale-content", css(styles$q.rationale));
25616
+ const descriptionClassName = classNames$1("description", css(styles$p.description));
25617
+ const rationaleClassName = classNames$1("perseus-radio-rationale-content", css(styles$p.rationale));
25620
25618
 
25621
25619
  // We want to show the choices as dimmed out when the choices are disabled.
25622
25620
  // However, we don't want to do this when we're in review mode in the
@@ -25776,7 +25774,7 @@ const Choice = function Choice(props) {
25776
25774
  "data-testid": `perseus-radio-rationale-content-${pos}`
25777
25775
  }, rationale));
25778
25776
  };
25779
- const styles$q = StyleSheet.create({
25777
+ const styles$p = StyleSheet.create({
25780
25778
  description: {
25781
25779
  display: "inline-block",
25782
25780
  width: "100%"
@@ -25797,14 +25795,14 @@ var Choice$1 = /*#__PURE__*/React.forwardRef((props, ref) => /*#__PURE__*/React.
25797
25795
  forwardedRef: ref
25798
25796
  })));
25799
25797
 
25800
- const _excluded$6 = ["content", "forwardedRef", "showContent"];
25798
+ const _excluded$5 = ["content", "forwardedRef", "showContent"];
25801
25799
  const ChoiceNoneAbove = function ChoiceNoneAbove(_ref) {
25802
25800
  let {
25803
25801
  content,
25804
25802
  forwardedRef,
25805
25803
  showContent = true
25806
25804
  } = _ref,
25807
- rest = _objectWithoutPropertiesLoose(_ref, _excluded$6);
25805
+ rest = _objectWithoutPropertiesLoose(_ref, _excluded$5);
25808
25806
  const {
25809
25807
  strings
25810
25808
  } = usePerseusI18n();
@@ -25966,10 +25964,10 @@ const BaseRadio = function BaseRadio({
25966
25964
  const isMobile = apiOptions.isMobile;
25967
25965
  const firstChoiceHighlighted = choices[0].highlighted;
25968
25966
  const lastChoiceHighlighted = choices[choices.length - 1].highlighted;
25969
- const className = classNames$1("perseus-widget-radio", !editMode && "perseus-rendered-radio", css(styles$p.radio, styles$p.responsiveRadioContainer, firstChoiceHighlighted && isMobile && styles$p.radioContainerFirstHighlighted, lastChoiceHighlighted && isMobile && styles$p.radioContainerLastHighlighted));
25970
- const instructionsClassName = classNames$1("instructions", css(styles$p.instructions, isMobile && styles$p.instructionsMobile));
25967
+ const className = classNames$1("perseus-widget-radio", !editMode && "perseus-rendered-radio", css(styles$o.radio, styles$o.responsiveRadioContainer, firstChoiceHighlighted && isMobile && styles$o.radioContainerFirstHighlighted, lastChoiceHighlighted && isMobile && styles$o.radioContainerLastHighlighted));
25968
+ const instructionsClassName = classNames$1("instructions", css(styles$o.instructions, isMobile && styles$o.instructionsMobile));
25971
25969
  const instructions = getInstructionsText(multipleSelect, countChoices, numCorrect, strings);
25972
- const responsiveClassName = css(styles$p.responsiveFieldset);
25970
+ const responsiveClassName = css(styles$o.responsiveFieldset);
25973
25971
  const fieldset = /*#__PURE__*/React.createElement("fieldset", {
25974
25972
  className: `perseus-widget-radio-fieldset ${responsiveClassName}`
25975
25973
  }, /*#__PURE__*/React.createElement("legend", {
@@ -26020,7 +26018,7 @@ const BaseRadio = function BaseRadio({
26020
26018
  const aphroditeClassName = checked => {
26021
26019
  // Whether or not to show correctness borders
26022
26020
  // for this choice and the next choice.
26023
- return css(sharedStyles.aboveScratchpad, styles$p.item, styles$p.responsiveItem, checked && styles$p.selectedItem, checked && choice.highlighted && styles$p.aboveBackdrop, checked && choice.highlighted && apiOptions.isMobile && styles$p.aboveBackdropMobile, nextChoiceHighlighted && apiOptions.isMobile && styles$p.nextHighlighted);
26021
+ return css(sharedStyles.aboveScratchpad, styles$o.item, styles$o.responsiveItem, checked && styles$o.selectedItem, checked && choice.highlighted && styles$o.aboveBackdrop, checked && choice.highlighted && apiOptions.isMobile && styles$o.aboveBackdropMobile, nextChoiceHighlighted && apiOptions.isMobile && styles$o.nextHighlighted);
26024
26022
  };
26025
26023
 
26026
26024
  // HACK(abdulrahman): Preloads the selection-state
@@ -26085,10 +26083,10 @@ const BaseRadio = function BaseRadio({
26085
26083
  // Allow for horizontal scrolling if content is too wide, which may be
26086
26084
  // an issue especially on phones.
26087
26085
  return /*#__PURE__*/React.createElement("div", {
26088
- className: css(styles$p.responsiveContainer)
26086
+ className: css(styles$o.responsiveContainer)
26089
26087
  }, fieldset);
26090
26088
  };
26091
- const styles$p = StyleSheet.create({
26089
+ const styles$o = StyleSheet.create({
26092
26090
  instructions: {
26093
26091
  display: "block",
26094
26092
  color: gray17,
@@ -26580,7 +26578,7 @@ Radio$1.defaultProps = {
26580
26578
  showSolutions: "none"
26581
26579
  };
26582
26580
 
26583
- const _excluded$5 = ["noneOfTheAbove"];
26581
+ const _excluded$4 = ["noneOfTheAbove"];
26584
26582
  const {
26585
26583
  shuffle: shuffle$2,
26586
26584
  random
@@ -26672,7 +26670,7 @@ const propUpgrades$1 = {
26672
26670
  const {
26673
26671
  noneOfTheAbove
26674
26672
  } = v0props,
26675
- rest = _objectWithoutPropertiesLoose(v0props, _excluded$5);
26673
+ rest = _objectWithoutPropertiesLoose(v0props, _excluded$4);
26676
26674
  if (noneOfTheAbove) {
26677
26675
  throw new Error("radio widget v0 no longer supports auto noneOfTheAbove");
26678
26676
  }
@@ -26795,9 +26793,9 @@ class Categorizer extends React.Component {
26795
26793
  indexedItems = Util.shuffle(indexedItems, this.props.problemNum);
26796
26794
  }
26797
26795
  const table = /*#__PURE__*/React.createElement("table", {
26798
- className: "categorizer-table " + css(styles$o.mobileTable)
26796
+ className: "categorizer-table " + css(styles$n.mobileTable)
26799
26797
  }, /*#__PURE__*/React.createElement("thead", null, /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("td", {
26800
- className: css(styles$o.emptyHeaderCell)
26798
+ className: css(styles$n.emptyHeaderCell)
26801
26799
  }), this.props.categories.map((category, i) => {
26802
26800
  // Array index is the correct key here, as that's
26803
26801
  // how category grading actually works -- no way
@@ -26805,7 +26803,7 @@ class Categorizer extends React.Component {
26805
26803
  // middle. (If we later add that, this should be
26806
26804
  // fixed.)
26807
26805
  return /*#__PURE__*/React.createElement("th", {
26808
- className: css(styles$o.header),
26806
+ className: css(styles$n.header),
26809
26807
  key: i
26810
26808
  }, /*#__PURE__*/React.createElement(Renderer, {
26811
26809
  content: category,
@@ -26827,7 +26825,7 @@ class Categorizer extends React.Component {
26827
26825
  })), self.props.categories.map((catName, catNum) => {
26828
26826
  const selected = self.props.values[itemNum] === catNum;
26829
26827
  return /*#__PURE__*/React.createElement("td", {
26830
- className: "category " + css(styles$o.cell),
26828
+ className: "category " + css(styles$n.cell),
26831
26829
  key: catNum
26832
26830
  }, /*#__PURE__*/React.createElement("div", {
26833
26831
  className: ClassNames.INTERACTIVE,
@@ -26842,7 +26840,7 @@ class Categorizer extends React.Component {
26842
26840
  onChange: () => this.onChange(itemNum, catNum),
26843
26841
  onClick: e => e.stopPropagation()
26844
26842
  }), !isMobile && /*#__PURE__*/React.createElement("span", {
26845
- className: css(styles$o.radioSpan, selected && styles$o.checkedRadioSpan, this.props.static && selected && styles$o.staticCheckedRadioSpan)
26843
+ className: css(styles$n.radioSpan, selected && styles$n.checkedRadioSpan, this.props.static && selected && styles$n.staticCheckedRadioSpan)
26846
26844
  }, selected ? /*#__PURE__*/React.createElement(InlineIcon, iconCircle) : /*#__PURE__*/React.createElement(InlineIcon, iconCircleThin))));
26847
26845
  }));
26848
26846
  /* eslint-enable max-len */
@@ -26854,7 +26852,7 @@ class Categorizer extends React.Component {
26854
26852
  "categorizer-container": true,
26855
26853
  "static-mode": this.props.static
26856
26854
  });
26857
- const inlineStyles = this.props.apiOptions.isMobile ? [styles$o.fullBleedContainer] : [];
26855
+ const inlineStyles = this.props.apiOptions.isMobile ? [styles$n.fullBleedContainer] : [];
26858
26856
  return /*#__PURE__*/React.createElement("div", {
26859
26857
  className: extraClassNames + " " + css(...inlineStyles)
26860
26858
  }, table);
@@ -26873,7 +26871,7 @@ const pageMargin = 16;
26873
26871
 
26874
26872
  // Stylesheets aren't directly testable
26875
26873
  /* c8 ignore next */
26876
- const styles$o = StyleSheet.create({
26874
+ const styles$n = StyleSheet.create({
26877
26875
  mobileTable: {
26878
26876
  [mediaQueries.smOrSmaller]: {
26879
26877
  minWidth: "auto"
@@ -27131,7 +27129,7 @@ class CSProgram extends React.Component {
27131
27129
  // creator "went wild".
27132
27130
  // http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
27133
27131
  return /*#__PURE__*/React.createElement("div", {
27134
- className: css(styles$n.widthOverride, styleContainer && styles$n.container)
27132
+ className: css(styles$m.widthOverride, styleContainer && styles$m.container)
27135
27133
  }, /*#__PURE__*/React.createElement("iframe", {
27136
27134
  sandbox: sandboxOptions,
27137
27135
  src: url,
@@ -27148,7 +27146,7 @@ CSProgram.defaultProps = {
27148
27146
  // optional message
27149
27147
  message: null
27150
27148
  };
27151
- const styles$n = StyleSheet.create({
27149
+ const styles$m = StyleSheet.create({
27152
27150
  // Override the inherited width from the perseus paragraph class
27153
27151
  // 820 is minimum width for 420px editor and 400px canvas
27154
27152
  widthOverride: {
@@ -27194,7 +27192,7 @@ class Definition extends React.Component {
27194
27192
  }) => /*#__PURE__*/React.createElement(Popover, {
27195
27193
  content: /*#__PURE__*/React.createElement(PopoverContentCore, {
27196
27194
  color: "white",
27197
- style: styles$m.tooltipBody,
27195
+ style: styles$l.tooltipBody,
27198
27196
  closeButtonVisible: true
27199
27197
  }, /*#__PURE__*/React.createElement(Renderer, {
27200
27198
  apiOptions: this.props.apiOptions,
@@ -27227,7 +27225,7 @@ Definition.defaultProps = {
27227
27225
  togglePrompt: "define me",
27228
27226
  definition: "definition goes here"
27229
27227
  };
27230
- const styles$m = {
27228
+ const styles$l = {
27231
27229
  tooltipBody: {
27232
27230
  color: color.offBlack,
27233
27231
  fontSize: 20,
@@ -27490,7 +27488,7 @@ class Explanation extends React.Component {
27490
27488
  marginRight: "-6px",
27491
27489
  "white-space": "normal"
27492
27490
  };
27493
- const contentStyling = [styles$l.content, this.state.expanded ? styles$l.contentExpanded : styles$l.contentCollapsed, allowTransition && (this.state.expanded ? styles$l.transitionExpanded : styles$l.transitionCollapsed)];
27491
+ const contentStyling = [styles$k.content, this.state.expanded ? styles$k.contentExpanded : styles$k.contentCollapsed, allowTransition && (this.state.expanded ? styles$k.transitionExpanded : styles$k.transitionCollapsed)];
27494
27492
  return /*#__PURE__*/React.createElement(UniqueIDProvider, {
27495
27493
  mockOnFirstRender: true,
27496
27494
  scope: "explanation-widget"
@@ -27509,7 +27507,7 @@ class Explanation extends React.Component {
27509
27507
  "aria-hidden": !this.state.expanded,
27510
27508
  testId: "content-container"
27511
27509
  }, /*#__PURE__*/React.createElement(View, {
27512
- style: styles$l.contentWrapper
27510
+ style: styles$k.contentWrapper
27513
27511
  }, /*#__PURE__*/React.createElement(Renderer, {
27514
27512
  apiOptions: this.props.apiOptions,
27515
27513
  content: this.props.explanation,
@@ -27530,7 +27528,7 @@ Explanation.defaultProps = {
27530
27528
  const leftBorderSpacing = 23;
27531
27529
  const verticalContentPadding = 10;
27532
27530
  const arrowHeight = 14;
27533
- const styles$l = StyleSheet.create({
27531
+ const styles$k = StyleSheet.create({
27534
27532
  content: {
27535
27533
  borderLeft: "0px solid #ccc",
27536
27534
  display: "inline-grid",
@@ -27613,15 +27611,15 @@ class GradedGroupAnswerBar extends React.Component {
27613
27611
  correctExcited,
27614
27612
  nextQuestion
27615
27613
  } = this.context.strings;
27616
- const answerBarStyle = _extends({}, styles$k.answerBar, {
27614
+ const answerBarStyle = _extends({}, styles$j.answerBar, {
27617
27615
  backgroundColor: answerBarState === "CORRECT" ? color.offWhite : "white",
27618
27616
  // Center the "Correct!" message only when there's no next question
27619
27617
  justifyContent: answerBarState === "CORRECT" && !onNextQuestion ? "center" : "space-between"
27620
27618
  });
27621
27619
  const message = answerBarState === "INCORRECT" ? /*#__PURE__*/React.createElement("span", {
27622
- style: styles$k.text
27620
+ style: styles$j.text
27623
27621
  }, /*#__PURE__*/React.createElement("span", {
27624
- style: styles$k.tryAgainIcon
27622
+ style: styles$j.tryAgainIcon
27625
27623
  }, /*#__PURE__*/React.createElement(InlineIcon, iconTryAgain)), /*#__PURE__*/React.createElement("span", {
27626
27624
  style: {
27627
27625
  marginLeft: 8
@@ -27640,7 +27638,7 @@ class GradedGroupAnswerBar extends React.Component {
27640
27638
  return /*#__PURE__*/React.createElement("div", {
27641
27639
  style: answerBarStyle
27642
27640
  }, /*#__PURE__*/React.createElement("span", {
27643
- style: styles$k.text
27641
+ style: styles$j.text
27644
27642
  }, /*#__PURE__*/React.createElement("span", {
27645
27643
  style: {
27646
27644
  fontSize: 28,
@@ -27663,7 +27661,7 @@ class GradedGroupAnswerBar extends React.Component {
27663
27661
  }
27664
27662
  GradedGroupAnswerBar.contextType = PerseusI18nContext;
27665
27663
  const fontSize = 17;
27666
- const styles$k = {
27664
+ const styles$j = {
27667
27665
  answerBar: {
27668
27666
  display: "flex",
27669
27667
  alignItems: "center",
@@ -27848,7 +27846,7 @@ class GradedGroup extends React.Component {
27848
27846
  }));
27849
27847
  gradeStatus = this.context.strings.incorrect;
27850
27848
  }
27851
- const mobileClass = this.props.inGradedGroupSet ? css(styles$j.gradedGroupInSet) : css(styles$j.gradedGroup);
27849
+ const mobileClass = this.props.inGradedGroupSet ? css(styles$i.gradedGroupInSet) : css(styles$i.gradedGroup);
27852
27850
  const classes = classNames$1({
27853
27851
  [mobileClass]: apiOptions.isMobile,
27854
27852
  "perseus-graded-group": true,
@@ -27867,7 +27865,7 @@ class GradedGroup extends React.Component {
27867
27865
  return /*#__PURE__*/React.createElement("div", {
27868
27866
  className: classes
27869
27867
  }, !!this.props.title && /*#__PURE__*/React.createElement("div", {
27870
- className: css(styles$j.title)
27868
+ className: css(styles$i.title)
27871
27869
  }, this.props.title), /*#__PURE__*/React.createElement(Renderer, _extends({}, this.props, {
27872
27870
  ref: this.rendererRef,
27873
27871
  apiOptions: _extends({}, apiOptions, {
@@ -27899,7 +27897,7 @@ class GradedGroup extends React.Component {
27899
27897
  }, this.context.strings.nextQuestion), this.props.hint && this.props.hint.content && (this.state.showHint ? /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("button", {
27900
27898
  // @ts-expect-error - TS2322 - Type 'string' is not assignable to type 'number | undefined'.
27901
27899
  tabIndex: "0",
27902
- className: css(styles$j.explanationTitle),
27900
+ className: css(styles$i.explanationTitle),
27903
27901
  onClick: () => this.setState({
27904
27902
  showHint: false
27905
27903
  }),
@@ -27931,7 +27929,7 @@ class GradedGroup extends React.Component {
27931
27929
  showHint: true
27932
27930
  });
27933
27931
  },
27934
- className: css(styles$j.showHintLink)
27932
+ className: css(styles$i.showHintLink)
27935
27933
  }, this.context.strings.explain)), apiOptions.isMobile && answerBarState !== "HIDDEN" && /*#__PURE__*/React.createElement(GradedGroupAnswerBar, {
27936
27934
  apiOptions: apiOptions,
27937
27935
  answerBarState: answerBarState,
@@ -27953,7 +27951,7 @@ GradedGroup.defaultProps = {
27953
27951
  const traverseChildWidgets$2 = function traverseChildWidgets(props, traverseRenderer) {
27954
27952
  return _.extend({}, props, traverseRenderer(props));
27955
27953
  };
27956
- const styles$j = StyleSheet.create({
27954
+ const styles$i = StyleSheet.create({
27957
27955
  gradedGroupInSet: {
27958
27956
  // Reset a few desktop-only styles that come from graded-group.less
27959
27957
  marginLeft: 0,
@@ -28036,18 +28034,18 @@ class Indicators extends React.Component {
28036
28034
  return /*#__PURE__*/React.createElement("ul", {
28037
28035
  // reduntantly add class name for use in .less files--
28038
28036
  // the styles object key gets hashed
28039
- className: classNames$1(css(styles$i.indicatorContainer), "indicatorContainer")
28037
+ className: classNames$1(css(styles$h.indicatorContainer), "indicatorContainer")
28040
28038
  }, this.props.gradedGroups.map(({
28041
28039
  title
28042
28040
  }, i) => /*#__PURE__*/React.createElement("li", {
28043
- className: css(styles$i.indicator),
28041
+ className: css(styles$h.indicator),
28044
28042
  key: title
28045
28043
  }, /*#__PURE__*/React.createElement(Clickable, {
28046
28044
  role: "button",
28047
28045
  "aria-label": this.context.strings.skipToTitle({
28048
28046
  title
28049
28047
  }),
28050
- style: styles$i.indicatorButton,
28048
+ style: styles$h.indicatorButton,
28051
28049
  onClick: () => this.props.onChangeCurrentGroup(i),
28052
28050
  onKeyDown: e => this.handleKeyDown(e, i)
28053
28051
  }, ({
@@ -28055,9 +28053,9 @@ class Indicators extends React.Component {
28055
28053
  focused,
28056
28054
  pressed
28057
28055
  }) => /*#__PURE__*/React.createElement(View, {
28058
- style: [styles$i.indicatorDot, (hovered || focused || pressed) && styles$i.indicatorDotFocused]
28056
+ style: [styles$h.indicatorDot, (hovered || focused || pressed) && styles$h.indicatorDotFocused]
28059
28057
  }, i === this.props.currentGroup && /*#__PURE__*/React.createElement(View, {
28060
- style: styles$i.indicatorDotActive
28058
+ style: styles$h.indicatorDotActive
28061
28059
  }, /*#__PURE__*/React.createElement("span", {
28062
28060
  className: css(a11y.srOnly)
28063
28061
  }, this.context.strings.current)))))));
@@ -28125,7 +28123,7 @@ class GradedGroupSet extends React.Component {
28125
28123
  } = getDependencies();
28126
28124
  if (JIPT.useJIPT && this.props.gradedGroups.length > 1) {
28127
28125
  return /*#__PURE__*/React.createElement("div", {
28128
- className: css(styles$i.container)
28126
+ className: css(styles$h.container)
28129
28127
  }, this.props.gradedGroups.map((gradedGroup, i) => {
28130
28128
  return (
28131
28129
  /*#__PURE__*/
@@ -28148,13 +28146,13 @@ class GradedGroupSet extends React.Component {
28148
28146
  const numGroups = this.props.gradedGroups.length;
28149
28147
  const handleNextQuestion = this.state.currentGroup < numGroups - 1 ? this.handleNextQuestion : null;
28150
28148
  return /*#__PURE__*/React.createElement("div", {
28151
- className: css(styles$i.container)
28149
+ className: css(styles$h.container)
28152
28150
  }, /*#__PURE__*/React.createElement("div", {
28153
- className: css(styles$i.top)
28151
+ className: css(styles$h.top)
28154
28152
  }, /*#__PURE__*/React.createElement("div", {
28155
- className: css(styles$i.title)
28153
+ className: css(styles$h.title)
28156
28154
  }, currentGroup.title), /*#__PURE__*/React.createElement("div", {
28157
- className: css(styles$i.spacer)
28155
+ className: css(styles$h.spacer)
28158
28156
  }), /*#__PURE__*/React.createElement(Indicators, {
28159
28157
  currentGroup: this.state.currentGroup,
28160
28158
  gradedGroups: this.props.gradedGroups,
@@ -28196,7 +28194,7 @@ var GradedGroupSet$1 = {
28196
28194
  tracking: "all",
28197
28195
  isLintable: true
28198
28196
  };
28199
- const styles$i = StyleSheet.create({
28197
+ const styles$h = StyleSheet.create({
28200
28198
  top: {
28201
28199
  display: "flex",
28202
28200
  flexDirection: "row"
@@ -28301,7 +28299,7 @@ class ButtonGroup extends React.Component {
28301
28299
  id: "" + i,
28302
28300
  ref: "button" + i,
28303
28301
  key: "" + i,
28304
- className: css(styles$h.buttonStyle, button.value === value && styles$h.selectedStyle),
28302
+ className: css(styles$g.buttonStyle, button.value === value && styles$g.selectedStyle),
28305
28303
  onClick: () => this.toggleSelect(button.value)
28306
28304
  }, button.content || "" + button.value);
28307
28305
  });
@@ -28318,7 +28316,7 @@ ButtonGroup.defaultProps = {
28318
28316
  value: null,
28319
28317
  allowEmpty: true
28320
28318
  };
28321
- const styles$h = StyleSheet.create({
28319
+ const styles$g = StyleSheet.create({
28322
28320
  buttonStyle: {
28323
28321
  backgroundColor: "white",
28324
28322
  border: "1px solid #ccc",
@@ -31659,7 +31657,7 @@ const VisibleIcon = () => /*#__PURE__*/React.createElement("svg", {
31659
31657
  width: "24",
31660
31658
  height: "24",
31661
31659
  viewBox: "0 0 24 24",
31662
- className: css(styles$g.icon)
31660
+ className: css(styles$f.icon)
31663
31661
  }, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("path", {
31664
31662
  id: "a",
31665
31663
  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"
@@ -31690,7 +31688,7 @@ const HiddenIcon = () => /*#__PURE__*/React.createElement("svg", {
31690
31688
  width: "24",
31691
31689
  height: "24",
31692
31690
  viewBox: "0 0 24 24",
31693
- className: css(styles$g.icon)
31691
+ className: css(styles$f.icon)
31694
31692
  }, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("path", {
31695
31693
  id: "a",
31696
31694
  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"
@@ -31728,20 +31726,20 @@ const HUD = ({
31728
31726
  let state;
31729
31727
  let icon;
31730
31728
  if (enabled) {
31731
- state = styles$g.enabled;
31729
+ state = styles$f.enabled;
31732
31730
  icon = /*#__PURE__*/React.createElement(VisibleIcon, null);
31733
31731
  } else {
31734
- state = styles$g.disabled;
31732
+ state = styles$f.disabled;
31735
31733
  icon = /*#__PURE__*/React.createElement(HiddenIcon, null);
31736
31734
  }
31737
31735
  return /*#__PURE__*/React.createElement("button", {
31738
- className: css(styles$g.hud, _fixedPosition && styles$g.hudFixedPosition, state),
31736
+ className: css(styles$f.hud, _fixedPosition && styles$f.hudFixedPosition, state),
31739
31737
  onClick: e => {
31740
31738
  onClick();
31741
31739
  }
31742
31740
  }, icon, message);
31743
31741
  };
31744
- const styles$g = StyleSheet.create({
31742
+ const styles$f = StyleSheet.create({
31745
31743
  hud: {
31746
31744
  boxSizing: "border-box",
31747
31745
  height: 36,
@@ -31794,16 +31792,16 @@ const styles$g = StyleSheet.create({
31794
31792
  const InfoTipBase = props => {
31795
31793
  return /*#__PURE__*/React.createElement(Tooltip$1, {
31796
31794
  content: /*#__PURE__*/React.createElement(Body, {
31797
- style: styles$f.body
31795
+ style: styles$e.body
31798
31796
  }, props.children),
31799
31797
  placement: "right"
31800
31798
  }, /*#__PURE__*/React.createElement(PhosphorIcon, {
31801
31799
  size: "small",
31802
31800
  icon: questionIcon,
31803
- style: styles$f.spacingLeft
31801
+ style: styles$e.spacingLeft
31804
31802
  }));
31805
31803
  };
31806
- const styles$f = StyleSheet.create({
31804
+ const styles$e = StyleSheet.create({
31807
31805
  spacingLeft: {
31808
31806
  marginInlineStart: spacing.xxxSmall_4
31809
31807
  },
@@ -31882,7 +31880,7 @@ class MultiButtonGroup extends React.Component {
31882
31880
  id: "" + i,
31883
31881
  key: "" + i,
31884
31882
  ref: "button" + i,
31885
- className: css(styles$e.buttonStyle, selected && styles$e.selectedStyle),
31883
+ className: css(styles$d.buttonStyle, selected && styles$d.selectedStyle),
31886
31884
  onClick: () => this.toggleSelect(button.value)
31887
31885
  }, button.content || "" + button.value);
31888
31886
  });
@@ -31899,7 +31897,7 @@ MultiButtonGroup.defaultProps = {
31899
31897
  values: [],
31900
31898
  allowEmpty: true
31901
31899
  };
31902
- const styles$e = StyleSheet.create({
31900
+ const styles$d = StyleSheet.create({
31903
31901
  buttonStyle: {
31904
31902
  backgroundColor: "white",
31905
31903
  border: "1px solid #ccc",
@@ -31932,7 +31930,7 @@ const styles$e = StyleSheet.create({
31932
31930
  }
31933
31931
  });
31934
31932
 
31935
- const _excluded$4 = ["onFormatChange", "checkValidity", "useArrowKeys"];
31933
+ const _excluded$3 = ["onFormatChange", "checkValidity", "useArrowKeys"];
31936
31934
  const {
31937
31935
  firstNumericalParse,
31938
31936
  captureScratchpadTouchStart
@@ -32086,7 +32084,7 @@ class NumberInput extends React.Component {
32086
32084
  classes = classes + " " + this.props.className;
32087
32085
  }
32088
32086
  const _this$props = this.props,
32089
- restProps = _objectWithoutPropertiesLoose(_this$props, _excluded$4);
32087
+ restProps = _objectWithoutPropertiesLoose(_this$props, _excluded$3);
32090
32088
  return /*#__PURE__*/React.createElement("input", _extends({}, restProps, {
32091
32089
  className: classes,
32092
32090
  type: "text",
@@ -32296,14 +32294,6 @@ const LegacyGrid = ({
32296
32294
  * A "PerseusArticle" is an item that is meant to be rendered as an article.
32297
32295
  * This item is never scored and is rendered by the `ArticleRenderer`.
32298
32296
  */
32299
- /**
32300
- * A "MultiItem" is an advanced Perseus item. It is rendered by the
32301
- * `MultiRenderer` and you can control the layout of individual parts of the
32302
- * item.
32303
- *
32304
- * @deprecated MultiItem support is slated for removal in a future Perseus
32305
- * release.
32306
- */
32307
32297
  const ItemExtras = [
32308
32298
  // The user might benefit from using a Scientific Calculator. Provided on Khan Academy when true
32309
32299
  "calculator",
@@ -33076,12 +33066,12 @@ const MafsCssTransformWrapper = ({
33076
33066
  }
33077
33067
  }, children);
33078
33068
 
33079
- const _excluded$3 = ["children"];
33069
+ const _excluded$2 = ["children"];
33080
33070
  const TextLabel = _ref => {
33081
33071
  let {
33082
33072
  children
33083
33073
  } = _ref,
33084
- rest = _objectWithoutPropertiesLoose(_ref, _excluded$3);
33074
+ rest = _objectWithoutPropertiesLoose(_ref, _excluded$2);
33085
33075
  return /*#__PURE__*/React__default.createElement(Text$1, _extends({
33086
33076
  size: 16,
33087
33077
  svgTextProps: {
@@ -38711,7 +38701,7 @@ const HideAnswersToggle = props => {
38711
38701
  strings
38712
38702
  } = usePerseusI18n();
38713
38703
  return /*#__PURE__*/React.createElement(View, {
38714
- style: styles$d.switchWrapper
38704
+ style: styles$c.switchWrapper
38715
38705
  }, /*#__PURE__*/React.createElement(Switch, {
38716
38706
  id: switchId,
38717
38707
  checked: props.areAnswersHidden,
@@ -38723,7 +38713,7 @@ const HideAnswersToggle = props => {
38723
38713
  tag: "label"
38724
38714
  }, strings.hideAnswersToggleLabel));
38725
38715
  };
38726
- const styles$d = StyleSheet.create({
38716
+ const styles$c = StyleSheet.create({
38727
38717
  switchWrapper: {
38728
38718
  display: "flex",
38729
38719
  flexDirection: "row",
@@ -38779,14 +38769,14 @@ const AnswerPill = props => {
38779
38769
  id: idFactory.get("perseus-label-image-widget-answer-pill"),
38780
38770
  onClick: correct ? undefined : onClick,
38781
38771
  ref: ref,
38782
- style: [style, popperStyle, styles$c.pill, correct && styles$c.correct, incorrect && styles$c.incorrect, (focused || hovered) && BringToFront]
38772
+ style: [style, popperStyle, styles$b.pill, correct && styles$b.correct, incorrect && styles$b.incorrect, (focused || hovered) && BringToFront]
38783
38773
  }, /*#__PURE__*/React.createElement(Renderer, {
38784
38774
  content: answerString,
38785
38775
  strings: strings,
38786
38776
  inline: true
38787
38777
  })));
38788
38778
  };
38789
- const styles$c = StyleSheet.create({
38779
+ const styles$b = StyleSheet.create({
38790
38780
  correct: {
38791
38781
  // WB green darkened by 18%
38792
38782
  backgroundColor: "#00880b"
@@ -38850,23 +38840,23 @@ class Marker extends React.Component {
38850
38840
  icon: iconNull
38851
38841
  };
38852
38842
  if (showCorrectness) {
38853
- iconStyles = [styles$b.markerGraded, showCorrectness === "correct" ? styles$b.markerCorrect : styles$b.markerIncorrect, isOpen && styles$b.markerSelected];
38843
+ iconStyles = [styles$a.markerGraded, showCorrectness === "correct" ? styles$a.markerCorrect : styles$a.markerIncorrect, isOpen && styles$a.markerSelected];
38854
38844
  args = _extends({}, args, {
38855
38845
  icon: showCorrectness === "correct" ? iconCheck : iconMinus
38856
38846
  });
38857
38847
  } else if (selectedAnswers && selectedAnswers.length > 0) {
38858
- iconStyles = [styles$b.markerFilled, isOpen && styles$b.markerSelected];
38848
+ iconStyles = [styles$a.markerFilled, isOpen && styles$a.markerSelected];
38859
38849
  } else if (isOpen) {
38860
- iconStyles = [styles$b.markerSelected];
38850
+ iconStyles = [styles$a.markerSelected];
38861
38851
  args = _extends({}, args, {
38862
38852
  icon: iconChevronDown,
38863
38853
  size: 8
38864
38854
  });
38865
38855
  } else if (showPulsate) {
38866
- iconStyles = [styles$b.markerPulsateBase, this._mounted && shouldReduceMotion() ? showPulsate && styles$b.markerUnfilledPulsateOnce : showPulsate && styles$b.markerUnfilledPulsateInfinite];
38856
+ iconStyles = [styles$a.markerPulsateBase, this._mounted && shouldReduceMotion() ? showPulsate && styles$a.markerUnfilledPulsateOnce : showPulsate && styles$a.markerUnfilledPulsateInfinite];
38867
38857
  }
38868
38858
  return /*#__PURE__*/React.createElement(View, {
38869
- style: [styles$b.markerIcon, iconStyles],
38859
+ style: [styles$a.markerIcon, iconStyles],
38870
38860
  ref: node => this._icon = node
38871
38861
  }, /*#__PURE__*/React.createElement(Icon, args));
38872
38862
  }
@@ -38888,7 +38878,7 @@ class Marker extends React.Component {
38888
38878
  const markerDisabled = showCorrectness === "correct";
38889
38879
  const active = hovered || focused;
38890
38880
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(View, {
38891
- style: [styles$b.marker, active && !markerDisabled && styles$b.markerActive],
38881
+ style: [styles$a.marker, active && !markerDisabled && styles$a.markerActive],
38892
38882
  "aria-label": markerDisabled ? this.context.strings.correctExcited : label
38893
38883
  }, this.renderIcon()), !!selected && showAnswer && /*#__PURE__*/React.createElement(AnswerPill, {
38894
38884
  selectedAnswers: selected,
@@ -38905,7 +38895,7 @@ Marker.contextType = PerseusI18nContext;
38905
38895
  Marker.defaultProps = {
38906
38896
  selected: []
38907
38897
  };
38908
- const styles$b = StyleSheet.create({
38898
+ const styles$a = StyleSheet.create({
38909
38899
  marker: {
38910
38900
  position: "absolute",
38911
38901
  backgroundColor: color.white,
@@ -39490,13 +39480,13 @@ class LabelImage extends React.Component {
39490
39480
  const promptString = isMobile ? multipleAnswers ? strings.tapMultiple : strings.tapSingle : multipleAnswers ? strings.clickMultiple : strings.clickSingle;
39491
39481
  const choicesString = strings.choices;
39492
39482
  return /*#__PURE__*/React.createElement("div", {
39493
- className: classNames$1("perseus-label-image-widget-instructions", css(styles$a.instructions))
39483
+ className: classNames$1("perseus-label-image-widget-instructions", css(styles$9.instructions))
39494
39484
  }, /*#__PURE__*/React.createElement("div", {
39495
- className: css(styles$a.instructionsCaption)
39485
+ className: css(styles$9.instructionsCaption)
39496
39486
  }, promptString, " ", !hideChoices && choicesString), !hideChoices && /*#__PURE__*/React.createElement("div", {
39497
- className: css(styles$a.instructionsChoices)
39487
+ className: css(styles$9.instructionsChoices)
39498
39488
  }, choices.map((choice, index) => /*#__PURE__*/React.createElement("div", {
39499
- className: css(styles$a.instructionsChoice),
39489
+ className: css(styles$9.instructionsChoice),
39500
39490
  key: index
39501
39491
  }, /*#__PURE__*/React.createElement(Renderer, {
39502
39492
  content: choice,
@@ -39514,20 +39504,20 @@ class LabelImage extends React.Component {
39514
39504
  activeMarkerIndex
39515
39505
  } = this.state;
39516
39506
  return /*#__PURE__*/React.createElement("div", null, this.renderInstructions(), /*#__PURE__*/React.createElement("div", {
39517
- className: css(styles$a.markersCanvas),
39507
+ className: css(styles$9.markersCanvas),
39518
39508
  style: {
39519
39509
  maxWidth: imageWidth,
39520
39510
  maxHeight: imageHeight
39521
39511
  }
39522
39512
  }, /*#__PURE__*/React.createElement("div", {
39523
- className: css(styles$a.imageContainer,
39513
+ className: css(styles$9.imageContainer,
39524
39514
  // Ensure image interaction is disabled while answer
39525
39515
  // choices popup is visible. This addresses specific
39526
39516
  // case where user may click on question image to
39527
39517
  // dismiss the popup. If the image is larger in size
39528
39518
  // than its rendered in the widget, this would
39529
39519
  // result in a zoom of the image.
39530
- activeMarkerIndex !== -1 && styles$a.imageInteractionDisabled)
39520
+ activeMarkerIndex !== -1 && styles$9.imageInteractionDisabled)
39531
39521
  }, /*#__PURE__*/React.createElement(context$1.Consumer, null, ({
39532
39522
  setAssetStatus
39533
39523
  }) => /*#__PURE__*/React.createElement(SvgImage, {
@@ -39552,7 +39542,7 @@ class LabelImage extends React.Component {
39552
39542
  }
39553
39543
  }
39554
39544
  LabelImage.contextType = PerseusI18nContext;
39555
- const styles$a = StyleSheet.create({
39545
+ const styles$9 = StyleSheet.create({
39556
39546
  instructions: {
39557
39547
  paddingBottom: 16
39558
39548
  },
@@ -39638,7 +39628,7 @@ class Placeholder extends React.Component {
39638
39628
  const {
39639
39629
  layout
39640
39630
  } = this.props;
39641
- const className = css(styles$9.card, styles$9.placeholder, layout === "horizontal" && styles$9.horizontalCard);
39631
+ const className = css(styles$8.card, styles$8.placeholder, layout === "horizontal" && styles$8.horizontalCard);
39642
39632
  const style = {
39643
39633
  width: this.props.width,
39644
39634
  height: this.props.height
@@ -39842,7 +39832,7 @@ class Draggable extends React.Component {
39842
39832
  // We need to keep backwards compatbility with rules specified directly
39843
39833
  // in CSS. Hence the hacky tacking on of manual classNames.
39844
39834
  // See sortable.less for details.
39845
- let className = css(styles$9.card, styles$9.draggable, layout === "horizontal" && styles$9.horizontalCard, layout === "vertical" && styles$9.verticalCard, type === ItemState.DRAGGING && styles$9.dragging, type === ItemState.DISABLED && styles$9.disabled, !includePadding && styles$9.unpaddedCard) + " " + ClassNames.INTERACTIVE + " perseus-sortable-draggable";
39835
+ let className = css(styles$8.card, styles$8.draggable, layout === "horizontal" && styles$8.horizontalCard, layout === "vertical" && styles$8.verticalCard, type === ItemState.DRAGGING && styles$8.dragging, type === ItemState.DISABLED && styles$8.disabled, !includePadding && styles$8.unpaddedCard) + " " + ClassNames.INTERACTIVE + " perseus-sortable-draggable";
39846
39836
  if (!includePadding) {
39847
39837
  className += " perseus-sortable-draggable-unpadded";
39848
39838
  }
@@ -40175,7 +40165,7 @@ class Sortable extends React.Component {
40175
40165
  } = this.props;
40176
40166
  // We need to keep backwards compatbility with rules specified directly
40177
40167
  // in CSS. See sortable.less for details.
40178
- const className = css(styles$9.sortable) + " perseus-sortable";
40168
+ const className = css(styles$8.sortable) + " perseus-sortable";
40179
40169
  const syncWidth = ((_this$props$constrain = this.props.constraints) == null ? void 0 : _this$props$constrain.width) || layout === "vertical";
40180
40170
  const syncHeight = ((_this$props$constrain2 = this.props.constraints) == null ? void 0 : _this$props$constrain2.height) || layout === "horizontal";
40181
40171
  _.each(this.state.items, function (item, i, items) {
@@ -40272,7 +40262,7 @@ Sortable.defaultProps = {
40272
40262
  linterContext: PerseusLinter.linterContextDefault,
40273
40263
  waitForTexRendererToLoad: true
40274
40264
  };
40275
- const styles$9 = StyleSheet.create({
40265
+ const styles$8 = StyleSheet.create({
40276
40266
  sortable: {
40277
40267
  boxSizing: "border-box",
40278
40268
  float: "left",
@@ -40458,25 +40448,25 @@ class Matcher extends React.Component {
40458
40448
  };
40459
40449
  const cellMarginPx = this.props.apiOptions.isMobile ? 8 : 5;
40460
40450
  return /*#__PURE__*/React.createElement("table", {
40461
- className: css(styles$8.widget) + " " + HACKY_CSS_CLASSNAME
40451
+ className: css(styles$7.widget) + " " + HACKY_CSS_CLASSNAME
40462
40452
  }, /*#__PURE__*/React.createElement("tbody", null, showLabels && /*#__PURE__*/React.createElement("tr", {
40463
- className: css(styles$8.row)
40453
+ className: css(styles$7.row)
40464
40454
  }, /*#__PURE__*/React.createElement("th", {
40465
- className: css(styles$8.column, styles$8.columnLabel)
40455
+ className: css(styles$7.column, styles$7.columnLabel)
40466
40456
  }, /*#__PURE__*/React.createElement(Renderer, {
40467
40457
  content: this.props.labels[0] || "...",
40468
40458
  linterContext: this.props.linterContext,
40469
40459
  strings: this.context.strings
40470
40460
  })), /*#__PURE__*/React.createElement("th", {
40471
- className: css(styles$8.column, styles$8.columnRight, styles$8.columnLabel)
40461
+ className: css(styles$7.column, styles$7.columnRight, styles$7.columnLabel)
40472
40462
  }, /*#__PURE__*/React.createElement(Renderer, {
40473
40463
  content: this.props.labels[1] || "...",
40474
40464
  linterContext: this.props.linterContext,
40475
40465
  strings: this.context.strings
40476
40466
  }))), /*#__PURE__*/React.createElement("tr", {
40477
- className: css(styles$8.row)
40467
+ className: css(styles$7.row)
40478
40468
  }, /*#__PURE__*/React.createElement("td", {
40479
- className: css(styles$8.column)
40469
+ className: css(styles$7.column)
40480
40470
  }, /*#__PURE__*/React.createElement(Sortable, {
40481
40471
  options: left,
40482
40472
  layout: "vertical",
@@ -40491,7 +40481,7 @@ class Matcher extends React.Component {
40491
40481
  ,
40492
40482
  ref: "left"
40493
40483
  })), /*#__PURE__*/React.createElement("td", {
40494
- className: css(styles$8.column, styles$8.columnRight)
40484
+ className: css(styles$7.column, styles$7.columnRight)
40495
40485
  }, /*#__PURE__*/React.createElement(Sortable, {
40496
40486
  options: right,
40497
40487
  layout: "vertical",
@@ -40520,7 +40510,7 @@ Matcher.defaultProps = {
40520
40510
  };
40521
40511
  const padding = 5;
40522
40512
  const border = "1px solid #444";
40523
- const styles$8 = StyleSheet.create({
40513
+ const styles$7 = StyleSheet.create({
40524
40514
  widget: {
40525
40515
  paddingTop: padding,
40526
40516
  maxWidth: "100%",
@@ -41062,7 +41052,7 @@ const getPromptJSON$7 = () => {
41062
41052
  return getUnsupportedPromptJSON("measurer");
41063
41053
  };
41064
41054
 
41065
- const _excluded$2 = ["imageUrl", "imageTop", "imageLeft"];
41055
+ const _excluded$1 = ["imageUrl", "imageTop", "imageLeft"];
41066
41056
  const defaultImage = {
41067
41057
  url: null,
41068
41058
  top: 0,
@@ -41174,7 +41164,7 @@ const propUpgrades = {
41174
41164
  imageTop,
41175
41165
  imageLeft
41176
41166
  } = v0props,
41177
- rest = _objectWithoutPropertiesLoose(v0props, _excluded$2);
41167
+ rest = _objectWithoutPropertiesLoose(v0props, _excluded$1);
41178
41168
  return _extends({}, rest, {
41179
41169
  image: {
41180
41170
  url: imageUrl,
@@ -41214,9 +41204,9 @@ function drawText(ctx, item) {
41214
41204
  if (item.value === null) {
41215
41205
  return;
41216
41206
  }
41217
- ctx.fillStyle = styles$7.fgColor;
41218
- ctx.font = styles$7.font;
41219
- ctx.fillText(item.value, item.pos[0] - styles$7.fontSizePx / 2 + 1, item.pos[1] + styles$7.fontSizePx / 2);
41207
+ ctx.fillStyle = styles$6.fgColor;
41208
+ ctx.font = styles$6.font;
41209
+ ctx.fillText(item.value, item.pos[0] - styles$6.fontSizePx / 2 + 1, item.pos[1] + styles$6.fontSizePx / 2);
41220
41210
  }
41221
41211
 
41222
41212
  /**
@@ -41237,16 +41227,16 @@ function drawText(ctx, item) {
41237
41227
  function drawDoubleLine(ctx, item) {
41238
41228
  // Outer line that forms both of the bond lines.
41239
41229
  let path = new Path2D();
41240
- ctx.lineWidth = 5 * styles$7.lineWidth;
41241
- ctx.strokeStyle = styles$7.fgColor;
41230
+ ctx.lineWidth = 5 * styles$6.lineWidth;
41231
+ ctx.strokeStyle = styles$6.fgColor;
41242
41232
  path.moveTo(item.startPos[0], item.startPos[1]);
41243
41233
  path.lineTo(item.endPos[0], item.endPos[1]);
41244
41234
  ctx.stroke(path);
41245
41235
 
41246
41236
  // Inner white line that separates the two bond lines.
41247
41237
  path = new Path2D();
41248
- ctx.lineWidth = 3 * styles$7.lineWidth;
41249
- ctx.strokeStyle = styles$7.bgColor;
41238
+ ctx.lineWidth = 3 * styles$6.lineWidth;
41239
+ ctx.strokeStyle = styles$6.bgColor;
41250
41240
  path.moveTo(item.startPos[0], item.startPos[1]);
41251
41241
  path.lineTo(item.endPos[0], item.endPos[1]);
41252
41242
  ctx.stroke(path);
@@ -41273,16 +41263,16 @@ function drawTripleLine(ctx, item) {
41273
41263
 
41274
41264
  // Outer line that will form the two outer bond lines.
41275
41265
  let path = new Path2D();
41276
- ctx.lineWidth = 7 * styles$7.lineWidth;
41277
- ctx.strokeStyle = styles$7.fgColor;
41266
+ ctx.lineWidth = 7 * styles$6.lineWidth;
41267
+ ctx.strokeStyle = styles$6.fgColor;
41278
41268
  path.moveTo(item.startPos[0], item.startPos[1]);
41279
41269
  path.lineTo(item.endPos[0], item.endPos[1]);
41280
41270
  ctx.stroke(path);
41281
41271
 
41282
41272
  // Middle white line that separates the bonds
41283
41273
  path = new Path2D();
41284
- ctx.lineWidth = 5 * styles$7.lineWidth;
41285
- ctx.strokeStyle = styles$7.bgColor;
41274
+ ctx.lineWidth = 5 * styles$6.lineWidth;
41275
+ ctx.strokeStyle = styles$6.bgColor;
41286
41276
  path.moveTo(item.startPos[0], item.startPos[1]);
41287
41277
  path.lineTo(item.endPos[0], item.endPos[1]);
41288
41278
  ctx.stroke(path);
@@ -41304,8 +41294,8 @@ function drawTripleLine(ctx, item) {
41304
41294
  */
41305
41295
  function drawLine(ctx, item) {
41306
41296
  const path = new Path2D();
41307
- ctx.lineWidth = styles$7.lineWidth;
41308
- ctx.strokeStyle = styles$7.fgColor;
41297
+ ctx.lineWidth = styles$6.lineWidth;
41298
+ ctx.strokeStyle = styles$6.fgColor;
41309
41299
  path.moveTo(item.startPos[0], item.startPos[1]);
41310
41300
  path.lineTo(item.endPos[0], item.endPos[1]);
41311
41301
  ctx.stroke(path);
@@ -41357,7 +41347,7 @@ function compareElements(item0, item1) {
41357
41347
  function draw(ctx, items) {
41358
41348
  items.sort(compareElements).forEach(drawItem(ctx));
41359
41349
  }
41360
- const styles$7 = {
41350
+ const styles$6 = {
41361
41351
  bgColor: "rgb(255, 255, 255)",
41362
41352
  fgColor: "rgb(0, 0, 0)",
41363
41353
  fontSizePx: 12,
@@ -43477,7 +43467,7 @@ class PhetSimulation extends React.Component {
43477
43467
  // http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
43478
43468
  const sandboxProperties = "allow-same-origin allow-scripts";
43479
43469
  return /*#__PURE__*/React.createElement(View, {
43480
- style: styles$6.widgetContainer
43470
+ style: styles$5.widgetContainer
43481
43471
  }, this.state.banner !== null &&
43482
43472
  /*#__PURE__*/
43483
43473
  // TODO(anna): Make this banner focusable
@@ -43490,12 +43480,12 @@ class PhetSimulation extends React.Component {
43490
43480
  kind: this.state.banner.kind,
43491
43481
  text: this.state.banner.message
43492
43482
  })), /*#__PURE__*/React.createElement(View, {
43493
- style: styles$6.iframeContainer
43483
+ style: styles$5.iframeContainer
43494
43484
  }, /*#__PURE__*/React.createElement("iframe", {
43495
43485
  ref: this.iframeRef,
43496
43486
  title: this.props.description,
43497
43487
  sandbox: sandboxProperties,
43498
- className: css(styles$6.iframeResponsive),
43488
+ className: css(styles$5.iframeResponsive),
43499
43489
  src: (_this$state$url = this.state.url) == null ? void 0 : _this$state$url.toString(),
43500
43490
  allow: "fullscreen"
43501
43491
  })), this.state.url !== null && /*#__PURE__*/React.createElement(IconButton, {
@@ -43528,7 +43518,7 @@ const makeSafeUrl = (urlString, locale) => {
43528
43518
  url.searchParams.set("locale", locale);
43529
43519
  return url;
43530
43520
  };
43531
- const styles$6 = StyleSheet.create({
43521
+ const styles$5 = StyleSheet.create({
43532
43522
  widgetContainer: {
43533
43523
  borderRadius: 6,
43534
43524
  borderWidth: 1,
@@ -44446,7 +44436,7 @@ class PythonProgram extends React.Component {
44446
44436
  // creator "went wild".
44447
44437
  // http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
44448
44438
  return /*#__PURE__*/React.createElement(View, {
44449
- style: styles$5.container
44439
+ style: styles$4.container
44450
44440
  }, /*#__PURE__*/React.createElement("iframe", {
44451
44441
  sandbox: sandboxOptions,
44452
44442
  src: url,
@@ -44458,7 +44448,7 @@ class PythonProgram extends React.Component {
44458
44448
  PythonProgram.defaultProps = {
44459
44449
  height: 400
44460
44450
  };
44461
- const styles$5 = StyleSheet.create({
44451
+ const styles$4 = StyleSheet.create({
44462
44452
  container: {
44463
44453
  margin: "auto",
44464
44454
  width: "100%"
@@ -44951,7 +44941,7 @@ const VideoTranscriptLink = props => {
44951
44941
  var _result$data;
44952
44942
  const video = (_result$data = result.data) == null ? void 0 : _result$data.video;
44953
44943
  return /*#__PURE__*/React.createElement(View, {
44954
- style: styles$4.transcriptLink
44944
+ style: styles$3.transcriptLink
44955
44945
  }, /*#__PURE__*/React.createElement(Text$2, null, video == null ? void 0 : video.title), /*#__PURE__*/React.createElement(Strut, {
44956
44946
  size: 10
44957
44947
  }), /*#__PURE__*/React.createElement(Link, {
@@ -44968,7 +44958,7 @@ const VideoTranscriptLink = props => {
44968
44958
  return /*#__PURE__*/React.createElement(View, null, strings.somethingWrong);
44969
44959
  }
44970
44960
  };
44971
- const styles$4 = StyleSheet.create({
44961
+ const styles$3 = StyleSheet.create({
44972
44962
  transcriptLink: {
44973
44963
  flexDirection: "row",
44974
44964
  width: "100%",
@@ -45086,7 +45076,7 @@ const init = function init() {
45086
45076
 
45087
45077
  // This file is processed by a Rollup plugin (replace) to inject the production
45088
45078
  const libName = "@khanacademy/perseus";
45089
- const libVersion = "46.0.1";
45079
+ const libVersion = "47.0.0";
45090
45080
  addLibraryVersionToPerseusDebug(libName, libVersion);
45091
45081
 
45092
45082
  const apiVersion = {
@@ -45298,7 +45288,7 @@ class HintRenderer extends React.Component {
45298
45288
  const {
45299
45289
  isMobile
45300
45290
  } = apiOptions;
45301
- const classNames = classNames$1("hint", !isMobile && "perseus-hint-renderer", isMobile && css(styles$3.newHint), isMobile && lastRendered && css(styles$3.lastRenderedNewHint), lastHint && "last-hint", lastRendered && "last-rendered", className);
45291
+ const classNames = classNames$1("hint", !isMobile && "perseus-hint-renderer", isMobile && css(styles$2.newHint), isMobile && lastRendered && css(styles$2.lastRenderedNewHint), lastHint && "last-hint", lastRendered && "last-rendered", className);
45302
45292
 
45303
45293
  // TODO(charlie): Allowing `customKeypad` would require that we
45304
45294
  // extend `ItemRenderer` to support nested inputs in the `HintsRenderer`.
@@ -45343,7 +45333,7 @@ HintRenderer.contextType = PerseusI18nContext;
45343
45333
  HintRenderer.defaultProps = {
45344
45334
  linterContext: PerseusLinter.linterContextDefault
45345
45335
  };
45346
- const styles$3 = StyleSheet.create({
45336
+ const styles$2 = StyleSheet.create({
45347
45337
  newHint: {
45348
45338
  marginBottom: 1.5 * baseUnitPx,
45349
45339
  borderLeftColor: gray97,
@@ -45516,14 +45506,14 @@ class HintsRenderer extends React.Component {
45516
45506
  } = apiOptions;
45517
45507
  const showGetAnotherHint = getAnotherHint && hintsVisible > 0 && hintsVisible < this.props.hints.length;
45518
45508
  const hintRatioCopy = `(${hintsVisible}/${this.props.hints.length})`;
45519
- const classNames = classNames$1(this.props.className, apiOptions.isMobile && hintsVisible > 0 && css(styles$2.mobileHintStylesHintsRenderer));
45509
+ const classNames = classNames$1(this.props.className, apiOptions.isMobile && hintsVisible > 0 && css(styles$1.mobileHintStylesHintsRenderer));
45520
45510
  return /*#__PURE__*/React.createElement("div", {
45521
45511
  className: classNames
45522
45512
  }, apiOptions.isMobile && hintsVisible > 0 && /*#__PURE__*/React.createElement("div", {
45523
- className: css(styles$2.mobileHintStylesHintTitle, sharedStyles.responsiveLabel)
45513
+ className: css(styles$1.mobileHintStylesHintTitle, sharedStyles.responsiveLabel)
45524
45514
  }, this.context.strings.hints), hints, showGetAnotherHint && /*#__PURE__*/React.createElement("button", {
45525
45515
  rel: "button",
45526
- className: css(styles$2.linkButton, styles$2.getAnotherHintButton, apiOptions.isMobile && styles$2.mobileHintStylesGetAnotherHintButton),
45516
+ className: css(styles$1.linkButton, styles$1.getAnotherHintButton, apiOptions.isMobile && styles$1.mobileHintStylesGetAnotherHintButton),
45527
45517
  onClick: evt => {
45528
45518
  evt.preventDefault();
45529
45519
  evt.stopPropagation();
@@ -45532,9 +45522,9 @@ class HintsRenderer extends React.Component {
45532
45522
  }
45533
45523
  }
45534
45524
  }, /*#__PURE__*/React.createElement("span", {
45535
- className: css(styles$2.plusText, apiOptions.isMobile && styles$2.mobileHintStylesPlusText)
45525
+ className: css(styles$1.plusText, apiOptions.isMobile && styles$1.mobileHintStylesPlusText)
45536
45526
  }, "+"), /*#__PURE__*/React.createElement("span", {
45537
- className: css(styles$2.getAnotherHintText)
45527
+ className: css(styles$1.getAnotherHintText)
45538
45528
  }, this.context.strings.getAnotherHint, " ", hintRatioCopy)));
45539
45529
  }
45540
45530
  }
@@ -45543,7 +45533,7 @@ HintsRenderer.defaultProps = {
45543
45533
  linterContext: PerseusLinter.linterContextDefault
45544
45534
  };
45545
45535
  const hintIndentation = baseUnitPx + hintBorderWidth;
45546
- const styles$2 = StyleSheet.create({
45536
+ const styles$1 = StyleSheet.create({
45547
45537
  // eslint-disable-next-line react-native/no-unused-styles
45548
45538
  rendererMargins: {
45549
45539
  marginTop: baseUnitPx
@@ -45952,7 +45942,7 @@ class ServerItemRenderer extends React.Component {
45952
45942
  // mobile hint styles, which are flush to the left.
45953
45943
  // NOTE(charlie): We may still want to apply this
45954
45944
  // padding for desktop exercises.
45955
- apiOptions.isMobile ? undefined : css(styles$1.hintsContainer)
45945
+ apiOptions.isMobile ? undefined : css(styles.hintsContainer)
45956
45946
  }, hintsRenderer)));
45957
45947
  }
45958
45948
  }
@@ -45963,7 +45953,7 @@ ServerItemRenderer.defaultProps = {
45963
45953
  linterContext: PerseusLinter.linterContextDefault,
45964
45954
  onRendered: isRendered => {}
45965
45955
  };
45966
- const styles$1 = StyleSheet.create({
45956
+ const styles = StyleSheet.create({
45967
45957
  hintsContainer: {
45968
45958
  marginLeft: 50
45969
45959
  }
@@ -46068,1144 +46058,6 @@ const traverse = function traverse(rendererOptions, contentCallback, widgetCallb
46068
46058
  return traverseRenderer$1(rendererOptions, contentCallback, deepCallbackFor(contentCallback, widgetCallback, optionsCallback), optionsCallback);
46069
46059
  };
46070
46060
 
46071
- /**
46072
- * Utility functions for constructing and inferring multi-item shapes.
46073
- *
46074
- * A shape is an object that serves as a runtime type declaration: it specifies
46075
- * a tree structure for a particular class of multi-item. See shape-types.js
46076
- * for further discussion.
46077
- *
46078
- * This module allows you to construct arbitrary Shape trees, by combining
46079
- * leaf node shapes like `content` and `hint` into composite shapes like
46080
- * `arrayOf(shape({foo: content, bar: hint}))`.
46081
- */
46082
-
46083
- /**
46084
- * These tools allow you to construct arbirtary shapes, by combining simple
46085
- * leaf shapes like `content` and `hint` into composite shapes like
46086
- * `arrayOf(shape({question: content, hints: arrayOf(hint)}))`.
46087
- */
46088
- const contentShape = {
46089
- type: "content"
46090
- };
46091
- const hintShape = {
46092
- type: "hint"
46093
- };
46094
- const tagsShape = {
46095
- type: "tags"
46096
- };
46097
- const buildArrayShape = elementShape => ({
46098
- type: "array",
46099
- elementShape
46100
- });
46101
- const buildObjectShape = shape => ({
46102
- type: "object",
46103
- shape
46104
- });
46105
- const hintsShape = buildArrayShape(hintShape);
46106
- var shapes = {
46107
- content: contentShape,
46108
- hint: hintShape,
46109
- hints: hintsShape,
46110
- tags: tagsShape,
46111
- arrayOf: buildArrayShape,
46112
- shape: buildObjectShape
46113
- };
46114
-
46115
- /**
46116
- * Utility functions for manipulating multi-item-shaped trees.
46117
- *
46118
- * Multi-items are trees! But we also often have other trees that are shaped
46119
- * like multi-items - for example, if we map a multi-item tree into a tree of
46120
- * renderer info and state, and then map that again into a tree of just the
46121
- * renderer nodes, like we do in MultiRenderer. See tree-types.js for further
46122
- * discussion.
46123
- *
46124
- * These functions enable us to manipulate generic multi-item-shaped trees,
46125
- * regardless of what type of data they contain at their leaves. You can use
46126
- * the mapper functions to transform a tree into another tree of the same
46127
- * shape, or to discover all the nodes of a particular type.
46128
- *
46129
- * We expose two simple mapper functions (mapContentNodes and mapHintNodes),
46130
- * and also a more complex interface for creating a mapping over all of a
46131
- * tree's node types simultaneously:
46132
- *
46133
- * `buildMapper()` returns a TreeMapper object that allows you to build your
46134
- * mapper object one node type at a time. Then, you can execute your mapping by
46135
- * calling the `mapTree` method.
46136
- *
46137
- * For example:
46138
- * const renderers = buildMapper()
46139
- * .setContentMapper(this.renderContentNode)
46140
- * .setHintMapper(this.renderHintNode)
46141
- * .setTagsMapper(this.renderTagsNode)
46142
- * .setArrayMapper(this.hideSkippedQuestions)
46143
- * .mapTree(tree, shape);
46144
- *
46145
- * This will copy the given tree, apply the given transformations to the
46146
- * content, hint, and array nodes respectively, and return the resulting tree.
46147
- *
46148
- * For node types whose mappers aren't specified, we default to the identity
46149
- * function. (This builder interface enables us to implement that default
46150
- * behavior in a provably type-safe way, while not requiring the call site to
46151
- * be aware of all the node types. Hooray!)
46152
- *
46153
- * The call to `setArrayMapper` must come last, because the array mapper's
46154
- * argument types depend on the other mappers' types. See ArrayMapper for more
46155
- * details.
46156
- *
46157
- * WARNING: These functions trust that the provided tree conforms to the
46158
- * provided shape. If not, behavior is undefined and may not respect the type
46159
- * signatures specified here.
46160
- */
46161
-
46162
- /**
46163
- * The sequence of edges that lead to a particular node in a Tree.
46164
- * Elements can be `string` to correspond to an ObjectNode key, or `number` to
46165
- * correspond to an ArrayNode index.
46166
- */
46167
-
46168
- /**
46169
- * These are function interfaces for mapping over various types of tree nodes.
46170
- *
46171
- * ArrayMapper is a bit more complicated than the leaf node mappers. It's
46172
- * executed in the context of a `mapTree` call, after we've finished mapping
46173
- * its child nodes, so the function has access to both the resulting array
46174
- * (with mapped elements) and the original array (with the original untouched
46175
- * elements).
46176
- *
46177
- * The ArrayMapper then has the opportunity to apply a final transformation to
46178
- * the resulting array, like filtering certain elements or (in the hacky
46179
- * MultiRenderer case) attaching a `renderHints` method to arrays of hint
46180
- * renderers :)
46181
- *
46182
- * This is why `TreeMapper#setArrayMapper` must be called last: ArrayMapper's
46183
- * types depend on the ContentMapper and HintMapper's types. And, since you can
46184
- * only specify one mapper at a time in this builder interface (which is
46185
- * necessary to provide default mappers in a type-safe way), you need your
46186
- * dependencies to already be in place by the time you call `setArrayMapper`.
46187
- * Otherwise, we'd have to set the ArrayMapper and *hope* that you *eventually*
46188
- * provide a compatible ContentMapper and HintMapper, which is difficult to
46189
- * prove at compile time.
46190
- *
46191
- * There's no ObjectMapper here, but not for any particular reason. We just
46192
- * don't have a use case for it yet, so we haven't built it yet.
46193
- */
46194
-
46195
- /**
46196
- * A TreeMapper is a collection of node mappers, which, together, compose the
46197
- * behavior for mapping over an entire tree.
46198
- *
46199
- * This serves as the interface for the two TreeMapper classes, including both
46200
- * the internal mapper properties that we care about, and the `mapTree`
46201
- * function that the call site will use.
46202
- */
46203
-
46204
- /**
46205
- * This is a TreeMapper that only has mappers specified for its leaf nodes; its
46206
- * array mapper is the identity function.
46207
- *
46208
- * This is the TreeMapper initially returned by `buildMapper`. It allows you to
46209
- * change the types of your ContentMapper and HintMapper, which is safe because
46210
- * none of the other mappers that depend on those types (aka ArrayMapper) have
46211
- * been specified yet. (Or, more specifically, the ArrayMapper is currently
46212
- * `identity`, which can trivially vary with the ContentMapper and HintMapper's
46213
- * types.)
46214
- *
46215
- * Once you call `setArrayMapper`, however, we move to the other class:
46216
- * TreeMapperForLeavesAndCollections.
46217
- */
46218
- class TreeMapperJustForLeaves {
46219
- constructor(content, hint, tags) {
46220
- this.content = void 0;
46221
- this.hint = void 0;
46222
- this.tags = void 0;
46223
- this.array = void 0;
46224
- this.content = content;
46225
- this.hint = hint;
46226
- this.tags = tags;
46227
- this.array = identity;
46228
- }
46229
- setContentMapper(newContentMapper) {
46230
- return new TreeMapperJustForLeaves(newContentMapper, this.hint, this.tags);
46231
- }
46232
- setHintMapper(newHintMapper) {
46233
- return new TreeMapperJustForLeaves(this.content, newHintMapper, this.tags);
46234
- }
46235
- setTagsMapper(newTagsMapper) {
46236
- return new TreeMapperJustForLeaves(this.content, this.hint, newTagsMapper);
46237
- }
46238
- setArrayMapper(newArrayMapper) {
46239
- return new TreeMapperForLeavesAndCollections(this.content, this.hint, this.tags, newArrayMapper);
46240
- }
46241
-
46242
- // @ts-expect-error - TS2315 - Type 'Tree' is not generic. | TS2315 - Type 'Tree' is not generic.
46243
- mapTree(tree, shape) {
46244
- return mapTree(tree, shape, [], this);
46245
- }
46246
- }
46247
-
46248
- /**
46249
- * This is a TreeMapper that already has an ArrayMapper specified, so its
46250
- * ContentMapper and HintMapper are now locked in.
46251
- */
46252
- class TreeMapperForLeavesAndCollections {
46253
- constructor(content, hint, tags, array) {
46254
- this.content = void 0;
46255
- this.hint = void 0;
46256
- this.tags = void 0;
46257
- this.array = void 0;
46258
- this.content = content;
46259
- this.hint = hint;
46260
- this.tags = tags;
46261
- this.array = array;
46262
- }
46263
- setArrayMapper(newArrayMapper) {
46264
- return new TreeMapperForLeavesAndCollections(this.content, this.hint, this.tags, newArrayMapper);
46265
- }
46266
-
46267
- // @ts-expect-error - TS2315 - Type 'Tree' is not generic. | TS2315 - Type 'Tree' is not generic.
46268
- mapTree(tree, shape) {
46269
- return mapTree(tree, shape, [], this);
46270
- }
46271
- }
46272
- function identity(x) {
46273
- return x;
46274
- }
46275
-
46276
- /**
46277
- * Return a new TreeMapper that will perform a no-op transformation on an input
46278
- * tree. To make it useful, chain any combination of `setContentMapper`,
46279
- * `setHintMapper`, `setTagMapper`, and `setArrayMapper` to specify
46280
- * transformations for the individual node types.
46281
- */
46282
- // @ts-expect-error - TS2300 - Duplicate identifier 'C'. | TS2300 - Duplicate identifier 'H'. | TS2300 - Duplicate identifier 'T'.
46283
- function buildMapper() {
46284
- return new TreeMapperJustForLeaves(identity, identity, identity);
46285
- }
46286
-
46287
- /**
46288
- * Copy the given tree, apply the corresponding transformation specified in the
46289
- * TreeMapper to each node, and return the resulting tree.
46290
- */
46291
- function mapTree(
46292
- // @ts-expect-error - TS2315 - Type 'Tree' is not generic.
46293
- tree, shape, path, mappers
46294
- // @ts-expect-error - TS2315 - Type 'Tree' is not generic.
46295
- ) {
46296
- // We trust the shape of the multi-item to match the shape provided at
46297
- // runtime. Therefore, in each shape branch, we cast the node to `any` and
46298
- // reinterpret it as the expected node type.
46299
- if (shape.type === "content") {
46300
- const content = tree;
46301
- return mappers.content(content, shape, path);
46302
- }
46303
- if (shape.type === "hint") {
46304
- const hint = tree;
46305
- return mappers.hint(hint, shape, path);
46306
- }
46307
- if (shape.type === "tags") {
46308
- const tags = tree;
46309
- return mappers.tags(tags, shape, path);
46310
- }
46311
- if (shape.type === "array") {
46312
- // @ts-expect-error - TS2315 - Type 'ArrayNode' is not generic.
46313
- const array = tree;
46314
- if (!Array.isArray(array)) {
46315
- throw new PerseusError(`Invalid object of type "${typeof array}" found at path ` +
46316
- // @ts-expect-error - TS2769 - No overload matches this call.
46317
- `${["<root>"].concat(path).join(".")}. Expected array.`, Errors.Internal);
46318
- }
46319
- const elementShape = shape.elementShape;
46320
- // @ts-expect-error - TS2315 - Type 'ArrayNode' is not generic.
46321
- const mappedElements = array.map((inner, i) => mapTree(inner, elementShape, path.concat(i), mappers));
46322
- return mappers.array(mappedElements, array, shape, path);
46323
- }
46324
- if (shape.type === "object") {
46325
- const object = tree;
46326
- if (object && typeof object !== "object") {
46327
- throw new PerseusError(`Invalid object of type "${typeof object}" found at ` +
46328
- // @ts-expect-error - TS2769 - No overload matches this call.
46329
- `path ${["<root>"].concat(path).join(".")}. Expected ` + `"object" type.`, Errors.InvalidInput);
46330
- }
46331
- const valueShapes = shape.shape;
46332
- if (!valueShapes) {
46333
- throw new PerseusError(`Unexpected shape ${JSON.stringify(shape)} at path ` +
46334
- // @ts-expect-error - TS2769 - No overload matches this call.
46335
- `${["<root>"].concat(path).join(".")}.`, Errors.InvalidInput);
46336
- }
46337
- const newObject = {};
46338
- Object.keys(valueShapes).forEach(key => {
46339
- if (!(key in object)) {
46340
- throw new PerseusError(`Key "${key}" is missing from shape at path ` +
46341
- // @ts-expect-error - TS2769 - No overload matches this call.
46342
- `${["<root>"].concat(path).join(".")}.`, Errors.InvalidInput);
46343
- }
46344
- newObject[key] = mapTree(object[key], valueShapes[key], path.concat(key), mappers);
46345
- });
46346
- return newObject;
46347
- }
46348
- throw new PerseusError(
46349
- // @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
46350
- `unexpected shape type ${shape.type}`, Errors.InvalidInput);
46351
- }
46352
-
46353
- /**
46354
- * Utility functions for constructing and manipulating multi-items.
46355
- *
46356
- * These functions apply *specifically* to Items and ItemTrees - things that
46357
- * actually semantically *are* multi-items. For more general functions for
46358
- * traversing and manipulating *anything* shaped like a multi-item (like a
46359
- * renderer tree or a score tree or, well, a multi-item), see trees.js.
46360
- */
46361
- /**
46362
- * Return a semantically empty ItemTree that conforms to the given shape.
46363
- *
46364
- * - An empty content node has an empty content string and no widgets/images.
46365
- * - An empty hint node has an empty content string and no widgets/images.
46366
- * - An empty array node has no elements.
46367
- * - An empty object node has a semantically empty node for each of its keys.
46368
- * (That is, we recursively call buildEmptyItemTreeForShape for each key.)
46369
- */
46370
- function buildEmptyItemTreeForShape(shape) {
46371
- if (shape.type === "content") {
46372
- return {
46373
- __type: "content",
46374
- content: "",
46375
- images: {},
46376
- widgets: {}
46377
- };
46378
- }
46379
- if (shape.type === "hint") {
46380
- return {
46381
- __type: "hint",
46382
- replace: false,
46383
- content: "",
46384
- images: {},
46385
- widgets: {}
46386
- };
46387
- }
46388
- if (shape.type === "tags") {
46389
- return [];
46390
- }
46391
- if (shape.type === "array") {
46392
- return [];
46393
- }
46394
- if (shape.type === "object") {
46395
- const valueShapes = shape.shape;
46396
- const object = {};
46397
- Object.keys(valueShapes).forEach(key => {
46398
- object[key] = buildEmptyItemTreeForShape(valueShapes[key]);
46399
- });
46400
- return object;
46401
- }
46402
- throw new PerseusError(
46403
- // @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
46404
- `unexpected shape type ${shape.type}`, Errors.InvalidInput);
46405
- }
46406
-
46407
- /**
46408
- * Return a semantically empty Item that conforms to the given shape.
46409
- *
46410
- * - An empty content node has an empty content string and no widgets/images.
46411
- * - An empty hint node has an empty content string and no widgets/images.
46412
- * - An empty array node has no elements.
46413
- * - An empty object node has a semantically empty node for each of its keys.
46414
- * (That is, we recursively call buildEmptyItemTreeForShape for each key.)
46415
- */
46416
- function buildEmptyItemForShape(shape) {
46417
- return treeToItem(buildEmptyItemTreeForShape(shape));
46418
- }
46419
-
46420
- /**
46421
- * Given an Item and its Shape, yield all of its content nodes to the callback.
46422
- */
46423
- function findContentNodesInItem$2(item, shape, callback) {
46424
- const itemTree = itemToTree(item);
46425
- buildMapper().setContentMapper(callback).mapTree(itemTree, shape);
46426
- }
46427
-
46428
- /**
46429
- * Given an Item and its Shape, yield all of its hint nodes to the callback.
46430
- */
46431
- function findHintNodesInItem(item, shape, callback) {
46432
- const itemTree = itemToTree(item);
46433
- buildMapper().setHintMapper(callback).mapTree(itemTree, shape);
46434
- }
46435
-
46436
- /**
46437
- * Given an ItemTree, return a Shape that it conforms to.
46438
- *
46439
- * The Shape might not be complete or correct Shape that this Item was designed
46440
- * for. If you have access to the intended Shape, use that instead.
46441
- */
46442
- function inferItemShape$2(item) {
46443
- const itemTree = itemToTree(item);
46444
- return inferItemTreeShape(itemTree);
46445
- }
46446
- function inferItemTreeShape(node) {
46447
- if (Array.isArray(node)) {
46448
- if (node.length) {
46449
- if (typeof node[0] === "string") {
46450
- // There's no ItemTree that can manifest as a string.
46451
- // So, an array of strings must be a TagsNode, not ArrayNode.
46452
- return shapes.tags;
46453
- }
46454
- // Otherwise, assume that this is a valid ArrayNode, and
46455
- // therefore the shape of the first element applies to all
46456
- // elements in the array.
46457
- return shapes.arrayOf(inferItemTreeShape(node[0]));
46458
- }
46459
- // The array is empty, so we arbitrarily guess that it's a content
46460
- // array. As discussed in the docstring, this might be incorrect,
46461
- // and you shouldn't depend on it.
46462
- return shapes.arrayOf(shapes.content);
46463
- }
46464
- if (
46465
- // TODO(mdr): Remove #LegacyContentNode support.
46466
- typeof node === "object" && (node.__type === "content" || node.__type === "item")) {
46467
- return shapes.content;
46468
- }
46469
- if (typeof node === "object" && node.__type === "hint") {
46470
- return shapes.hint;
46471
- }
46472
- if (typeof node === "object") {
46473
- const valueShapes = {};
46474
- Object.keys(node).forEach(key => {
46475
- valueShapes[key] = inferItemTreeShape(node[key]);
46476
- });
46477
- return shapes.shape(valueShapes);
46478
- }
46479
- throw new PerseusError(`unexpected multi-item node ${JSON.stringify(node)}`, Errors.InvalidInput);
46480
- }
46481
-
46482
- /**
46483
- * Convert the given Item to an ItemTree, by unwrapping the `_multi` key.
46484
- */
46485
- function itemToTree(item) {
46486
- return item._multi;
46487
- }
46488
-
46489
- /**
46490
- * Convert the given ItemTree to an Item, by wrapping it in the `_multi` key.
46491
- */
46492
- function treeToItem(node) {
46493
- return {
46494
- _multi: node
46495
- };
46496
- }
46497
-
46498
- var isObject$1 = function isObject(obj) {
46499
- return obj === Object(obj);
46500
- };
46501
- var merge = function merge() {
46502
- var obj = {};
46503
- for (var i = 0; i < arguments.length; i++) {
46504
- var source = arguments[i];
46505
- if (source) {
46506
- for (var prop in source) {
46507
- obj[prop] = source[prop];
46508
- }
46509
- }
46510
- }
46511
- return obj;
46512
- };
46513
- var clone = function clone(obj) {
46514
- if (!isObject$1(obj)) {
46515
- return obj;
46516
- }
46517
- return Array.isArray(obj) ? obj.slice() : merge(obj);
46518
- };
46519
-
46520
- /* TODO batch *all* mutations
46521
- * idea: freeze / thaw implementations for all types
46522
- * lens constructor thaws, freeze delegates to type's freeze
46523
- */
46524
-
46525
- // equivalents, without requiring it
46526
- // find the implementation to use for a given object
46527
- var dispatch = function dispatch(x) {
46528
- if (Array.isArray(x)) {
46529
- return arr;
46530
- } else if (isObject$1(x)) {
46531
- return obj;
46532
- } else if (typeof x === "string") {
46533
- return str;
46534
- }
46535
- };
46536
-
46537
- // This is underscore with a different name
46538
- var lens = function lens(obj) {
46539
- if (obj instanceof lens) {
46540
- return obj;
46541
- }
46542
- if (!(this instanceof lens)) {
46543
- return new lens(obj);
46544
- }
46545
- var ops = dispatch(obj);
46546
- this._wrapped = ops.thaw ? ops.thaw(obj) : obj;
46547
- };
46548
- lens.prototype.freeze = function () {
46549
- var obj = this._wrapped;
46550
- var ops = dispatch(obj);
46551
- return ops.freeze ? ops.freeze(obj) : obj;
46552
- };
46553
- lens.prototype.zoom = function (lensArr) {
46554
- if (this._zoomStack === undefined) {
46555
- this._zoomStack = [];
46556
- }
46557
- this._zoomStack.push({
46558
- zoom: lensArr,
46559
- wrapped: this._wrapped
46560
- });
46561
- this._wrapped = lens(this._wrapped).get(lensArr);
46562
- return this;
46563
- };
46564
- lens.prototype.deZoom = function () {
46565
- var frame = this._zoomStack.pop();
46566
- this._wrapped = lens(frame.wrapped).set(frame.zoom, this._wrapped).freeze();
46567
- return this;
46568
- };
46569
- lens.prototype.get = function (lensArr) {
46570
- var obj = this._wrapped;
46571
- for (var i = 0; i < lensArr.length; i++) {
46572
- obj = dispatch(obj).get(obj, lensArr[i]);
46573
- }
46574
- return obj;
46575
- };
46576
- lens.prototype.mod = function (lensArr, f) {
46577
- var obj = this._wrapped;
46578
- var newObj = clone(obj);
46579
- var ops = dispatch(obj);
46580
- if (lensArr.length === 0) {
46581
- this._wrapped = f(this._wrapped);
46582
- } else if (lensArr.length === 1) {
46583
- this._wrapped = ops.mod(newObj, lensArr[0], f);
46584
- } else {
46585
- var monocle = lensArr[0];
46586
- var shortLens = lensArr.slice(1);
46587
-
46588
- // newObj = ops.mod(obj[monocle], shortLens, f);
46589
-
46590
- newObj[monocle] = lens(obj[monocle]).mod(shortLens, f).freeze();
46591
- this._wrapped = newObj;
46592
- }
46593
- return this;
46594
- };
46595
-
46596
- // TODO - move to individual files
46597
- lens.prototype.merge = function (lensArr, props) {
46598
- this._wrapped = lens(this._wrapped).mod(lensArr, function (oldProps) {
46599
- return merge(oldProps, props);
46600
- }).freeze();
46601
- return this;
46602
- };
46603
-
46604
- // Lens must have length >= 1 or there would be nothing to return
46605
- lens.prototype.del = function (lensArr) {
46606
- var obj = this._wrapped;
46607
- var ops = dispatch(obj);
46608
- if (lensArr.length === 1) {
46609
- this._wrapped = ops.del(obj, lensArr[0]);
46610
- } else {
46611
- var monocle = lensArr[0];
46612
- var shortLens = lensArr.slice(1);
46613
- var newObj = clone(obj);
46614
- newObj[monocle] = lens(obj[monocle]).del(shortLens).freeze();
46615
- this._wrapped = newObj;
46616
- }
46617
- return this;
46618
- };
46619
- lens.prototype.set = function (lensArr, set) {
46620
- return this.mod(lensArr, function () {
46621
- return set;
46622
- });
46623
- };
46624
-
46625
- // ARRAY FUNCTIONS
46626
-
46627
- const arr = {
46628
- get: function (arr, monocle) {
46629
- return arr[monocle];
46630
- },
46631
- set: function (arr, monocle, val) {
46632
- var newArr = arr.splice();
46633
- newArr[monocle] = val;
46634
- return newArr;
46635
- },
46636
- mod: function (arr, monocle, f) {
46637
- var newArr = arr.slice();
46638
- newArr[monocle] = f(arr[monocle]);
46639
- return newArr;
46640
- },
46641
- del: function (arr, monocle) {
46642
- var newArr = arr.slice();
46643
- newArr.splice(monocle, 1);
46644
- return newArr;
46645
- }
46646
- };
46647
-
46648
- /*
46649
- // Lens must point to a member of an array. We'll insert into that array.
46650
- lens.prototype.insertAt = function(lensArr, toInsert) {
46651
- var obj = this._wrapped;
46652
-
46653
- var arrLens = lensArr.slice(0, -1);
46654
- var arr = lens(obj).get(arrLens).slice(); // slice to copy
46655
-
46656
- var arrIdx = lensArr[lensArr.length-1];
46657
- arr.splice(arrIdx, 0, toInsert);
46658
- return lens(obj).set(arrLens, arr);
46659
- };
46660
-
46661
- lens.prototype.insertBefore = lens.prototype.insertAt;
46662
- lens.prototype.insertAfter = function(lensArr, toInsert) {
46663
- var newLens = lensArr.slice();
46664
- newLens[newLens.length-1] += 1;
46665
- return lens(this._wrapped).insertAt(newLens, toInsert);
46666
- };
46667
- */
46668
-
46669
- // OBJECT FUNCTIONS
46670
-
46671
- const obj = {
46672
- get: function (obj, monocle) {
46673
- return obj[monocle];
46674
- },
46675
- set: function (obj, monocle, val) {
46676
- var newObj = clone(obj);
46677
- newObj[monocle] = val;
46678
- return newObj;
46679
- },
46680
- mod: function (obj, monocle, f) {
46681
- var newObj = clone(obj);
46682
- newObj[monocle] = f(obj[monocle]);
46683
- return newObj;
46684
- },
46685
- del: function (obj, monocle) {
46686
- var newObj = clone(obj);
46687
- delete newObj[monocle];
46688
- return newObj;
46689
- }
46690
- };
46691
-
46692
- // STRING FUNCTIONS
46693
-
46694
- const str = {
46695
- get: function (arr, monocle) {
46696
- return arr[monocle];
46697
- },
46698
- set: function (arr, monocle, val) {
46699
- var newArr = arr.splice();
46700
- newArr[monocle] = val;
46701
- return newArr;
46702
- },
46703
- mod: function (arr, monocle, f) {
46704
- var newArr = arr.splice();
46705
- newArr[monocle] = f(arr[monocle]);
46706
- return newArr;
46707
- },
46708
- del: function (arr, monocle) {
46709
- var newArr = arr.slice();
46710
- newArr.splice(monocle);
46711
- return newArr;
46712
- }
46713
- };
46714
-
46715
- const _excluded$1 = ["item", "children", "shape", "serializedState", "onSerializedStateUpdated"];
46716
-
46717
- // TODO(mdr)
46718
- // TODO(mdr)
46719
- // TODO(mdr)
46720
-
46721
- // @ts-expect-error - TS2315 - Type 'Tree' is not generic.
46722
-
46723
- /**
46724
- * TODO(somewhatabstract, JIRA-XXXX):
46725
- * Some usage of this type somewhere is causing TypeScript to believe that these
46726
- * elements should be components. Don't know where that is, to fix it up
46727
- * properly. In reality, these types are just too hard to use and aren't
46728
- * really helping us out as I now have to suppress this rather than
46729
- * get value from the type. :(
46730
- */ // @ts-expect-error - TS2315 - Type 'Tree' is not generic.
46731
- // @ts-expect-error - TS2315 - Type 'Tree' is not generic.
46732
- // @ts-expect-error - TS2315 - Type 'Tree' is not generic.
46733
- class MultiRenderer extends React.Component {
46734
- constructor(props) {
46735
- super(props);
46736
- this.rendererDataTreeMapper = void 0;
46737
- this.getRenderersMapper = void 0;
46738
- this._handleSerializedStateUpdated = (path, newState) => {
46739
- const {
46740
- onSerializedStateUpdated
46741
- } = this.props;
46742
- if (onSerializedStateUpdated) {
46743
- const oldState = this._getSerializedState(this.props.serializedState);
46744
- onSerializedStateUpdated(lens(oldState).set(path, newState).freeze());
46745
- }
46746
- };
46747
- this.rendererDataTreeMapper = buildMapper()
46748
- // @ts-expect-error - TS2345 - Argument of type 'unknown' is not assignable to parameter of type 'ContentNode'.
46749
- .setContentMapper((c, _, p) => this._makeContentRendererData(c, p))
46750
- // @ts-expect-error - TS2345 - Argument of type 'unknown' is not assignable to parameter of type 'HintNode'.
46751
- .setHintMapper(h => this._makeHintRendererData(h)).setTagsMapper(t => null);
46752
-
46753
- // @ts-expect-error - TS2322 - Type 'TreeMapperForLeavesAndCollections<unknown, any, unknown, any, unknown, unknown>' is not assignable to type 'TreeMapper<ContentRendererData, ContentRendererElement, HintRendererData, HintRendererElement, null, null>'.
46754
- this.getRenderersMapper = buildMapper()
46755
- // @ts-expect-error - TS2571 - Object is of type 'unknown'.
46756
- .setContentMapper(c => c.makeRenderer())
46757
- // @ts-expect-error - TS2571 - Object is of type 'unknown'.
46758
- .setHintMapper(h => h.makeRenderer()).setArrayMapper((renderers, data, shape) => this._annotateRendererArray(renderers, data, shape));
46759
-
46760
- // Keep state in sync with props.
46761
- this.state = this._tryMakeRendererState(this.props);
46762
- }
46763
- UNSAFE_componentWillReceiveProps(nextProps) {
46764
- // Keep state in sync with props.
46765
- if (nextProps.item !== this.props.item) {
46766
- this.setState(this._tryMakeRendererState(nextProps));
46767
- }
46768
- }
46769
-
46770
- /**
46771
- * Attempt to build a State that includes a renderer tree corresponding to
46772
- * the item provided in props. On error, return a state with `renderError`
46773
- * set instead.
46774
- */
46775
- _tryMakeRendererState(props) {
46776
- try {
46777
- return {
46778
- rendererDataTree: this._makeRendererDataTree(props.item, props.shape),
46779
- renderError: null
46780
- };
46781
- } catch (e) {
46782
- Log.error("Error building tree state", Errors.Internal, {
46783
- cause: e
46784
- });
46785
- return {
46786
- rendererDataTree: null,
46787
- renderError: e
46788
- };
46789
- }
46790
- }
46791
- /**
46792
- * Props that aren't directly used by the MultiRenderer are delegated to
46793
- * the underlying Renderers.
46794
- */
46795
- _getRendererProps() {
46796
- // `item`, `children`, and others are unused. I'm
46797
- // explicitly pulling them out of `this.props` so I don't pass them to
46798
- // `<Renderer>`. I'm not sure how else to do this.
46799
- const _this$props = this.props,
46800
- otherProps = _objectWithoutPropertiesLoose(_this$props, _excluded$1);
46801
- return _extends({}, otherProps, {
46802
- strings: this.context.strings
46803
- });
46804
- }
46805
-
46806
- /**
46807
- * Construct a Renderer and a ref placeholder for the given ContentNode.
46808
- */
46809
- _makeContentRendererData(content, path) {
46810
- // NOTE(emily): The `findExternalWidgets` function here is computed
46811
- // inline and thus changes each time we run this function. If it
46812
- // were to change every render, it would cause the Renderer to
46813
- // re-render a lot more than is necessary. Don't re-compute this
46814
- // element unless it is necessary!
46815
- // HACK(mdr): TypeScript can't prove that this is a ContentRendererData,
46816
- // because of how we awkwardly construct it in order to obtain a
46817
- // circular reference. But it is, I promise.
46818
- const data = {
46819
- ref: null,
46820
- makeRenderer: null
46821
- };
46822
- const refFunc = e => data.ref = e;
46823
- const findExternalWidgets = criterion => this._findWidgets(data, criterion);
46824
- const handleSerializedState = state => this._handleSerializedStateUpdated(path, state);
46825
- data.makeRenderer = () =>
46826
- /*#__PURE__*/
46827
- /**
46828
- * TODO(somewhatabstract, JIRA-XXXX):
46829
- * `content` contains props that Renderer doesn't have. However,
46830
- * since the type for `content` is not exact, it's hard to know
46831
- * if this spread is including undocumented props so mapping
46832
- * one to one could introduce a bug. Need to work out the exact
46833
- * type for ContentNode and then fix this.
46834
- */
46835
- // @ts-expect-error - TS2322 - Type '{ ref: (e: any) => any; findExternalWidgets: (criterion: any) => readonly (Widget | null | undefined)[]; serializedState: any; onSerializedStateUpdated: (state: any) => void; __type: "content" | "item"; ... 9 more ...; reviewMode?: boolean | ... 1 more ... | undefined; }' is not assignable to type 'InexactPartial<Pick<Readonly<Props> & Readonly<{ children?: ReactNode; }>, "content" | "images" | "onRender" | "linterContext" | "widgets" | "alwaysUpdate" | ... 6 more ... | "serializedState">>'.
46836
- React.createElement(Renderer, _extends({}, this._getRendererProps(), content, {
46837
- ref: refFunc,
46838
- findExternalWidgets: findExternalWidgets,
46839
- serializedState: this.props.serializedState ? lens(this.props.serializedState).get(path) : null,
46840
- onSerializedStateUpdated: handleSerializedState
46841
- }));
46842
- return data;
46843
- }
46844
-
46845
- /**
46846
- * Construct a Renderer for the given HintNode, and keep track of the hint
46847
- * itself for future use, too.
46848
- */
46849
- _makeHintRendererData(hint) {
46850
- // TODO(mdr): Once HintsRenderer supports inter-widget communication,
46851
- // give it a ref. Until then, leave the ref null forever, to avoid
46852
- // confusing the findWidgets functions.
46853
- //
46854
- // NOTE(davidflanagan): As a partial step toward inter-widget
46855
- // communication we're going to pass a findExternalWidgets function
46856
- // (using a dummy data object). This allows passage-ref widgets in
46857
- // hints to use findWidget() to find the passage widgets they reference.
46858
- // Note that this is one-way only, however. It does not allow
46859
- // widgets in the question to find widgets in the hints, for example.
46860
- const findExternalWidgets = criterion => this._findWidgets({}, criterion);
46861
- return {
46862
- hint,
46863
- findExternalWidgets,
46864
- // _annotateRendererArray() needs this
46865
- ref: null,
46866
- makeRenderer: () => /*#__PURE__*/React.createElement(HintsRenderer, _extends({}, this._getRendererProps(), {
46867
- findExternalWidgets: findExternalWidgets
46868
- // Note(Jeremy): The MultiRenderer codebase types are
46869
- // slightly different from the rest of the codebase.
46870
- // Ideally `HintNode` would spread the `Hint` type into it,
46871
- // but there is a difference in optionality of `widgets`
46872
- // and `images` and that causes a huge cascade of type
46873
- // errors (they are truly optional, but most of our code
46874
- // assumes they're provided/set). Ignoring this for now.
46875
- // @ts-expect-error - TS2769 - Type 'HintNode' is not assignable to type 'Hint'.
46876
- ,
46877
- hints: [hint]
46878
- }))
46879
- };
46880
- }
46881
-
46882
- /**
46883
- * Construct a tree of interconnected RendererDatas, corresponding to the
46884
- * given item. Called in `_tryMakeRendererState`, in order to store this
46885
- * tree in the component state.
46886
- */
46887
- _makeRendererDataTree(item, shape) {
46888
- const itemTree = itemToTree(item);
46889
- return this.rendererDataTreeMapper.mapTree(itemTree, shape);
46890
- }
46891
-
46892
- /**
46893
- * Return all widgets that meet the given criterion, from all Renderers
46894
- * except the Renderer that triggered this call.
46895
- *
46896
- * This function is provided to each Renderer's `findExternalWidgets` prop,
46897
- * which enables widgets in different Renderers to discover each other and
46898
- * communicate.
46899
- */
46900
- _findWidgets(callingData, filterCriterion) {
46901
- const results = [];
46902
- this._mapRenderers(data => {
46903
- if (callingData !== data && data.ref) {
46904
- results.push(...data.ref.findInternalWidgets(filterCriterion));
46905
- }
46906
- });
46907
- return results;
46908
- }
46909
-
46910
- /**
46911
- * Copy the renderer tree, apply the given transformation to the leaf nodes
46912
- * and the optional given transformation to the array nodes, and return the
46913
- * result.
46914
- *
46915
- * Used to provide structured data to the call site (the Renderer tree on
46916
- * `render`, the Score tree on `getScores`, etc.), and to traverse the
46917
- * renderer tree even when we disregard the output (like in
46918
- * `_findWidgets`).
46919
- */
46920
- _mapRenderers(
46921
- // eslint-disable-next-line no-restricted-syntax
46922
- leafMapper
46923
- // @ts-expect-error - TS2315 - Type 'Tree' is not generic.
46924
- ) {
46925
- const {
46926
- rendererDataTree
46927
- } = this.state;
46928
- if (!rendererDataTree) {
46929
- return null;
46930
- }
46931
- const mapper = buildMapper().setContentMapper(leafMapper).setHintMapper(leafMapper);
46932
- return mapper.mapTree(rendererDataTree, this.props.shape);
46933
- }
46934
- _scoreFromRef(ref) {
46935
- if (!ref) {
46936
- return null;
46937
- }
46938
- const [guess, score] = ref.guessAndScore();
46939
- let state;
46940
- if (ref.getSerializedState) {
46941
- state = ref.getSerializedState();
46942
- }
46943
- return keScoreFromPerseusScore(score, guess, state);
46944
- }
46945
-
46946
- /**
46947
- * Return a tree in the shape of the multi-item, with scores at each of
46948
- * the content nodes and `null` at the other leaf nodes.
46949
- */
46950
- getScores() {
46951
- return this._mapRenderers(data => this._scoreFromRef(data.ref));
46952
- }
46953
-
46954
- /**
46955
- * Return a single composite score for all rendered content nodes.
46956
- * The `guess` is a tree in the shape of the multi-item, with an individual
46957
- * guess at each content node and `null` at the other leaf nodes.
46958
- */
46959
- score() {
46960
- const scores = [];
46961
- const state = [];
46962
- const guess = this._mapRenderers(data => {
46963
- var _data$ref;
46964
- if (!data.ref) {
46965
- return null;
46966
- }
46967
- if (data.ref.getSerializedState) {
46968
- state.push(data.ref.getSerializedState());
46969
- }
46970
- scores.push(data.ref.score());
46971
- return (_data$ref = data.ref) == null ? void 0 : _data$ref.getUserInput();
46972
- });
46973
- const combinedScore = scores.reduce(combineScores);
46974
- return keScoreFromPerseusScore(combinedScore, guess, state);
46975
- }
46976
-
46977
- /**
46978
- * Return a tree in the shape of the multi-item, with serialized state at
46979
- * each of the content nodes and `null` at the other leaf nodes.
46980
- *
46981
- * If the lastSerializedState argument is supplied, this function will fill
46982
- * in the state of not-currently-rendered content and hint nodes with the
46983
- * values from the previous serialized state. If no lastSerializedState is
46984
- * supplied, `null` will be returned for not-currently-rendered content and
46985
- * hint nodes.
46986
- */
46987
- _getSerializedState(lastSerializedState) {
46988
- return this._mapRenderers((data, _, path) => {
46989
- if (data.ref) {
46990
- return data.ref.getSerializedState();
46991
- }
46992
- if (lastSerializedState) {
46993
- return lens(lastSerializedState).get(path);
46994
- }
46995
- return null;
46996
- });
46997
- }
46998
-
46999
- /**
47000
- * Given a tree in the shape of the multi-item, with serialized state at
47001
- * each of the content nodes, restore each state to the corresponding
47002
- * renderer if currently mounted.
47003
- */
47004
- restoreSerializedState(serializedState, callback) {
47005
- // We want to call our async callback only once all of the childrens'
47006
- // callbacks have run. We add one to this counter before we call out to
47007
- // each renderer and decrement it when it runs our callback.
47008
- let numCallbacks = 0;
47009
- const countCallback = () => {
47010
- numCallbacks--;
47011
- if (callback && numCallbacks === 0) {
47012
- callback();
47013
- }
47014
- };
47015
- this._mapRenderers((data, _, path) => {
47016
- var _data$ref2;
47017
- if (!data.ref) {
47018
- return;
47019
- }
47020
- const state = lens(serializedState).get(path);
47021
- if (!state) {
47022
- return;
47023
- }
47024
- numCallbacks++;
47025
- (_data$ref2 = data.ref) == null || _data$ref2.restoreSerializedState(state, countCallback);
47026
- });
47027
- }
47028
-
47029
- /**
47030
- * Given an array of renderers, if it happens to be an array of *hint*
47031
- * renderers, then attach a `firstN` method to the array, which allows the
47032
- * layout to render the hints together in one HintsRenderer.
47033
- */
47034
- _annotateRendererArray(renderers, rendererDatas, shape) {
47035
- if (shape.elementShape.type === "hint") {
47036
- // The shape says that these are HintRendererDatas, even though
47037
- // it's not provable at compile time, so perform a cast.
47038
- const hintRendererDatas = rendererDatas;
47039
- renderers = [...renderers];
47040
- renderers.firstN = n => /*#__PURE__*/React.createElement(HintsRenderer, _extends({}, this._getRendererProps(), {
47041
- findExternalWidgets: hintRendererDatas[0] ? hintRendererDatas[0].findExternalWidgets : undefined,
47042
- hints: hintRendererDatas.map(d => d.hint),
47043
- hintsVisible: n
47044
- }));
47045
- }
47046
- return renderers;
47047
- }
47048
-
47049
- /**
47050
- * Return a tree in the shape of the multi-item, with a Renderer at each
47051
- * content node and a HintRenderer at each hint node.
47052
- *
47053
- * This is generated by running each of the `makeRenderer` functions at the
47054
- * leaf nodes.
47055
- */
47056
- _getRenderers() {
47057
- return this.getRenderersMapper.mapTree(this.state.rendererDataTree, this.props.shape);
47058
- }
47059
- render() {
47060
- if (this.state.renderError) {
47061
- return /*#__PURE__*/React.createElement("div", {
47062
- className: css(styles.error)
47063
- }, this.context.strings.errorRendering({
47064
- error: String(this.state.renderError)
47065
- }));
47066
- }
47067
-
47068
- // Pass the renderer tree to the `children` function, which will
47069
- // determine the actual content of this component.
47070
- return /*#__PURE__*/React.createElement(DependenciesContext.Provider, {
47071
- value: this.props.dependencies
47072
- }, this.props.children({
47073
- renderers: this._getRenderers()
47074
- }));
47075
- }
47076
- }
47077
- MultiRenderer.contextType = PerseusI18nContext;
47078
- const styles = StyleSheet.create({
47079
- error: {
47080
- color: "red"
47081
- }
47082
- });
47083
-
47084
- /**
47085
- * Utility functions to build React PropTypes for multi-items and shapes.
47086
- *
47087
- * If you're writing new components, though, consider using the Item and Shape
47088
- * types instead.
47089
- */
47090
- /**
47091
- * Return a PropType that accepts Items of the given shape, and rejects other
47092
- * objects.
47093
- *
47094
- * Usage: `propTypes: {item: buildPropTypeForShape(myShape)}`
47095
- */
47096
- function buildPropTypeForShape(shape) {
47097
- return PropTypes.oneOfType([PropTypes.shape({
47098
- _multi: buildTreePropTypeForShape(shape)
47099
- }), PropTypes.oneOf([null, undefined])]);
47100
- }
47101
-
47102
- /**
47103
- * Return a PropType that accepts ItemTrees of the given shape, and rejects
47104
- * other objects.
47105
- */
47106
- function buildTreePropTypeForShape(shape) {
47107
- if (shape.type === "content") {
47108
- return PropTypes.shape({
47109
- // TODO(mdr): Remove #LegacyContentNode support.
47110
- __type: PropTypes.oneOf(["content", "item"]).isRequired,
47111
- content: PropTypes.string,
47112
- images: PropTypes.objectOf(PropTypes.any),
47113
- widgets: PropTypes.objectOf(PropTypes.any)
47114
- });
47115
- }
47116
- if (shape.type === "hint") {
47117
- return PropTypes.shape({
47118
- __type: PropTypes.oneOf(["hint"]).isRequired,
47119
- content: PropTypes.string,
47120
- images: PropTypes.objectOf(PropTypes.any),
47121
- widgets: PropTypes.objectOf(PropTypes.any),
47122
- replace: PropTypes.bool
47123
- });
47124
- }
47125
- if (shape.type === "tags") {
47126
- return PropTypes.arrayOf(PropTypes.string.isRequired);
47127
- }
47128
- if (shape.type === "array") {
47129
- const elementPropType = buildTreePropTypeForShape(shape.elementShape);
47130
- return PropTypes.arrayOf(elementPropType.isRequired);
47131
- }
47132
- if (shape.type === "object") {
47133
- const valueShapes = shape.shape;
47134
- const propTypeShape = {};
47135
- Object.keys(valueShapes).forEach(key => {
47136
- propTypeShape[key] = buildTreePropTypeForShape(valueShapes[key]).isRequired;
47137
- });
47138
- return PropTypes.shape(propTypeShape);
47139
- }
47140
- throw new PerseusError(
47141
- // @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
47142
- `unexpected shape type ${shape.type}`, Errors.InvalidInput);
47143
- }
47144
-
47145
- /**
47146
- * This library provides support for Perseus multi-items: structured Perseus
47147
- * content that content creators can easily create, and that applications can
47148
- * easily render into different parts of the layout.
47149
- *
47150
- * For more details about application and motivation, see:
47151
- * https://sites.google.com/a/khanacademy.org/forge/for-developers/perseus-items-and-multi-items
47152
- *
47153
- * This file primarily exposes the `MultiRenderer` component, which performs
47154
- * multi-rendering. To multi-render a question, pass in the content of the item
47155
- * to the `MultiRenderer` component as a props. Then, pass in a function which
47156
- * takes an object of renderers (in the same structure as the content), and
47157
- * return a render tree. The `MultiRenderer` component will allow you to
47158
- * combine scores, serialized state, etc. without having to manually call on
47159
- * each of the functions. It also handles inter-widgets requests between the
47160
- * different renderers.
47161
- * For more details, see `multi-items/multi-renderer.jsx`.
47162
- *
47163
- * Example:
47164
- *
47165
- * item = {_multi: {
47166
- * left: <content data>,
47167
- * right: [<content data>, <content data>],
47168
- * }}
47169
- * shape = shapes.shape({
47170
- * left: shapes.content,
47171
- * right: shapes.arrayOf(shapes.content),
47172
- * })
47173
- *
47174
- * <MultiRenderer item={item} shape={shape}>
47175
- * {({renderers}) =>
47176
- * <div>
47177
- * <div id="left">{renderers.left}</div>
47178
- * <ul id="right">
47179
- * {renderers.right.map(r => <li>{r}</li>)}
47180
- * </ul>
47181
- * </div>
47182
- * }
47183
- * </MultiRenderer>
47184
- *
47185
- * This file also exposes `shapes`, which helps you construct a runtime type
47186
- * declaration for your particular class of multi-item. This can then be used
47187
- * to create a MultirendererEditor for your multi-item shape, and to validate
47188
- * that a multi-item conforms to the shape via `buildPropTypeForShape`.
47189
- * For more details, see `multi-items/shapes.js`.
47190
- *
47191
- * This file also exposes some utility functions for working with generic
47192
- * multi-items, like `findContentNodesInItem`, `findHintNodesInItem`,
47193
- * `inferItemShape`, and `buildEmptyItemForShape`.
47194
- * For more details, see `multi-items/items.js`.
47195
- */
47196
- var MultiItems = {
47197
- // Tools for rendering your multi-items
47198
- MultiRenderer,
47199
- // Tools for declaring your multi-item shapes
47200
- shapes,
47201
- buildPropTypeForShape,
47202
- // Tools for generically manipulating multi-items
47203
- buildEmptyItemForShape,
47204
- findContentNodesInItem: findContentNodesInItem$2,
47205
- findHintNodesInItem,
47206
- inferItemShape: inferItemShape$2
47207
- };
47208
-
47209
46061
  /**
47210
46062
  * Calculates whether a perseus item is renderable by a specific
47211
46063
  * perseus-item-data version.
@@ -47215,10 +46067,6 @@ var MultiItems = {
47215
46067
  * This supports widgets that contain renderers, such as the
47216
46068
  * group or sequence widgets.
47217
46069
  */
47218
- const {
47219
- findContentNodesInItem: findContentNodesInItem$1,
47220
- inferItemShape: inferItemShape$1
47221
- } = MultiItems;
47222
46070
  const isUpgradedWidgetInfoRenderableBy = function isUpgradedWidgetInfoRenderableBy(widgetInfo, widgetRendererVersion) {
47223
46071
  if (widgetRendererVersion == null) {
47224
46072
  // If the widget does not exist in this version, this will
@@ -47264,17 +46112,6 @@ const isItemRenderableByVersion = function isItemRenderableByVersion(itemData, r
47264
46112
  if (itemData == null || rendererContentVersion == null) {
47265
46113
  throw new PerseusError("missing parameter to Perseus.isRenderable.item", Errors.InvalidInput);
47266
46114
  }
47267
- if (itemData._multi) {
47268
- const shape = inferItemShape$1(itemData);
47269
- let isRenderable = true;
47270
- findContentNodesInItem$1(itemData, shape, node => {
47271
- const nodeIsRenderable = isRendererContentRenderableBy(node, rendererContentVersion);
47272
- if (!nodeIsRenderable) {
47273
- isRenderable = false;
47274
- }
47275
- });
47276
- return isRenderable;
47277
- }
47278
46115
  return isRendererContentRenderableBy(itemData.question, rendererContentVersion);
47279
46116
  };
47280
46117
 
@@ -47282,10 +46119,6 @@ const isItemRenderableByVersion = function isItemRenderableByVersion(itemData, r
47282
46119
  * Identifies whether or not a given perseus item requires the use of a mouse
47283
46120
  * or screen, based on the widgets it contains.
47284
46121
  */
47285
- const {
47286
- findContentNodesInItem,
47287
- inferItemShape
47288
- } = MultiItems;
47289
46122
 
47290
46123
  // Iterate over a single Perseus renderer, mutating `widgets` by appending
47291
46124
  // violating widget types discovered in this item.
@@ -47306,12 +46139,7 @@ function traverseRenderer(itemData, widgets) {
47306
46139
  function violatingWidgets(itemData) {
47307
46140
  // TODO(jordan): Hints as well
47308
46141
  const widgets = [];
47309
- if (itemData._multi) {
47310
- const shape = inferItemShape(itemData);
47311
- findContentNodesInItem(itemData, shape, content => traverseRenderer(content, widgets));
47312
- } else {
47313
- traverseRenderer(itemData.question, widgets);
47314
- }
46142
+ traverseRenderer(itemData.question, widgets);
47315
46143
 
47316
46144
  // Uniquify the list of widgets (by type)
47317
46145
  return _.uniq(widgets);
@@ -49818,5 +48646,9 @@ const ScoringUtil = {
49818
48646
  keScoreFromPerseusScore
49819
48647
  };
49820
48648
 
49821
- export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ScoringUtil, ref as ServerItemRenderer, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, addWidget, apiVersion, bodyXsmallBold, buildEmptyItemTreeForShape, buildMapper, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getClockwiseAngle, getCorrectAnswerForWidgetId, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, iconChevronDown, iconTrash, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, makeSafeUrl, mathOnlyParser, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, scorePerseusItem, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
48649
+ /**
48650
+ * Types
48651
+ */
48652
+
48653
+ export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ScoringUtil, ref as ServerItemRenderer, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, addWidget, apiVersion, bodyXsmallBold, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getClockwiseAngle, getCorrectAnswerForWidgetId, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, iconChevronDown, iconTrash, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, makeSafeUrl, mathOnlyParser, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, scorePerseusItem, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
49822
48654
  //# sourceMappingURL=index.js.map